diff --git a/BaseUri.php b/BaseUri.php index 31ddbcdf..965803e0 100644 --- a/BaseUri.php +++ b/BaseUri.php @@ -17,6 +17,7 @@ use League\Uri\Contracts\UriAccess; use League\Uri\Contracts\UriInterface; use League\Uri\Exceptions\MissingFeature; +use League\Uri\Idna\Converter; use League\Uri\IPv4\Converter as IPv4Converter; use Psr\Http\Message\UriFactoryInterface; use Psr\Http\Message\UriInterface as Psr7UriInterface; @@ -27,7 +28,6 @@ use function end; use function explode; use function implode; -use function in_array; use function str_repeat; use function strpos; use function substr; @@ -161,6 +161,14 @@ public function isSameDocument(Stringable|string $uri): bool return $this->normalize(static::filterUri($uri)) === $this->normalize($this->uri); } + /** + * Tells whether the URI contains an Internationalized Domain Name (IDN). + */ + public function hasIdn(): bool + { + return Converter::isIdn($this->uri->getHost()); + } + /** * Resolves a URI against a base URI using RFC3986 rules. * @@ -512,7 +520,11 @@ final protected static function formatPath(string $path, string $basePath): stri $slashPosition = strpos($path, '/'); return match (true) { - '' === $path => in_array($basePath, ['', '/'], true) ? $basePath : './', + '' === $path => match (true) { + '' === $basePath, + '/' === $basePath => $basePath, + default => './', + }, false === $colonPosition => $path, false === $slashPosition, $colonPosition < $slashPosition => "./$path", diff --git a/BaseUriTest.php b/BaseUriTest.php index 5f3357e1..1704309d 100644 --- a/BaseUriTest.php +++ b/BaseUriTest.php @@ -451,4 +451,41 @@ public function testGetOriginWithPsr7Implementation(Psr7UriInterface|Uri|string self::assertSame($expectedOrigin, $origin); } + + /** + * @dataProvider provideIDNUri + */ + public function testHostIsIDN(string $uri, bool $expected): void + { + self::assertSame($expected, BaseUri::from($uri)->hasIdn()); + self::assertSame($expected, BaseUri::from(Utils::uriFor($uri), new \GuzzleHttp\Psr7\HttpFactory())->hasIdn()); + } + + public static function provideIDNUri(): iterable + { + yield 'ascii uri (1)' => [ + 'uri' => 'https://www.example.com', + 'expected' => false, + ]; + + yield 'ascii uri with invalid converted i18n' => [ + 'uri' => 'https://www.xn--ample.com', + 'expected' => false, + ]; + + yield 'idn uri' => [ + 'uri' => 'https://www.bébé.be', + 'expected' => true, + ]; + + yield 'uri without host' => [ + 'uri' => '/path/to/the?sky=1', + 'expected' => false, + ]; + + yield 'uri without empty host' => [ + 'uri' => 'file:///path/to/the/sky', + 'expected' => false, + ]; + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index ac7cc384..558a250b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,25 @@ All Notable changes to `League\Uri` will be documented in this file -## 7.1.0 - 2023-08-21 +## [7.1.0](https://github.com/thephpleague/uri/compare/7.1.0...master) - TBD + +### Added + +- `BasUri::hasIDN` + +### Fixed + +- None + +### Deprecated + +- None + +### Removed + +- None + +## [7.1.0](https://github.com/thephpleague/uri/compare/7.0.0...7.1.0) - 2023-08-21 ### Added