Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for RFC8089 #131

Merged
merged 4 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion docs/uri/7.0/base-uri.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,30 @@ BaseUri::from('file:///path%20empty/bar')->unixPath();
//returns '/path empty/bar'
~~~

If the URI scheme is present and is not the `file` scheme, `null` will be returned,
If the URI scheme is present and is not the `file` scheme, `null` will be returned.

### BaseUri::toRfc8089

<p class="message-notice">since version <code>7.4.0</code></p>

Returns the RFC8089 representation of a file URI

~~~php
BaseUri::from('file://localhost/etc/fstab')->toRfc8089();
//returns 'file:/etc/fstab'

BaseUri::from('file:///path%20empty/bar')->toRfc8089();
//returns 'file:/path empty/bar'
~~~

If the URI scheme is not the `file` scheme, `null` will be returned.

### BaseUri::isLocalFile

<p class="message-notice">since version <code>7.4.0</code></p>

Tells whether the given URI object represents a local file path.

~~~php
BaseUri::from("file://localhost/etc/fstab")->isLocalFile(); //returns true
~~~
7 changes: 6 additions & 1 deletion docs/uri/7.0/rfc3986.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ The `fromData`
named constructor generates a [Data URI](https://datatracker.ietf.org/doc/html/rfc2397)
following its RFC specification. with the provided data and an optional mimetype and parameters.

Last but not least, you can easily translate Windows and Unix paths to URI using the two
Last but not least, you can easily translate RFC8089, Windows, Unix paths to URI using the three (3)
following methods.

~~~php
Expand All @@ -106,8 +106,13 @@ echo $uri; //returns 'file://localhost/c:My%20Documents/my%20word.docx'

$uri = Uri::fromUnixPath('/path/to/my/file.xml');
echo $uri; //returns 'file://localhost/path/to/my/file.xml'

$uri = Uri::fromRfc8089('file:/etc/fstab');
echo $uri = //returns 'file:///etc/fstab'
~~~

<p class="message-notice"><code>fromRfc8089</code> is added since version <code>7.4.0</code></p>

Accessing URI properties
-------

Expand Down
32 changes: 32 additions & 0 deletions uri/BaseUri.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,38 @@ public function windowsPath(): ?string
};
}

/**
* Returns a string representation of a File URI according to RFC8089.
*
* The method will return null if the URI scheme is not the `file` scheme
*/
public function toRfc8089(): ?string
{
$path = $this->uri->getPath();

return match (true) {
'file' !== $this->uri->getScheme() => null,
in_array($this->uri->getAuthority(), ['', null, 'localhost'], true) => 'file:'.match (true) {
'' === $path,
'/' === $path[0] => $path,
default => '/'.$path,
},
default => (string) $this->uri,
};
}

/**
* Tells whether the `file` scheme base URI represents a local file.
*/
public function isLocalFile(): bool
{
return match (true) {
'file' !== $this->uri->getScheme() => false,
in_array($this->uri->getAuthority(), ['', null, 'localhost'], true) => true,
default => false,
};
}

/**
* Tells whether two URI do not share the same origin.
*/
Expand Down
30 changes: 29 additions & 1 deletion uri/BaseUriTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,6 @@ public function testReturnsWindowsPath(?string $expected, string $input): void
{
self::assertSame($expected, BaseUri::from($input)->windowsPath());
self::assertSame($expected, BaseUri::from(Utils::uriFor($input))->windowsPath());

}

public static function windowLocalPathProvider(): array
Expand Down Expand Up @@ -575,4 +574,33 @@ public static function windowLocalPathProvider(): array
],
];
}

/** @dataProvider rfc8089UriProvider */
public function testReturnsRFC8089UriString(?string $expected, string $input): void
{
self::assertSame($expected, BaseUri::from($input)->toRfc8089());
self::assertSame($expected, BaseUri::from(Utils::uriFor($input))->toRfc8089());
}

public static function rfc8089UriProvider(): iterable
{
return [
'localhost' => [
'expected' => 'file:/etc/fstab',
'input' => 'file://localhost/etc/fstab',
],
'empty authority' => [
'expected' => 'file:/etc/fstab',
'input' => 'file:///etc/fstab',
],
'file with authority' => [
'expected' => 'file://yesman/etc/fstab',
'input' => 'file://yesman/etc/fstab',
],
'invalid scheme' => [
'expected' => null,
'input' => 'foobar://yesman/etc/fstab',
],
];
}
}
2 changes: 2 additions & 0 deletions uri/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ All Notable changes to `League\Uri` will be documented in this file
### Added

- `Uri::fromData`
- `Uri::fromRfc8089`
- `BaseUri::unixPath`
- `BaseUri::windowsPath`
- `BaseUri::toRfc8089`

### Fixed

Expand Down
44 changes: 44 additions & 0 deletions uri/FactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,50 @@ public static function windowLocalPathProvider(): array
];
}

/** @dataProvider rfc8089UriProvider */
public function testCreateFromRfc8089(string $expected, string $uri): void
{
self::assertSame($expected, Uri::fromRfc8089($uri)->toString());
}

public static function rfc8089UriProvider(): iterable
{
return [
'empty authority' => [
'expected' => 'file:///etc/fstab',
'uri' => 'file:/etc/fstab',
],
'localhost' => [
'expected' => 'file:///etc/fstab',
'uri' => 'file://localhost/etc/fstab',
],
'file with authority' => [
'expected' => 'file://yesman/etc/fstab',
'uri' => 'file://yesman/etc/fstab',
],
];
}

/** @dataProvider invalidRfc8089UriProvider */
public function testIfFailsToGenerateAnUriFromRfc8089(string $invalidUri): void
{
$this->expectException(SyntaxError::class);

Uri::fromRfc8089($invalidUri);
}

public static function invalidRfc8089UriProvider(): iterable
{
return [
'unsupported scheme' => [
'invalidUri' => 'http://www.example.com',
],
'missing scheme' => [
'invalidUri' => '//localhost/etc/fstab',
],
];
}

public function testCreateFromUri(): void
{
$expected = 'https://login:[email protected]:443/test/query.php?kingkong=toto#doc3';
Expand Down
17 changes: 17 additions & 0 deletions uri/Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,23 @@ public static function fromWindowsPath(Stringable|string $path): self
return Uri::fromComponents(['host' => $host, 'path' => '/'.$path, 'scheme' => 'file']);
}

/**
* Creates a new instance from a RFC8089 compatible URI.
*
* @see https://datatracker.ietf.org/doc/html/rfc8089
*/
public static function fromRfc8089(Stringable|string $uri): UriInterface
{
$fileUri = self::new((string) preg_replace(',^(file:/)([^/].*)$,i', 'file:///$2', (string) $uri));
$scheme = $fileUri->getScheme();

return match (true) {
'file' !== $scheme => throw new SyntaxError('As per RFC8089, the URI scheme must be `file`.'),
'localhost' === $fileUri->getAuthority() => $fileUri->withHost(''),
default => $fileUri,
};
}

/**
* Create a new instance from the environment.
*/
Expand Down