From e457b42b2f0654ea2d9c29adef1557d3382faa9d Mon Sep 17 00:00:00 2001 From: ignace nyamagana butera Date: Fri, 29 Sep 2023 16:28:13 +0200 Subject: [PATCH] Adding BaseUri::unixPath and BaseUri::windowsPath (#125) --- docs/uri/7.0/base-uri.md | 16 ++++++++ uri/BaseUri.php | 51 +++++++++++++++++++++++ uri/BaseUriTest.php | 87 ++++++++++++++++++++++++++++++++++++++++ uri/CHANGELOG.md | 3 +- 4 files changed, 156 insertions(+), 1 deletion(-) diff --git a/docs/uri/7.0/base-uri.md b/docs/uri/7.0/base-uri.md index dd1b9ba9..4fec0c13 100644 --- a/docs/uri/7.0/base-uri.md +++ b/docs/uri/7.0/base-uri.md @@ -195,3 +195,19 @@ use League\Uri\BaseUri; BaseUri::from((Http::new('/path/to/endpoint'))->origin(); //returns null ~~~ + +### BaseUri::unixPath and BaseUri::windowsPath + +

since version 7.3.0

+ +Returns the OS specific path from a URI. + +~~~php +BaseUri::from('file://server/share/My%20Documents%20100%2520/foo.txt')->windowsPath(); +//returns '\\server\share\My Documents 100%20\foo.txt' + +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, diff --git a/uri/BaseUri.php b/uri/BaseUri.php index c338b903..b98687cc 100644 --- a/uri/BaseUri.php +++ b/uri/BaseUri.php @@ -29,7 +29,11 @@ use function end; use function explode; use function implode; +use function in_array; +use function preg_match; +use function rawurldecode; use function str_repeat; +use function str_replace; use function strpos; use function substr; @@ -98,6 +102,53 @@ public function origin(): ?self }; } + /** + * Returns the Unix filesystem path. + * + * The method will return null if a scheme is present and is not the `file` scheme + */ + public function unixPath(): ?string + { + return match ($this->uri->getScheme()) { + 'file', $this->nullValue => rawurldecode($this->uri->getPath()), + default => null, + }; + } + + /** + * Returns the Windows filesystem path. + * + * The method will return null if a scheme is present and is not the `file` scheme + */ + public function windowsPath(): ?string + { + static $regexpWindowsPath = ',^(?[a-zA-Z]:),'; + + if (!in_array($this->uri->getScheme(), ['file', $this->nullValue], true)) { + return null; + } + + $originalPath = $this->uri->getPath(); + $path = $originalPath; + if ('/' === ($path[0] ?? '')) { + $path = substr($path, 1); + } + + if (1 === preg_match($regexpWindowsPath, $path, $matches)) { + $root = $matches['root']; + $path = substr($path, strlen($root)); + + return $root.str_replace('/', '\\', rawurldecode($path)); + } + + $host = $this->uri->getHost(); + + return match ($this->nullValue) { + $host => str_replace('/', '\\', rawurldecode($originalPath)), + default => '\\\\'.$host.'\\'.str_replace('/', '\\', rawurldecode($path)), + }; + } + /** * Tells whether two URI do not share the same origin. */ diff --git a/uri/BaseUriTest.php b/uri/BaseUriTest.php index 1704309d..22aaee1c 100644 --- a/uri/BaseUriTest.php +++ b/uri/BaseUriTest.php @@ -488,4 +488,91 @@ public static function provideIDNUri(): iterable 'expected' => false, ]; } + + /** @dataProvider unixpathProvider */ + public function testReturnsUnixPath(?string $expected, string $input): void + { + self::assertSame($expected, BaseUri::from($input)->unixPath()); + self::assertSame($expected, BaseUri::from(Utils::uriFor($input))->unixPath()); + } + + public static function unixpathProvider(): array + { + return [ + 'relative path' => [ + 'expected' => 'path', + 'input' => 'path', + ], + 'absolute path' => [ + 'expected' => '/path', + 'inout' => 'file:///path', + ], + 'path with empty char' => [ + 'expected' => '/path empty/bar', + 'inout' => 'file:///path%20empty/bar', + ], + 'relative path with dot segments' => [ + 'expected' => 'path/./relative', + 'input' => 'path/./relative', + ], + 'absolute path with dot segments' => [ + 'expected' => '/path/./../relative', + 'input' => 'file:///path/./../relative', + ], + 'unsupported scheme' => [ + 'expected' => null, + 'input' => 'http://example.com/foo/bar', + ], + ]; + } + + /** @dataProvider windowLocalPathProvider */ + 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 + { + return [ + 'relative path' => [ + 'expected' => 'path', + 'input' => 'path', + ], + 'relative path with dot segments' => [ + 'expected' => 'path\.\relative', + 'input' => 'path/./relative', + ], + 'absolute path' => [ + 'expected' => 'c:\windows\My Documents 100%20\foo.txt', + 'input' => 'file:///c:/windows/My%20Documents%20100%2520/foo.txt', + ], + 'windows relative path' => [ + 'expected' => 'c:My Documents 100%20\foo.txt', + 'input' => 'file:///c:My%20Documents%20100%2520/foo.txt', + ], + 'absolute path with `|`' => [ + 'expected' => 'c:\windows\My Documents 100%20\foo.txt', + 'input' => 'file:///c:/windows/My%20Documents%20100%2520/foo.txt', + ], + 'windows relative path with `|`' => [ + 'expected' => 'c:My Documents 100%20\foo.txt', + 'input' => 'file:///c:My%20Documents%20100%2520/foo.txt', + ], + 'absolute path with dot segments' => [ + 'expected' => '\path\.\..\relative', + 'input' => '/path/./../relative', + ], + 'absolute UNC path' => [ + 'expected' => '\\\\server\share\My Documents 100%20\foo.txt', + 'input' => 'file://server/share/My%20Documents%20100%2520/foo.txt', + ], + 'unsupported scheme' => [ + 'expected' => null, + 'input' => 'http://example.com/foo/bar', + ], + ]; + } } diff --git a/uri/CHANGELOG.md b/uri/CHANGELOG.md index 50b38176..e6bd05fc 100644 --- a/uri/CHANGELOG.md +++ b/uri/CHANGELOG.md @@ -6,7 +6,8 @@ All Notable changes to `League\Uri` will be documented in this file ### Added -- None +- `BaseUri::unixPath` +- `BaseUri::windowsPath` ### Fixed