Skip to content

Commit

Permalink
Adding new mwthods to UriString
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Dec 27, 2024
1 parent cd2ea2d commit 4f2cde4
Show file tree
Hide file tree
Showing 3 changed files with 6 additions and 81 deletions.
84 changes: 6 additions & 78 deletions Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
use function array_keys;
use function array_map;
use function array_pop;
use function array_reduce;
use function base64_decode;
use function base64_encode;
use function count;
Expand All @@ -64,7 +63,6 @@
use function ltrim;
use function preg_match;
use function preg_replace_callback;
use function preg_split;
use function rawurldecode;
use function rawurlencode;
use function restore_error_handler;
Expand All @@ -74,13 +72,11 @@
use function str_repeat;
use function str_replace;
use function str_starts_with;
use function strcmp;
use function strlen;
use function strpos;
use function strspn;
use function strtolower;
use function substr;
use function uksort;

use const FILEINFO_MIME;
use const FILEINFO_MIME_TYPE;
Expand All @@ -91,7 +87,6 @@
use const FILTER_VALIDATE_IP;
use const JSON_PRESERVE_ZERO_FRACTION;
use const PHP_ROUND_HALF_EVEN;
use const PREG_SPLIT_NO_EMPTY;

/**
* @phpstan-import-type ComponentMap from UriString
Expand Down Expand Up @@ -244,9 +239,6 @@ final class Uri implements Conditionable, UriInterface, UriRenderer, UriInspecto
/** @var array<string,int> */
private const WHATWG_SPECIAL_SCHEMES = ['ftp' => 1, 'http' => 1, 'https' => 1, 'ws' => 1, 'wss' => 1];

/** @var array<string,int> */
private const DOT_SEGMENTS = ['.' => 1, '..' => 1];

private readonly ?string $scheme;
private readonly ?string $user;
private readonly ?string $pass;
Expand Down Expand Up @@ -1686,17 +1678,18 @@ public function equals(UriInterface|Stringable|string $uri, bool $excludeFragmen
public function normalize(): UriInterface
{
return $this
->withUserInfo($this->decodeUnreservedCharacters($this->user), $this->decodeUnreservedCharacters($this->pass))
->withHost($this->normalizeHost())
->withPath($this->normalizePath())
->withQuery($this->decodeUnreservedCharacters($this->sortQuery($this->query)))
->withQuery($this->decodeUnreservedCharacters($this->query))
->withFragment($this->decodeUnreservedCharacters($this->fragment));
}

private function normalizePath(): string
{
$path = $this->path;
if ('/' === ($path[0] ?? '') || '' !== $this->scheme.$this->authority) {
$path = self::removeDotSegments($path);
$path = UriString::removeDotSegments($path);
}

$path = (string) $this->decodeUnreservedCharacters($path);
Expand All @@ -1720,71 +1713,6 @@ private function decodeUnreservedCharacters(?string $str): ?string
};
}

private function sortQuery(?string $query): ?string
{
$codepoints = fn (?string $str): string => in_array($str, ['', null], true) ? '' : implode('.', array_map(
mb_ord(...), /* @phpstan-ignore-line */
(array) preg_split(pattern:'//u', subject: $str, flags: PREG_SPLIT_NO_EMPTY)
));

$compare = fn (string $name1, string $name2): int => match (1) {
preg_match('/[^\x20-\x7f]/', $name1.$name2) => strcmp($codepoints($name1), $codepoints($name2)),
default => strcmp($name1, $name2),
};

$pairs = QueryString::parseFromValue($query);
$parameters = array_reduce($pairs, function (array $carry, array $pair) {
$carry[$pair[0]] ??= [];
$carry[$pair[0]][] = $pair[1];

return $carry;
}, []);

uksort($parameters, $compare);

$newPairs = [];
foreach ($parameters as $key => $values) {
$newPairs = [...$newPairs, ...array_map(fn ($value) => [$key, $value], $values)];
}

return match ($newPairs) {
$pairs => $query,
default => QueryString::buildFromPairs($newPairs),
};
}

/**
* Remove dot segments from the URI path as per RFC specification.
*/
private static function removeDotSegments(string $path): string
{
if (!str_contains($path, '.')) {
return $path;
}

$reducer = function (array $carry, string $segment): array {
if ('..' === $segment) {
array_pop($carry);

return $carry;
}

if (!isset(static::DOT_SEGMENTS[$segment])) {
$carry[] = $segment;
}

return $carry;
};

$oldSegments = explode('/', $path);
$newPath = implode('/', array_reduce($oldSegments, $reducer(...), []));
if (isset(static::DOT_SEGMENTS[end($oldSegments)])) {
$newPath .= '/';
}

return $newPath;
}

/**
* Resolves a URI against a base URI using RFC3986 rules.
*
Expand All @@ -1804,17 +1732,17 @@ public function resolve(Stringable|string $uri): UriInterface

if (null !== $uri->getScheme()) {
return $uri
->withPath(self::removeDotSegments($uri->getPath()));
->withPath(UriString::removeDotSegments($uri->getPath()));
}

if (null !== $uri->getAuthority()) {
return $uri
->withPath(self::removeDotSegments($uri->getPath()))
->withPath(UriString::removeDotSegments($uri->getPath()))
->withScheme($this->scheme);
}

[$path, $query] = $this->resolvePathAndQuery($uri);
$path = self::removeDotSegments($path);
$path = UriString::removeDotSegments($path);
if ('' !== $path && '/' !== $path[0] && null !== $this->getAuthority()) {
$path = '/'.$path;
}
Expand Down
2 changes: 0 additions & 2 deletions UriTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,6 @@ public static function resolveProvider(): array
];
}



public function testRelativizeIsNotMade(): void
{
$uri = '//path#fragment';
Expand Down
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
"league/uri-components" : "Needed to easily manipulate URI objects components",
"php-64bit": "to improve IPV4 host parsing",
"symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present",
"symfony/polyfill-mbstring": "to handle URI normalization if the ext-mbstring is not present"
},
"extra": {
"branch-alias": {
Expand Down

0 comments on commit 4f2cde4

Please sign in to comment.