diff --git a/components/Modifier.php b/components/Modifier.php index 4e85eb12..a2fb8669 100644 --- a/components/Modifier.php +++ b/components/Modifier.php @@ -26,6 +26,7 @@ use League\Uri\Contracts\PathInterface; use League\Uri\Contracts\UriAccess; use League\Uri\Contracts\UriInterface; +use League\Uri\Contracts\UriRenderer; use League\Uri\Exceptions\MissingFeature; use League\Uri\Exceptions\SyntaxError; use League\Uri\Idna\Converter as IdnaConverter; @@ -853,6 +854,15 @@ final protected static function ipv4Converter(): IPv4Converter return $converter; } + public function displayUriString(): string + { + if ($this->uri instanceof UriRenderer) { + return $this->uri->toDisplayString(); + } + + return Uri::new($this->uri)->toDisplayString(); + } + /** * DEPRECATION WARNING! This method will be removed in the next major point release. * diff --git a/composer.json b/composer.json index f7ea897b..34576747 100644 --- a/composer.json +++ b/composer.json @@ -30,18 +30,20 @@ "ext-gmp": "*", "ext-intl": "*", "ext-mbstring": "*", - "friendsofphp/php-cs-fixer": "^3.65.0", + "friendsofphp/php-cs-fixer": "^3.67.1", "guzzlehttp/psr7": "^2.7.0", + "http-interop/http-factory-tests": "^2.2", "laminas/laminas-diactoros": "^3.5.0", "nyholm/psr7": "^1.8.2", "phpbench/phpbench": "^1.3.1", - "phpstan/phpstan": "^1.12.14", + "phpstan/phpstan": "^1.12.15", "phpstan/phpstan-deprecation-rules": "^1.2.1", "phpstan/phpstan-phpunit": "^1.4.2", "phpstan/phpstan-strict-rules": "^1.6.1", - "phpunit/phpunit": "^10.5.17 || ^11.5.2", + "phpunit/phpunit": "^10.5.17 || ^11.5.3", "psr/http-factory": "^1.1.0", "psr/http-message": "^1.1.0 || ^2.0", + "slim/psr7": "^1.7", "symfony/var-dumper": "^6.4.15", "uri-templates/uritemplate-test": "dev-master" }, @@ -61,7 +63,11 @@ ], "autoload": { "psr-4": { - "League\\Uri\\": ["uri", "components", "interfaces"] + "League\\Uri\\": [ + "uri", + "components", + "interfaces" + ] } }, "scripts": { diff --git a/docs/components/7.0/index.md b/docs/components/7.0/index.md index 1fdc052d..7f101c57 100644 --- a/docs/components/7.0/index.md +++ b/docs/components/7.0/index.md @@ -11,7 +11,10 @@ Uri Components Introduction ------- +[![Author](https://img.shields.io/badge/author-@nyamsprod-blue.svg?style=flat-square)](https://twitter.com/nyamsprod) [![Latest Version](https://img.shields.io/github/release/thephpleague/uri-components.svg?style=flat-square)](https://github.com/thephpleague/uri-components/releases) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
+[![Total Downloads](https://img.shields.io/packagist/dt/league/uri.svg?style=flat-square)](https://packagist.org/packages/league/uri) While working with URI, you may stumble on some tasks, such as parsing its query string or updating its host, that are not covered by the [URI package](/uri/7.0/). diff --git a/interfaces/Contracts/UriRenderer.php b/interfaces/Contracts/UriRenderer.php index ddb746c6..f01d4547 100644 --- a/interfaces/Contracts/UriRenderer.php +++ b/interfaces/Contracts/UriRenderer.php @@ -45,7 +45,7 @@ public function toNormalizedString(): ?string; * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.2 */ - public function toDisplayString(): ?string; + public function toDisplayString(): string; /** * Returns the string representation as a URI reference. diff --git a/interfaces/UriString.php b/interfaces/UriString.php index 4d0a6b23..e0ba6f42 100644 --- a/interfaces/UriString.php +++ b/interfaces/UriString.php @@ -338,40 +338,45 @@ public static function normalizeAuthority(Stringable|string $authority): string */ public static function resolve(Stringable|string $uri, Stringable|string|null $baseUri = null): string { + $uri = (string) $uri; if ('' === $uri) { $uri = $baseUri ?? throw new SyntaxError('The uri can not be the empty string when there\'s no base URI.'); } - $uri = self::parse($uri); - $baseUri = null !== $baseUri ? self::parse($baseUri) : $uri; - if (null === $baseUri['scheme']) { + $uriComponents = self::parse($uri); + $baseUriComponents = $uriComponents; + if (null !== $baseUri && (string) $uri !== (string) $baseUri) { + $baseUriComponents = self::parse($baseUri); + } + + if (null === $baseUriComponents['scheme']) { throw new SyntaxError('The base URI must be an absolute URI or null; If the base URI is null the URI must be an absolute URI.'); } - if (null !== $uri['scheme'] && '' !== $uri['scheme']) { - $uri['path'] = self::removeDotSegments($uri['path']); + if (null !== $uriComponents['scheme'] && '' !== $uriComponents['scheme']) { + $uriComponents['path'] = self::removeDotSegments($uriComponents['path']); - return UriString::build($uri); + return UriString::build($uriComponents); } - if (null !== self::buildAuthority($uri)) { - $uri['scheme'] = $baseUri['scheme']; - $uri['path'] = self::removeDotSegments($uri['path']); + if (null !== self::buildAuthority($uriComponents)) { + $uriComponents['scheme'] = $baseUriComponents['scheme']; + $uriComponents['path'] = self::removeDotSegments($uriComponents['path']); - return UriString::build($uri); + return UriString::build($uriComponents); } - [$path, $query] = self::resolvePathAndQuery($uri, $baseUri); + [$path, $query] = self::resolvePathAndQuery($uriComponents, $baseUriComponents); $path = UriString::removeDotSegments($path); - if ('' !== $path && '/' !== $path[0] && null !== self::buildAuthority($baseUri)) { + if ('' !== $path && '/' !== $path[0] && null !== self::buildAuthority($baseUriComponents)) { $path = '/'.$path; } - $baseUri['path'] = $path; - $baseUri['query'] = $query; - $baseUri['fragment'] = $uri['fragment']; + $baseUriComponents['path'] = $path; + $baseUriComponents['query'] = $query; + $baseUriComponents['fragment'] = $uriComponents['fragment']; - return UriString::build($baseUri); + return UriString::build($baseUriComponents); } /** diff --git a/interfaces/UriStringTest.php b/interfaces/UriStringTest.php index 6676ec64..2a0af99c 100644 --- a/interfaces/UriStringTest.php +++ b/interfaces/UriStringTest.php @@ -1025,10 +1025,11 @@ public function it_fails_parsing_uri_string_with_whitespace(string $uri): void public static function invalidUriWithWhitespaceProvider(): iterable { + yield 'empty string' => ['uri' => '']; yield 'uri containing only whitespaces' => ['uri' => ' ']; - yield 'uri stating with whitespace' => ['uri' => ' https://a/b?c']; - yield 'uri ending with whitespace' => ['uri' => 'https://a/b?c ']; - yield 'uri surrounded with whitespace' => ['uri' => ' https://a/b?c ']; - yield 'uri containing with whitespace' => ['uri' => 'https://a/b ?c']; + yield 'uri starting with whitespaces' => ['uri' => ' https://a/b?c']; + yield 'uri ending with whitespaces' => ['uri' => 'https://a/b?c ']; + yield 'uri surrounded by whitespaces' => ['uri' => ' https://a/b?c ']; + yield 'uri containing with whitespaces' => ['uri' => 'https://a/b ?c']; } } diff --git a/uri/FactoryTest.php b/uri/FactoryTest.php index e89e2b38..2ec238ee 100644 --- a/uri/FactoryTest.php +++ b/uri/FactoryTest.php @@ -11,11 +11,13 @@ namespace League\Uri; +use InvalidArgumentException; use League\Uri\Exceptions\SyntaxError; use Nyholm\Psr7\Uri as NyholmUri; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Stringable; @@ -549,4 +551,22 @@ public function testICanBeInstantiateFromRFC6750(): void Uri::fromTemplate($template, $params)->getPath() ); } + + #[Test] + #[DataProvider('invalidUriWithWhitespaceProvider')] + public function it_fails_parsing_uri_string_with_whitespace(string $uri): void + { + $this->expectException(InvalidArgumentException::class); + + Uri::new($uri); + } + + public static function invalidUriWithWhitespaceProvider(): iterable + { + yield 'uri containing only whitespaces' => ['uri' => ' ']; + yield 'uri starting with whitespaces' => ['uri' => ' https://a/b?c']; + yield 'uri ending with whitespaces' => ['uri' => 'https://a/b?c ']; + yield 'uri surrounded by whitespaces' => ['uri' => ' https://a/b?c ']; + yield 'uri containing whitespaces' => ['uri' => 'https://a/b ?c']; + } } diff --git a/uri/HttpFactoryTest.php b/uri/HttpFactoryTest.php index 6d067bbf..fdd8ce08 100644 --- a/uri/HttpFactoryTest.php +++ b/uri/HttpFactoryTest.php @@ -13,16 +13,42 @@ namespace League\Uri; +use InvalidArgumentException; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\UriFactoryInterface; final class HttpFactoryTest extends TestCase { + protected function createUriFactory(): UriFactoryInterface + { + return new HttpFactory(); + } + public function testCreateUri(): void { - $factory = new HttpFactory(); - $uri = $factory->createUri('https://nyholm.tech/foo'); + $uri = $this->createUriFactory()->createUri('https://nyholm.tech/foo'); self::assertInstanceOf(Http::class, $uri); self::assertEquals('https://nyholm.tech/foo', $uri->__toString()); } + + #[Test] + #[DataProvider('invalidUriWithWhitespaceProvider')] + public function it_fails_parsing_uri_string_with_whitespace(string $uri): void + { + $this->expectException(InvalidArgumentException::class); + + $this->createUriFactory()->createUri($uri); + } + + public static function invalidUriWithWhitespaceProvider(): iterable + { + yield 'uri containing only whitespaces' => ['uri' => ' ']; + yield 'uri starting with whitespaces' => ['uri' => ' https://a/b?c']; + yield 'uri ending with whitespaces' => ['uri' => 'https://a/b?c ']; + yield 'uri surrounded with whitespaces' => ['uri' => ' https://a/b?c ']; + yield 'uri containing whitespaces' => ['uri' => 'https://a/b ?c']; + } }