|
19 | 19 | use League\Uri\Idna\Converter as IdnaConverter; |
20 | 20 | use League\Uri\IPv6\Converter as IPv6Converter; |
21 | 21 | use Stringable; |
22 | | - |
23 | 22 | use Throwable; |
| 23 | + |
24 | 24 | use function array_merge; |
25 | 25 | use function array_pop; |
26 | 26 | use function array_reduce; |
| 27 | +use function defined; |
27 | 28 | use function explode; |
28 | 29 | use function filter_var; |
| 30 | +use function function_exists; |
29 | 31 | use function implode; |
30 | 32 | use function in_array; |
31 | 33 | use function inet_pton; |
32 | 34 | use function preg_match; |
| 35 | +use function preg_replace_callback; |
33 | 36 | use function rawurldecode; |
34 | 37 | use function sprintf; |
35 | 38 | use function strpos; |
36 | 39 | use function strtolower; |
| 40 | +use function strtoupper; |
37 | 41 | use function substr; |
38 | 42 |
|
39 | 43 | use const FILTER_FLAG_IPV4; |
@@ -286,11 +290,19 @@ public static function parseNormalized(Stringable|string $uri): array |
286 | 290 | $components['scheme'] = strtolower($components['scheme']); |
287 | 291 | } |
288 | 292 |
|
| 293 | + static $isSupported = null; |
| 294 | + $isSupported ??= (function_exists('\idn_to_ascii') && defined('\INTL_IDNA_VARIANT_UTS46')); |
| 295 | + |
289 | 296 | if (null !== $components['host'] && |
290 | 297 | false === filter_var($components['host'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && |
291 | 298 | !IPv6Converter::isIpv6($components['host']) |
292 | 299 | ) { |
293 | | - $components['host'] = IdnaConverter::toAscii($components['host'])->domain(); |
| 300 | + $formattedHost = rawurldecode($components['host']); |
| 301 | + $components['host'] = $isSupported ? IdnaConverter::toAscii($formattedHost)->domain() : (string) preg_replace_callback( |
| 302 | + '/%[0-9A-F]{2}/i', |
| 303 | + fn (array $matches): string => strtoupper($matches[0]), |
| 304 | + strtolower($components['host']) |
| 305 | + ); |
294 | 306 | } |
295 | 307 |
|
296 | 308 | $path = $components['path']; |
@@ -691,6 +703,14 @@ public static function isHost(Stringable|string|null $host): bool |
691 | 703 | private static function filterRegisteredName(string $host): void |
692 | 704 | { |
693 | 705 | $formattedHost = rawurldecode($host); |
| 706 | + if ($formattedHost !== $host) { |
| 707 | + if (IdnaConverter::toAscii($formattedHost)->hasErrors()) { |
| 708 | + throw new SyntaxError(sprintf('Host `%s` is invalid: the host is not a valid registered name', $host)); |
| 709 | + } |
| 710 | + |
| 711 | + return; |
| 712 | + } |
| 713 | + |
694 | 714 | if (1 === preg_match(self::REGEXP_REGISTERED_NAME, $formattedHost)) { |
695 | 715 | return; |
696 | 716 | } |
|
0 commit comments