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 97d13ab commit 0ca8ecb
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 125 deletions.
37 changes: 2 additions & 35 deletions components/Components/Path.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,14 @@
use League\Uri\Contracts\UriInterface;
use League\Uri\Encoder;
use League\Uri\Uri;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;

use function array_pop;
use function array_reduce;
use function end;
use function explode;
use function implode;
use function substr;

final class Path extends Component implements PathInterface
{
private const DOT_SEGMENTS = ['.' => 1, '..' => 1];
private const SEPARATOR = '/';

private readonly string $path;
Expand Down Expand Up @@ -116,35 +111,7 @@ public function withoutDotSegments(): PathInterface
return $this;
}

$input = explode(self::SEPARATOR, $current);
$new = implode(self::SEPARATOR, array_reduce($input, $this->filterDotSegments(...), []));
if (isset(self::DOT_SEGMENTS[end($input)])) {
$new .= self::SEPARATOR ;
}

return new self($new);
}

/**
* Filter Dot segment according to RFC3986.
*
* @see http://tools.ietf.org/html/rfc3986#section-5.2.4
*
* @return string[]
*/
private function filterDotSegments(array $carry, string $segment): array
{
if ('..' === $segment) {
array_pop($carry);

return $carry;
}

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

return $carry;
return new self(UriString::removeDotSegments($current));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"benchmark": "phpbench run --report=default",
"phpcs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run --allow-risky=yes --ansi",
"phpcs:fix": "php-cs-fixer fix -vvv --allow-risky=yes --ansi",
"phpstan": "phpstan analyse -l max -c phpstan.neon --ansi --memory-limit=256M",
"phpstan": "phpstan analyse -l max -c phpstan.neon --ansi --memory-limit=512M",
"phpunit": "XDEBUG_MODE=coverage phpunit --coverage-text",
"phpunit:min": "phpunit --no-coverage",
"test": [
Expand Down
56 changes: 56 additions & 0 deletions docs/interfaces/7.0/uri-parser-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The class act as a drop-in replacement for PHP's `parse_url` feature.
## URI parsing

~~~php
UriString::resolve(string $uri, ?string $baseUri = null): array
UriString::parse(string $uri): array
UriString::parseAuthority(string $autority): array
~~~
Expand Down Expand Up @@ -67,6 +68,25 @@ var_export(UriString::parse('http:www.example.com'));
<p class="message-warning">This invalid HTTP URI is successfully parsed.</p>
<p class="message-notice">The class also exposes a <code>UriString::parseAuthority</code> you can use to parse an authority string.</p>

If you need to resolve your URI in the context of a Base URI the `resolve` public static method will let you
do just that. The method expect either a full URI as its single parameter or a relative URI following by
a base URI which must be absolute, the URI will then be resolved using the base URI.

```php
$components = UriString::resolve('"/foo", "https://example.com");
//returns the following array
//array(
// 'scheme' => 'https',
// 'user' => null,
// 'pass' => null,
// 'host' => 'example.com'',
// 'port' => null,
// 'path' => '/foo',
// 'query' => null,
// 'fragment' => null,
//);
```

## URI Building

~~~php
Expand Down Expand Up @@ -99,3 +119,39 @@ echo UriString::build($components); //displays http://hello:[email protected][email protected]
The `build` method provides similar functionality to the `http_build_url()` function from v1.x of the [`pecl_http`](https://pecl.php.net/package/pecl_http) PECL extension.

<p class="message-notice">The class also exposes a <code>UriString::buildAuthority</code> you can use to build an authority from its hash representation.</p>

## URI Normalization

It is possible to normalize a URI against the RFC3986 rules using the `UriString::normalize` method.
The method expects a string and will return the same array as `UriString::parse` but each component will
have been normalized.

```php
use League\Uri\UriString;

$parsed = UriString::parse("https://EXAMPLE.COM/foo/../bar");
//returns the following array
//array(
// 'scheme' => 'http',
// 'user' => null,
// 'pass' => null,
// 'host' => 'EXAMPLE.COM',
// 'port' => null,
// 'path' => '/foo/../bar',
// 'query' => null,
// 'fragment' => null,
//);

$normalized = UriString::normalize("https://EXAMPLE.COM/foo/../bar");
//returns the following array
//array(
// 'scheme' => 'http',
// 'user' => null,
// 'pass' => null,
// 'host' => 'example.com',
// 'port' => null,
// 'path' => '/bar',
// 'query' => null,
// 'fragment' => null,
//);
```
3 changes: 0 additions & 3 deletions docs/uri/7.0/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ install the `fileinfo` extension otherwise an exception will be thrown.
To convert a URI into an HTML anchor tag you need to have the `ext-dom` extension
installed in your system.

To enable URI normalization, the `ext-mbstring` extension or a polyfill
like `symfony/polyfill-mbstring` must be present in your system.

Installation
--------

Expand Down
3 changes: 3 additions & 0 deletions interfaces/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ All Notable changes to `League\Uri\Interfaces` will be documented in this file
- `UriInterface::toNormalizedString`
- `UriInterface::getUser`
- `League\Uri\IPv6\Converter::isIpv6`
- `UriString::resolve`
- `UriString::removeDotSegments`
- `UriString::normalize`

### Fixed

Expand Down
20 changes: 20 additions & 0 deletions interfaces/Encoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ final class Encoder
private const REGEXP_PART_UNRESERVED = 'A-Za-z\d_\-.~';
private const REGEXP_PART_ENCODED = '%(?![A-Fa-f\d]{2})';

/**
* Unreserved characters.
*
* @see https://www.rfc-editor.org/rfc/rfc3986.html#section-2.3
*/
private const REGEXP_UNRESERVED_CHARACTERS = ',%(2[1-9A-Fa-f]|[3-7][0-9A-Fa-f]|61|62|64|65|66|7[AB]|5F),';

/**
* Encode User.
*
Expand Down Expand Up @@ -173,4 +180,17 @@ private static function decode(Stringable|string|int|null $component, Closure $d
default => $component,
};
}

public static function decodeUnreservedCharacters(?string $str): ?string
{
return match (true) {
null === $str,
'' === $str => $str,
default => preg_replace_callback(
self::REGEXP_UNRESERVED_CHARACTERS,
static fn (array $matches): string => rawurldecode($matches[0]),
$str
) ?? '',
};
}
}
2 changes: 1 addition & 1 deletion interfaces/Idna/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public static function toUnicode(Stringable|string $domain, Option|int|null $opt
$domain = rawurldecode((string) $domain);

if (false === stripos($domain, 'xn--')) {
return Result::fromIntl(['result' => $domain, 'isTransitionalDifferent' => false, 'errors' => Error::NONE->value]);
return Result::fromIntl(['result' => strtolower($domain), 'isTransitionalDifferent' => false, 'errors' => Error::NONE->value]);
}

FeatureDetection::supportsIdn();
Expand Down
Loading

0 comments on commit 0ca8ecb

Please sign in to comment.