diff --git a/components/CHANGELOG.md b/components/CHANGELOG.md index d6e627d9..3a93363e 100644 --- a/components/CHANGELOG.md +++ b/components/CHANGELOG.md @@ -10,6 +10,10 @@ All Notable changes to `League\Uri\Components` will be documented in this file - `Modifier::getIdnUriString` - `Modifier::hostToIpv6Compressed` - `Modifier::hostToIpv6Expanded` +- `Scheme::isHttp` +- `Scheme::isWebsocket` +- `Scheme::isSsl` +- `Scheme::isSpecial` ### Fixed diff --git a/components/Components/FragmentTest.php b/components/Components/FragmentTest.php index b436e34a..e3746bf7 100644 --- a/components/Components/FragmentTest.php +++ b/components/Components/FragmentTest.php @@ -141,7 +141,7 @@ public static function getURIProvider(): iterable ], 'PSR-7 URI object with empty string fragment' => [ 'uri' => Http::new('http://example.com#'), - 'expected' => '', + 'expected' => null, ], 'League URI object' => [ 'uri' => Uri::new('http://example.com#foobar'), diff --git a/components/Components/QueryTest.php b/components/Components/QueryTest.php index 1b8f7c6a..7933088d 100644 --- a/components/Components/QueryTest.php +++ b/components/Components/QueryTest.php @@ -753,7 +753,7 @@ public static function getURIProvider(): iterable ], 'PSR-7 URI object with empty string query' => [ 'uri' => Http::new('http://example.com?'), - 'expected' => '', + 'expected' => null, ], 'League URI object' => [ 'uri' => Uri::new('http://example.com?foo=bar'), diff --git a/components/Components/Scheme.php b/components/Components/Scheme.php index 0865895c..cee2248c 100644 --- a/components/Components/Scheme.php +++ b/components/Components/Scheme.php @@ -19,12 +19,30 @@ use Psr\Http\Message\UriInterface as Psr7UriInterface; use Stringable; +use function array_key_exists; +use function in_array; use function preg_match; use function sprintf; use function strtolower; final class Scheme extends Component { + /** + * Supported schemes and corresponding default port. + * + * @var array + */ + private const SCHEME_DEFAULT_PORT = [ + 'data' => null, + 'file' => null, + 'ftp' => 21, + 'gopher' => 70, + 'http' => 80, + 'https' => 443, + 'ws' => 80, + 'wss' => 443, + ]; + private const REGEXP_SCHEME = ',^[a-z]([-a-z0-9+.]+)?$,i'; private readonly ?string $scheme; @@ -34,6 +52,32 @@ private function __construct(Stringable|string|null $scheme) $this->scheme = $this->validate($scheme); } + public function isWebsocket(): bool + { + return in_array($this->scheme, ['ws', 'wss'], true); + } + + public function isHttp(): bool + { + return in_array($this->scheme, ['http', 'https'], true); + } + + public function isSsl(): bool + { + return in_array($this->scheme, ['https', 'wss'], true); + } + + public function isSpecial(): bool + { + return null !== $this->scheme + && array_key_exists($this->scheme, self::SCHEME_DEFAULT_PORT); + } + + public function defaultPort(): Port + { + return Port::new(self::SCHEME_DEFAULT_PORT[$this->scheme] ?? null); + } + /** * Validate a scheme. * @@ -50,7 +94,7 @@ private function validate(Stringable|string|null $scheme): ?string static $inMemoryCache = []; if (isset($inMemoryCache[$fScheme])) { - return $inMemoryCache[$fScheme]; + return $fScheme; } if (1 !== preg_match(self::REGEXP_SCHEME, $fScheme)) { @@ -60,8 +104,9 @@ private function validate(Stringable|string|null $scheme): ?string if (100 < count($inMemoryCache)) { unset($inMemoryCache[array_key_first($inMemoryCache)]); } + $inMemoryCache[$fScheme] = 1; - return $inMemoryCache[$fScheme] = $fScheme; + return $fScheme; } public static function new(Stringable|string|null $value = null): self @@ -92,7 +137,6 @@ public function getUriComponent(): string return $this->value().(null === $this->scheme ? '' : ':'); } - /** * DEPRECATION WARNING! This method will be removed in the next major point release. * diff --git a/components/Components/SchemeTest.php b/components/Components/SchemeTest.php index e6c0a8fd..ad0bebcb 100644 --- a/components/Components/SchemeTest.php +++ b/components/Components/SchemeTest.php @@ -106,4 +106,48 @@ public static function getURIProvider(): iterable ], ]; } + + #[DataProvider('getSchemeInfoProvider')] + public function it_can_detect_information_about_special_schemes( + ?string $scheme, + bool $isHttp, + bool $isSsl, + bool $isSpecial, + Port $defaultPort, + ): void { + self::assertSame($isHttp, Scheme::new($scheme)->isHttp()); + self::assertSame($isSsl, Scheme::new($scheme)->isSsl()); + self::assertSame($isSpecial, Scheme::new($scheme)->isSpecial()); + self::assertSame($defaultPort, Scheme::new($scheme)->defaultPort()); + } + + public static function getSchemeInfoProvider(): \Generator + { + yield 'detect an HTTP URL' => [ + 'scheme' => 'http', + 'isHttp' => true, + 'isWebsocket' => false, + 'isSsl' => false, + 'isSpecial' => true, + Port::new(80), + ]; + + yield 'detect an WSS URL' => [ + 'scheme' => 'wss', + 'isHttp' => false, + 'isWebsocket' => false, + 'isSsl' => true, + 'isSpecial' => true, + Port::new(443), + ]; + + yield 'detect an email URL' => [ + 'scheme' => 'email', + 'isHttp' => false, + 'isWebsocket' => false, + 'isSsl' => false, + 'isSpecial' => false, + Port::new(null), + ]; + } } diff --git a/components/Components/UserInfoTest.php b/components/Components/UserInfoTest.php index 42258603..42a2adbe 100644 --- a/components/Components/UserInfoTest.php +++ b/components/Components/UserInfoTest.php @@ -205,7 +205,7 @@ public static function getURIProvider(): iterable ], 'PSR-7 URI object with empty string user info' => [ 'uri' => Http::new('http://@example.com?foo=bar'), - 'expected' => '', + 'expected' => null, ], 'League URI object' => [ 'uri' => Uri::new('http://foo:bar@example.com?foo=bar'),