From b732a72d4501dbf9cd7d9a90bc6f7d6d99988570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Jensen?= Date: Sun, 4 Feb 2024 13:43:54 +0100 Subject: [PATCH] IDN encode/decode --- README.md | 4 ++-- docs/{README.md => Uri.md} | 21 +++++++++++++++++++-- src/Uri.php | 6 ++++++ tests/UriExtensionsTest.php | 20 ++++++++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) rename docs/{README.md => Uri.md} (94%) diff --git a/README.md b/README.md index 2b9477c..7ff841d 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Will cause paths to use absolute form, i.e. starting with `/`. Will attempt to normalize paths, e.g. `./a/./path/../to//something` will transform to `a/to/something`. -`IDN_ENCODE` +`IDN_ENCODE` + `IDN_ENCODE` Will IDN-encode host using non-ASCII characters. Only available with [Intl extension](https://www.php.net/manual/en/intl.installation.php). @@ -145,7 +145,7 @@ class Phrity\Net\UriFactory implements Psr\Http\Message\UriFactoryInterface | Version | PHP | | | --- | --- | --- | -| `2.0` | `^8.0` | Query helpers, with([]) and getComponents() methods | +| `2.0` | `^8.0` | Query helpers, with([]) and getComponents() methods, IDN encode/decode | | `1.3` | `^7.4\|^8.0` | | | `1.2` | `^7.4\|^8.0` | IDNA modifier | | `1.1` | `^7.4\|^8.0` | Require port, Absolute path, Normalize path modifiers | diff --git a/docs/README.md b/docs/Uri.md similarity index 94% rename from docs/README.md rename to docs/Uri.md index 59eee7c..82b5e19 100644 --- a/docs/README.md +++ b/docs/Uri.md @@ -293,9 +293,9 @@ All `get`, `with` and the `toString()` methods accept option flags. ### Host options -#### The `IDN_ENCODE` option +#### The `IDN_ENCODE` and `IDN_DECODE` options -Using this option will IDN-encode host using non-ASCII characters. +Using `IDN_ENCODE` option will IDN-encode host using non-ASCII characters. Only available with [Intl extension](https://www.php.net/manual/en/intl.installation.php). ```php @@ -312,6 +312,23 @@ $clone->getHost(); // -> "xn--7ca5b9p776i" echo "{$clone} \n"; // -> "https://xn--7ca5b9p776i" ``` +Using `IDN_DECODE` option will IDN-decode host previously encoded to ASCII-only characters. +Only available with [Intl extension](https://www.php.net/manual/en/intl.installation.php). + +```php +$uri = new Uri('https://xn--zca0cg32z7rau82strvd.com'); + +$uri->getHost(); // -> "xn--zca0cg32z7rau82strvd.com" +$uri->toString(); // -> "https://xn--zca0cg32z7rau82strvd.com" + +$uri->getHost(Uri::IDN_DECODE); // -> "ηßöø必Дあ.com" +$uri->toString(Uri::IDN_DECODE); // -> "https://xηßöø必Дあ.com" + +$clone = $uri->withHost('xn--7ca5b9p776i', Uri::IDN_DECODE); +$clone->getHost(); // -> "œüç∂" +echo "{$clone} \n"; // -> "https://œüç∂" +``` + ### Port options #### The `REQUIRE_PORT` option diff --git a/src/Uri.php b/src/Uri.php index 59ac6e5..fa6ba66 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -148,6 +148,9 @@ public function getHost(int $flags = 0): string if ($flags & self::IDN_ENCODE) { return $this->idnEncode($this->host); } + if ($flags & self::IDN_DECODE) { + return $this->idnDecode($this->host); + } return $this->host; } @@ -484,6 +487,9 @@ protected function setHost(string $host, int $flags = 0): void if ($flags & self::IDN_ENCODE) { $host = $this->idnEncode($host); } + if ($flags & self::IDN_DECODE) { + $host = $this->idnDecode($host); + } $this->host = mb_strtolower($host); } diff --git a/tests/UriExtensionsTest.php b/tests/UriExtensionsTest.php index 8429dc4..aa550d8 100644 --- a/tests/UriExtensionsTest.php +++ b/tests/UriExtensionsTest.php @@ -155,6 +155,26 @@ public function testIdnEncodeHost(): void $this->assertSame('', $clone->getHost()); } + public function testIdnDecodeHost(): void + { + // Get converted host + $uri = new Uri('https://xn--zca0cg32z7rau82strvd.com'); + $this->assertSame('xn--zca0cg32z7rau82strvd.com', $uri->getHost()); + + $this->assertSame('ηßöø必дあ.com', $uri->getHost(Uri::IDN_DECODE)); + $this->assertSame('https://xn--zca0cg32z7rau82strvd.com', $uri->toString()); + $this->assertSame('https://ηßöø必дあ.com', $uri->toString(Uri::IDN_DECODE)); + + // Should convert host on clone + $clone = $uri->withHost('xn--zca0cg32z7rau82strvd.com', Uri::IDN_DECODE); + $this->assertSame('ηßöø必дあ.com', $clone->getHost()); + $this->assertSame('https://ηßöø必дあ.com', $clone->__toString()); + + // Should not attempt conversion + $clone = $uri->withHost('', Uri::IDN_DECODE); + $this->assertSame('', $clone->getHost()); + } + public function testWithMethod(): void { $uri = new Uri('http://domain.tld:80/path?query=1#fragment');