$urls Adressen der Website, wie WordPress sie führt * @return list Hostnamen in Kleinschreibung, ohne Dubletten, in stabiler Reihenfolge */ public static function hostnames(array $urls): array { $hosts = []; foreach ($urls as $url) { $host = self::host($url); if ($host !== null && ! isset($hosts[$host])) { $hosts[$host] = true; } } $out = array_keys($hosts); sort($out); return $out; } /** Der überwachbare Hostname einer Adresse, sonst null. */ public static function host(string $url): ?string { $url = trim($url); if ($url === '') { return null; } if (! preg_match('~^[a-z][a-z0-9+.-]*://~i', $url)) { $url = 'https://'.$url; } // wp_parse_url ist der Weg in WordPress (gleiches Verhalten über PHP-Versionen hinweg); ohne WordPress – // also im Test – bleibt parse_url. Die Prüfung des Verzeichnisses kennt den Rückfall nicht, daher der Hinweis. $host = self::unicodeHost($url) ?? (function_exists('wp_parse_url') ? wp_parse_url($url, PHP_URL_HOST) // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url -- Rückfall, damit die Klasse ohne WordPress prüfbar bleibt. : parse_url($url, PHP_URL_HOST)); if (! is_string($host) || $host === '') { return null; } $host = strtolower(rtrim($host, '.')); // IP-Adressen und Adressen ohne Punkt (localhost, Containernamen) kann von außen niemand auflösen if (filter_var($host, FILTER_VALIDATE_IP) !== false || ! str_contains($host, '.')) { return null; } foreach (self::RESERVED_SUFFIXES as $suffix) { if (str_ends_with($host, $suffix)) { return null; } } // internationalisierte Namen (müller.de) führt DomainWarn als Punycode (xn--mller-kva.de) $host = SiteHosting::normalizeHost($host) ?? $host; // ein gültiger Hostname besteht aus Labels mit Buchstaben, Ziffern und Bindestrich (Punycode inbegriffen); die // Endung ist entweder rein alphabetisch (.de) oder eine internationalisierte als Punycode (.рф = .xn--p1ai) return preg_match('/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{2,63}|xn--[a-z0-9-]{1,59})$/', $host) === 1 ? $host : null; } /** * Hostname einer Adresse mit Nicht-ASCII-Zeichen (https://пример.рф/). parse_url ersetzt manche Bytes von UTF-8 * (etwa die von Kyrillisch) durch Unterstriche – der Name wäre danach weder lesbar noch in Punycode umrechenbar. * Für reine ASCII-Adressen null: dann bleibt es bei parse_url. */ private static function unicodeHost(string $url): ?string { if (preg_match('/[^\x00-\x7f]/', $url) !== 1) { return null; } return preg_match('~^[a-z][a-z0-9+.-]*://(?:[^@/?#]*@)?([^/?#:@\[\]]+)(?::\d*)?(?:[/?#]|$)~iu', $url, $m) === 1 ? $m[1] : null; } }