Skip to content

Commit ccbecf3

Browse files
committed
:octocat: Uri: unicode domain name awareness
1 parent 404000a commit ccbecf3

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

src/Psr7/Uri.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use InvalidArgumentException;
1414
use Psr\Http\Message\UriInterface;
1515

16-
use function call_user_func_array, explode, filter_var, is_array, is_string, ltrim, parse_url,
16+
use function call_user_func_array, explode, filter_var, is_array, is_string, ltrim, mb_strtolower,
1717
preg_replace_callback, rawurlencode, strpos, strtolower, ucfirst, var_export;
1818

1919
use const FILTER_FLAG_IPV6, FILTER_VALIDATE_IP;
@@ -48,7 +48,7 @@ public function __construct($uri = null){
4848
if($uri !== null){
4949

5050
if(is_string($uri)){
51-
$uri = parse_url($uri);
51+
$uri = parseUrl($uri);
5252
}
5353

5454
if(!is_array($uri)){
@@ -238,7 +238,7 @@ protected function filterHost($host):string{
238238
$host = '['.$host.']';
239239
}
240240

241-
return strtolower($host);
241+
return mb_strtolower($host);
242242
}
243243

244244
/**

src/Psr7/message_helpers.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
use function array_filter, array_keys, array_map, array_values, count, explode,
1515
gzdecode, gzinflate, gzuncompress, implode, is_array, is_numeric, is_scalar, is_string,
16-
json_decode, json_encode, rawurldecode, rawurlencode, simplexml_load_string, strtolower, trim,
17-
ucfirst;
16+
json_decode, json_encode, parse_url, preg_match, preg_replace_callback, rawurldecode, rawurlencode,
17+
simplexml_load_string, strtolower, trim, ucfirst, urlencode;
1818

1919
const PSR7_INCLUDES = true;
2020

@@ -511,3 +511,37 @@ function uriWithQueryValue(UriInterface $uri, string $key, string $value = null)
511511
return $uri->withQuery(implode('&', $result));
512512
}
513513

514+
/**
515+
* UTF-8 aware \parse_url() replacement.
516+
*
517+
* The internal function produces broken output for non ASCII domain names
518+
* (IDN) when used with locales other than "C".
519+
*
520+
* On the other hand, cURL understands IDN correctly only when UTF-8 locale
521+
* is configured ("C.UTF-8", "en_US.UTF-8", etc.).
522+
*
523+
* @see https://bugs.php.net/bug.php?id=52923
524+
* @see https://www.php.net/manual/en/function.parse-url.php#114817
525+
* @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
526+
*
527+
* @link https://github.com/guzzle/psr7/blob/c0dcda9f54d145bd4d062a6d15f54931a67732f9/src/Uri.php#L89-L130
528+
*/
529+
function parseUrl(string $url):?array{
530+
// If IPv6
531+
$prefix = '';
532+
/** @noinspection RegExpRedundantEscape */
533+
if(preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)){
534+
/** @var array{0:string, 1:string, 2:string} $matches */
535+
$prefix = $matches[1];
536+
$url = $matches[2];
537+
}
538+
539+
$encodedUrl = preg_replace_callback('%[^:/@?&=#]+%usD', fn($matches) => urlencode($matches[0]), $url);
540+
$result = parse_url($prefix.$encodedUrl);
541+
542+
if($result === false){
543+
return null;
544+
}
545+
546+
return array_map('urldecode', $result);
547+
}

tests/Psr7/UriTest.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* Class UriTest
44
*
5-
* @link https://github.com/guzzle/psr7/blob/4b981cdeb8c13d22a6c193554f8c686f53d5c958/tests/UriTest.php
5+
* @link https://github.com/guzzle/psr7/blob/c0dcda9f54d145bd4d062a6d15f54931a67732f9/tests/UriTest.php
66
* @link https://github.com/bakame-php/psr7-uri-interface-tests/blob/5a556fdfe668a6c6a14772efeba6134c0b7dae34/tests/AbstractUriTestCase.php
77
*
88
* @created 10.08.2018
@@ -662,4 +662,22 @@ public function testWithPartSamePart():void{
662662
$this::assertSame($expected, (string)$uri);
663663
}
664664

665+
public function testInternationalizedDomainName():void{
666+
$uri = new Uri('https://яндекс.рф');
667+
self::assertSame('яндекс.рф', $uri->getHost());
668+
669+
$uri = new Uri('https://яндекAс.рф');
670+
self::assertSame('яндекaс.рф', $uri->getHost());
671+
}
672+
673+
public function testIPv6Host():void{
674+
$uri = new Uri('https://[2a00:f48:1008::212:183:10]');
675+
self::assertSame('[2a00:f48:1008::212:183:10]', $uri->getHost());
676+
677+
$uri = new Uri('http://[2a00:f48:1008::212:183:10]:56?foo=bar');
678+
self::assertSame('[2a00:f48:1008::212:183:10]', $uri->getHost());
679+
self::assertSame(56, $uri->getPort());
680+
self::assertSame('foo=bar', $uri->getQuery());
681+
}
682+
665683
}

0 commit comments

Comments
 (0)