Skip to content

Commit

Permalink
Improve internal codebase (#138)
Browse files Browse the repository at this point in the history
* Improve internal codebase
* Deprecate usage of PSR-7 in API
* Adding IPv6 Converter and usage in uri-interfaces and uri-components
  • Loading branch information
nyamsprod committed Jun 26, 2024
1 parent dd10ba6 commit 921c180
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 81 deletions.
22 changes: 4 additions & 18 deletions BaseUri.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class BaseUri implements Stringable, JsonSerializable, UriAccess
protected readonly Psr7UriInterface|UriInterface|null $origin;
protected readonly ?string $nullValue;

/**
* @param Psr7UriInterface|UriInterface $uri
* @param UriFactoryInterface|null $uriFactory Deprecated, will be removed in the next major release
*/
final protected function __construct(
protected readonly Psr7UriInterface|UriInterface $uri,
protected readonly ?UriFactoryInterface $uriFactory
Expand Down Expand Up @@ -79,24 +83,6 @@ public function getUri(): Psr7UriInterface|UriInterface
return $this->uri;
}

public function getIdnUriString(): string
{
$currentHost = $this->uri->getHost();
if (null === $currentHost || '' === $currentHost) {
return $this->getUriString();
}

$host = Converter::toUnicode($currentHost)->domain();
if ($host === $currentHost) {
return $this->getUriString();
}

$components = $this->uri instanceof UriInterface ? $this->uri->getComponents() : UriString::parse($this->uri);
$components['host'] = $host;

return UriString::build($components);
}

public function getUriString(): string
{
return $this->uri->__toString();
Expand Down
29 changes: 0 additions & 29 deletions BaseUriTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -583,33 +583,4 @@ public static function rfc8089UriProvider(): iterable
],
];
}

#[DataProvider('idnUriProvider')]
public function testItReturnsTheCorrectUriString(string $expected, string $input): void
{
self::assertSame($expected, BaseUri::from($input)->getIdnUriString());
}

public static function idnUriProvider(): iterable
{
yield 'basic uri stays the same' => [
'expected' => 'http://example.com/foo/bar',
'input' => 'http://example.com/foo/bar',
];

yield 'idn host are changed' => [
'expected' => "http://bébé.be",
'input' => "http://xn--bb-bjab.be",
];

yield 'idn host are the same' => [
'expected' => "http://bébé.be",
'input' => "http://bébé.be",
];

yield 'the rest of the URI is not affected and uses RFC3986 rules' => [
'expected' => "http://bébé.be?q=toto%20le%20h%C3%A9ros",
'input' => "http://bébé.be:80?q=toto le héros",
];
}
}
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ All Notable changes to `League\Uri` will be documented in this file

### Added

- Adding `BaseUri::getIdnUriString`
- `Uri::getUsername` returns the encoded user component of the URI.
- `Uri::getPassword` returns the encoded password component of the URI.

### Fixed

- None
- Adding `SensitiveParameter` attribute in the `Uri` and the `BaseUri` class.

### Deprecated

- None
- Usage of PSR-7 `UriFactoryInterface` is deprecated in `BaseUri` class

### Removed

Expand Down
73 changes: 42 additions & 31 deletions Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ final class Uri implements UriInterface
*
* @var string
*/
private const REGEXP_HOST_IPFUTURE = '/^
private const REGEXP_HOST_IP_FUTURE = '/^
v(?<version>[A-F\d])+\.
(?:
(?<unreserved>[a-z\d_~\-\.])|
Expand Down Expand Up @@ -201,6 +201,10 @@ final class Uri implements UriInterface
/** @readonly */
private ?string $scheme;
/** @readonly */
private ?string $username;
/** @readonly */
private ?string $password;
/** @readonly */
private ?string $userInfo;
/** @readonly */
private ?string $host;
Expand All @@ -219,8 +223,7 @@ final class Uri implements UriInterface
private function __construct(
?string $scheme,
?string $user,
#[SensitiveParameter]
?string $pass,
#[SensitiveParameter] ?string $pass,
?string $host,
?int $port,
string $path,
Expand All @@ -229,6 +232,7 @@ private function __construct(
) {
$this->scheme = $this->formatScheme($scheme);
$this->userInfo = $this->formatUserInfo($user, $pass);
[$this->username, $this->password] = $this->setUserAndPass();
$this->host = $this->formatHost($host);
$this->port = $this->formatPort($port);
$this->authority = $this->setAuthority();
Expand Down Expand Up @@ -263,8 +267,7 @@ private function formatScheme(?string $scheme): ?string
*/
private function formatUserInfo(
?string $user,
#[SensitiveParameter]
?string $password
#[SensitiveParameter] ?string $password
): ?string {
return match (null) {
$password => Encoder::encodeUser($user),
Expand All @@ -289,7 +292,7 @@ private function formatHost(?string $host): ?string
$formattedHost = '[' === $host[0] ? $this->formatIp($host) : $this->formatRegisteredName($host);
$formattedHostCache[$host] = $formattedHost;
if (self::MAXIMUM_FORMATTED_HOST_CACHED < count($formattedHostCache)) {
unset($formattedHostCache[array_key_first($formattedHostCache)]);
array_shift($formattedHostCache);
}

return $formattedHost;
Expand Down Expand Up @@ -327,7 +330,7 @@ private function formatIp(string $host): string
return $host;
}

if (1 === preg_match(self::REGEXP_HOST_IPFUTURE, $ip, $matches) && !in_array($matches['version'], ['4', '6'], true)) {
if (1 === preg_match(self::REGEXP_HOST_IP_FUTURE, $ip, $matches) && !in_array($matches['version'], ['4', '6'], true)) {
return $host;
}

Expand Down Expand Up @@ -377,21 +380,6 @@ public static function new(#[SensitiveParameter] Stringable|string $uri = ''): s
{
$components = match (true) {
$uri instanceof UriInterface => $uri->getComponents(),
$uri instanceof Psr7UriInterface => (function (Psr7UriInterface $uri): array {
$normalize = fn ($component) => '' !== $component ? $component : null;
$userInfo = $uri->getUserInfo();
[$user, $pass] = '' !== $userInfo ? explode(':', $userInfo, 2) : ['', ''];
return [
'scheme' => $normalize($uri->getScheme()),
'user' => $normalize($user),
'pass' => $normalize($pass),
'host' => $normalize($uri->getHost()),
'port' => $uri->getPort(),
'path' => $uri->getPath(),
'query' => $normalize($uri->getQuery()),
'fragment' => $normalize($uri->getFragment()),
];
})($uri),
default => UriString::parse($uri),
};

Expand All @@ -412,7 +400,7 @@ public static function new(#[SensitiveParameter] Stringable|string $uri = ''): s
*
* The returned URI must be absolute.
*/
public static function fromBaseUri(Stringable|string $uri, Stringable|string|null $baseUri = null): self
public static function fromBaseUri(#[SensitiveParameter] Stringable|string $uri, #[SensitiveParameter] Stringable|string|null $baseUri = null): self
{
$uri = self::new($uri);
$baseUri = BaseUri::from($baseUri ?? $uri);
Expand Down Expand Up @@ -447,7 +435,7 @@ public static function fromTemplate(UriTemplate|Stringable|string $template, ite
*
* @param InputComponentMap $components a hash representation of the URI similar to PHP parse_url function result
*/
public static function fromComponents(array $components = []): self
public static function fromComponents(#[SensitiveParameter] array $components = []): self
{
$components += [
'scheme' => null, 'user' => null, 'pass' => null, 'host' => null,
Expand Down Expand Up @@ -507,7 +495,7 @@ public static function fromFileContents(Stringable|string $path, $context = null
}

/**
* Create a new instance from a data string.
* Create a new instance from a data URI string.
*
* @throws SyntaxError If the parameter syntax is invalid
*/
Expand Down Expand Up @@ -609,7 +597,7 @@ public static function fromRfc8089(Stringable|string $uri): UriInterface
/**
* Create a new instance from the environment.
*/
public static function fromServer(array $server): self
public static function fromServer(#[SensitiveParameter] array $server): self
{
$components = ['scheme' => self::fetchScheme($server)];
[$components['user'], $components['pass']] = self::fetchUserInfo($server);
Expand Down Expand Up @@ -637,7 +625,7 @@ private static function fetchScheme(array $server): string
*
* @return non-empty-array{0: ?string, 1: ?string}
*/
private static function fetchUserInfo(array $server): array
private static function fetchUserInfo(#[SensitiveParameter] array $server): array
{
$server += ['PHP_AUTH_USER' => null, 'PHP_AUTH_PW' => null, 'HTTP_AUTHORIZATION' => ''];
$user = $server['PHP_AUTH_USER'];
Expand Down Expand Up @@ -742,6 +730,14 @@ private function setAuthority(): ?string
return $authority;
}

/**
* @return non-empty-array<int, ?string>
*/
private function setUserAndPass(): array
{
return null !== $this->userInfo ? explode(':', $this->userInfo, 2) + [1 => null] : [null, null];
}

/**
* Format the Path component.
*/
Expand Down Expand Up @@ -998,12 +994,10 @@ public function jsonSerialize(): string
*/
public function getComponents(): array
{
[$user, $pass] = null !== $this->userInfo ? explode(':', $this->userInfo, 2) : [null, null];

return [
'scheme' => $this->scheme,
'user' => $user,
'pass' => $pass,
'user' => $this->username,
'pass' => $this->password,
'host' => $this->host,
'port' => $this->port,
'path' => $this->path,
Expand All @@ -1028,6 +1022,22 @@ public function getAuthority(): ?string
return $this->authority;
}

/**
* {@inheritDoc}
*/
public function getUsername(): ?string
{
return $this->username;
}

/**
* {@inheritDoc}
*/
public function getPassword(): ?string
{
return $this->password;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -1139,6 +1149,7 @@ public function withUserInfo(

$clone = clone $this;
$clone->userInfo = $user_info;
[$clone->username, $clone->password] = $clone->setUserAndPass();
$clone->authority = $clone->setAuthority();
$clone->assertValidState();

Expand Down

0 comments on commit 921c180

Please sign in to comment.