diff --git a/src/Uri.php b/src/Uri.php index 5d1516c..4e7fc55 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -449,7 +449,12 @@ public function getQueryItem(string $name, int $flags = 0): array|string|null public function withQueryItems(array $items, int $flags = 0): UriInterface { $clone = $this->clone($flags); - $clone->setQuery(http_build_query($this->merge($this->getQueryItems($flags), $items)), $flags); + $clone->setQuery(http_build_query( + $this->queryMerge($this->getQueryItems($flags), $items), + '', + null, + PHP_QUERY_RFC3986 + ), $flags); return $clone; } @@ -557,7 +562,7 @@ private function parse(string $uri_string = ''): void if (empty($auth) && $main['authority'] !== '') { throw new InvalidArgumentException("Invalid 'authority'."); } - if ($this->isEmpty($auth['host']) && !$this->isEmpty($auth['user'])) { + if ($auth['host'] === '' && $auth['user'] !== '') { throw new InvalidArgumentException("Invalid 'authority'."); } $this->setUser(isset($auth['user']) ? $auth['user'] : ''); @@ -589,14 +594,10 @@ private function uriEncode(string $source, int $flags = 0, string $keep = ''): s }, $source); } - private function formatComponent(mixed $value, string $before = '', string $after = ''): string + private function formatComponent(string|int|null $value, string $before = '', string $after = ''): string { - return $this->isEmpty($value) ? '' : "{$before}{$value}{$after}"; - } - - private function isEmpty(mixed $value): bool - { - return is_null($value) || $value === ''; + $string = strval($value); + return $string === '' ? '' : "{$before}{$string}{$after}"; } private function normalizePath(string $path): string @@ -645,15 +646,17 @@ private function idnDecode(string $value): string return idn_to_utf8($value, IDNA_NONTRANSITIONAL_TO_UNICODE, INTL_IDNA_VARIANT_UTS46); } - private function merge(array $a, array $b): array + private function queryMerge(array $a, array $b): array { foreach ($b as $key => $value) { if (is_int($key)) { $a[] = $value; - } elseif (array_key_exists($key, $a) && is_array($a[$key]) && is_array($b[$key])) { - $a[$key] = $this->merge($a[$key], $b[$key]); + } elseif (is_array($value)) { + $a[$key] = $this->queryMerge($a[$key] ?? [], $b[$key] ?? []); + } elseif (is_scalar($value)) { + $a[$key] = rawurldecode($b[$key]); } else { - $a[$key] = $b[$key]; + unset($a[$key]); } } return $a; diff --git a/tests/UriExtensionsTest.php b/tests/UriExtensionsTest.php index 698f72b..4baa425 100644 --- a/tests/UriExtensionsTest.php +++ b/tests/UriExtensionsTest.php @@ -234,6 +234,19 @@ public function testComponents(): void $this->assertEquals(parse_url($uri_str), $uri->getComponents()); } + public function testQueryHelpers2(): void + { + $uri = new Uri('http://domain.tld:80/path?aaa=ö +-:;%C3%B6'); + $this->assertEquals('aaa=%C3%B6%20+-:;%C3%B6', $uri->getQuery()); + $this->assertEquals(['aaa' => 'ö -:;ö'], $uri->getQueryItems()); + $this->assertEquals('ö -:;ö', $uri->getQueryItem('aaa')); + + $uri = $uri->withQueryItem('aaa', 'å -+:;%C3%A5'); + $this->assertEquals('aaa=%C3%A5%20-%2B%3A%3B%C3%A5', $uri->getQuery()); + $this->assertEquals(['aaa' => 'å -+:;å'], $uri->getQueryItems()); + $this->assertEquals('å -+:;å', $uri->getQueryItem('aaa')); + } + public function testQueryHelpers(): void { $uri = new Uri('http://domain.tld:80/path?arr%5B0%5D=arr1&arr%5B1%5D=arr2#fragment');