translate(); return; } $domain = self::asciiDomain(mb_substr($email, $at + 1)); if ($domain === null || ! str_contains($domain, '.')) { $fail('Die Domain der E-Mail-Adresse :attribute ist unvollständig — es fehlt die Endung (z. B. .de).')->translate(); return; } $labels = explode('.', $domain); foreach ($labels as $label) { if (preg_match('/^(?!-)[a-z0-9-]{1,63}(?translate(); return; } } $tld = end($labels); if (preg_match('/^(xn--[a-z0-9-]+|[a-z]{2,63})$/', $tld) !== 1) { $fail('Die Domain der E-Mail-Adresse :attribute hat keine gültige Endung.')->translate(); return; } if (in_array($tld, self::TYPO_TLDS, true)) { $fail('Die Endung „.:tld" der E-Mail-Adresse :attribute sieht nach einem Tippfehler aus.')->translate(['tld' => $tld]); } } /** * Internationale Domains in die DNS-taugliche Form bringen (Punycode). * Ohne `intl` bleibt die Eingabe unverändert — dann greifen nur die * ASCII-Regeln, was für den Zweck reicht. */ public static function asciiDomain(string $domain): ?string { $domain = mb_strtolower(trim(rtrim($domain, '.'))); if ($domain === '') { return null; } if (function_exists('idn_to_ascii')) { $converted = idn_to_ascii($domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46); if (is_string($converted) && $converted !== '') { return $converted; } // Nicht konvertierbar (verbotene Zeichen, zu lang) — als // unplausibel behandeln, nicht als gültige ASCII-Domain. return null; } return $domain; } /** * Die Domain einer Adresse in DNS-Form, oder null. */ public static function domainOf(?string $email): ?string { $email = trim((string) $email); $at = strrpos($email, '@'); return $at === false ? null : self::asciiDomain(mb_substr($email, $at + 1)); } }