schneespur/vendor/league/uri/FactoryTest.php
Michael ee3dbba6cc Initial release v1.0.0
Schneespur — Open-source winter service documentation software (PWA + Admin).
GPS tracking via OwnTracks, weather data, photo evidence, and legally
compliant service records for winter maintenance operators.

License: AGPL-3.0-or-later
2026-05-17 13:33:51 +00:00

589 lines
22 KiB
PHP

<?php
/**
* League.Uri (https://uri.thephpleague.com)
*
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Uri;
use InvalidArgumentException;
use League\Uri\Exceptions\SyntaxError;
use Nyholm\Psr7\Uri as NyholmUri;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;
use Stringable;
#[CoversClass(Uri::class)]
#[Group('factory')]
final class FactoryTest extends TestCase
{
#[DataProvider('invalidDataPath')]
public function testCreateFromPathFailed(string $path): void
{
self::expectException(SyntaxError::class);
Uri::fromFileContents($path);
}
public static function invalidDataPath(): array
{
return [
'invalid format' => ['/usr/bin/yeah'],
];
}
#[DataProvider('validFilePath')]
public function testCreateFromPath(string $path, string $expected): void
{
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => "Accept-language: en\r\nCookie: foo=bar\r\n",
],
]);
$uri = Uri::fromFileContents(dirname(__DIR__).'/test_files/'.$path, $context);
self::assertStringContainsString($expected, $uri->getPath());
}
public static function validFilePath(): array
{
return [
'text file' => ['hello-world.txt', 'text/plain'],
'img file' => ['red-nose.gif', 'image/gif'],
];
}
#[DataProvider('provideValidData')]
public function testFromData(string $data, string $mimetype, string $parameters, string $expected): void
{
self::assertSame($expected, Uri::fromData($data, $mimetype, $parameters)->toString());
}
public static function provideValidData(): iterable
{
yield 'empty data' => [
'data' => '',
'mimetype' => '',
'parameters' => '',
'expected' => 'data:text/plain;charset=us-ascii,',
];
yield 'simple string' => [
'data' => 'Hello World!',
'mimetype' => '',
'parameters' => '',
'expected' => 'data:text/plain;charset=us-ascii,Hello%20World%21',
];
yield 'changing the mimetype' => [
'data' => 'Hello World!',
'mimetype' => 'text/no-plain',
'parameters' => '',
'expected' => 'data:text/no-plain;charset=us-ascii,Hello%20World%21',
];
yield 'empty mimetype but added parameters' => [
'data' => 'Hello World!',
'mimetype' => '',
'parameters' => 'foo=bar',
'expected' => 'data:text/plain;foo=bar,Hello%20World%21',
];
yield 'empty data with optional argument field' => [
'data' => '',
'mimetype' => 'application/json',
'parameters' => 'foo=bar',
'expected' => 'data:application/json;foo=bar,',
];
yield 'changing the parameters' => [
'data' => 'Hello World!',
'mimetype' => 'text/no-plain',
'parameters' => 'foo=bar',
'expected' => 'data:text/no-plain;foo=bar,Hello%20World%21',
];
yield 'the parameters can start with the ; character' => [
'data' => 'Hello World!',
'mimetype' => 'text/no-plain',
'parameters' => ';foo=bar',
'expected' => 'data:text/no-plain;foo=bar,Hello%20World%21',
];
}
public function testFromDataFailsWithInvalidMimeType(): void
{
$this->expectException(SyntaxError::class);
Uri::fromData('Hello World!', 'text\plain');
}
public function testFromDataFailsWithReservedParameterName(): void
{
$this->expectException(SyntaxError::class);
Uri::fromData('Hello World!', 'text/plain', 'base64=foobar');
}
public function testFromDataFailsWithMalformedParameters(): void
{
$this->expectException(SyntaxError::class);
Uri::fromData('Hello World!', 'text/plain', 'foobar');
}
#[DataProvider('unixpathProvider')]
public function testCreateFromUnixPath(string $input, string $expected): void
{
self::assertSame($expected, Uri::fromUnixPath($input)->toString());
}
public static function unixpathProvider(): array
{
return [
'relative path' => [
'input' => 'path',
'expected' => 'path',
],
'absolute path' => [
'input' => '/path',
'expected' => 'file:///path',
],
'path with empty char' => [
'input' => '/path empty/bar',
'expected' => 'file:///path%20empty/bar',
],
'relative path with dot segments' => [
'input' => 'path/./relative',
'expected' => 'path/./relative',
],
'absolute path with dot segments' => [
'input' => '/path/./../relative',
'expected' => 'file:///path/./../relative',
],
];
}
#[DataProvider('windowLocalPathProvider')]
public function testCreateFromWindowsLocalPath(string $input, string $expected): void
{
self::assertSame($expected, Uri::fromWindowsPath($input)->toString());
}
public static function windowLocalPathProvider(): array
{
return [
'relative path' => [
'input' => 'path',
'expected' => 'path',
],
'relative path with dot segments' => [
'input' => 'path\.\relative',
'expected' => 'path/./relative',
],
'absolute path' => [
'input' => 'c:\windows\My Documents 100%20\foo.txt',
'expected' => 'file:///c:/windows/My%20Documents%20100%2520/foo.txt',
],
'windows relative path' => [
'input' => 'c:My Documents 100%20\foo.txt',
'expected' => 'file:///c:My%20Documents%20100%2520/foo.txt',
],
'absolute path with `|`' => [
'input' => 'c|\windows\My Documents 100%20\foo.txt',
'expected' => 'file:///c:/windows/My%20Documents%20100%2520/foo.txt',
],
'windows relative path with `|`' => [
'input' => 'c:My Documents 100%20\foo.txt',
'expected' => 'file:///c:My%20Documents%20100%2520/foo.txt',
],
'absolute path with dot segments' => [
'input' => '\path\.\..\relative',
'expected' => '/path/./../relative',
],
'absolute UNC path' => [
'input' => '\\\\server\share\My Documents 100%20\foo.txt',
'expected' => 'file://server/share/My%20Documents%20100%2520/foo.txt',
],
];
}
#[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',
],
];
}
#[TestWith(['invalidUri' => 'http://www.example.com'], 'unsupported scheme')]
#[TestWith(['invalidUri' => '//localhost/etc/fstab'], 'missing scheme')]
public function it_fails_to_generate_an_uri_from_rfc8089(string $invalidUri): void
{
$this->expectException(SyntaxError::class);
Uri::fromRfc8089($invalidUri);
}
public function testCreateFromUri(): void
{
$expected = 'https://login:pass@secure.example.com:443/test/query.php?kingkong=toto#doc3';
$psr7 = Http::new($expected);
$leagueUri = Uri::new($expected);
$uriFromPsr7 = Uri::new($psr7);
$uriFromLeagueUri = Uri::new($leagueUri);
self::assertSame((string) $psr7, $uriFromPsr7->toString());
self::assertSame((string) $psr7, $uriFromLeagueUri->toString());
$uribis = Http::new();
self::assertSame((string) $uribis, Uri::new($uribis)->toString());
}
#[DataProvider('validServerArray')]
public function testCreateFromServer(string $expected, array $input): void
{
self::assertSame($expected, Uri::fromServer($input)->toString());
self::assertSame($expected, (string) Http::fromServer($input));
}
public static function validServerArray(): array
{
return [
'with host' => [
'https://example.com:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'SERVER_PORT' => '23',
'HTTP_HOST' => 'example.com',
],
],
'server address IPv4' => [
'https://127.0.0.1:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
],
],
'server address IPv6' => [
'https://[::1]:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '::1',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
],
],
'with port attached to host' => [
'https://localhost:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'SERVER_PORT' => 80,
'HTTP_HOST' => 'localhost:23',
],
],
'with standard apache HTTP server' => [
'http://localhost:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => '',
'SERVER_PORT' => 80,
'HTTP_HOST' => 'localhost:23',
],
],
'with IIS HTTP server' => [
'http://localhost:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'off',
'SERVER_PORT' => 80,
'HTTP_HOST' => 'localhost:23',
],
],
'with IIS Rewritting server' => [
'http://localhost:23/foo/bar?foo=bar',
[
'PHP_SELF' => '',
'IIS_WasUrlRewritten' => '1',
'UNENCODED_URL' => '/foo/bar?foo=bar',
'REQUEST_URI' => 'toto',
'SERVER_PORT' => 23,
'HTTP_HOST' => 'localhost',
],
],
'with standard port setting' => [
'https://localhost:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
'HTTP_HOST' => 'localhost',
],
],
'without port' => [
'https://localhost',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'HTTP_HOST' => 'localhost',
],
],
'with user info' => [
'https://foo:bar@localhost:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => 'bar',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
'HTTP_HOST' => 'localhost:23',
],
],
'with user info and HTTP AUTHORIZATION' => [
'https://foo:bar@localhost:23',
[
'PHP_SELF' => '',
'REQUEST_URI' => '',
'SERVER_ADDR' => '127.0.0.1',
'HTTP_AUTHORIZATION' => 'basic '.base64_encode('foo:bar'),
'HTTPS' => 'on',
'SERVER_PORT' => 23,
'HTTP_HOST' => 'localhost:23',
],
],
'without request uri' => [
'https://127.0.0.1:23/toto?foo=bar',
[
'PHP_SELF' => '/toto',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
'QUERY_STRING' => 'foo=bar',
],
],
'without request uri and server host' => [
'https://127.0.0.1:23',
[
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
],
],
];
}
public function testFailCreateFromServerWithoutHost(): void
{
self::expectException(SyntaxError::class);
Uri::fromServer([
'PHP_SELF' => '',
'REQUEST_URI' => '',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
]);
}
public function testFailCreateFromServerWithoutInvalidUserInfo(): void
{
self::expectException(SyntaxError::class);
Uri::fromServer([
'PHP_SELF' => '/toto',
'SERVER_ADDR' => '127.0.0.1',
'HTTPS' => 'on',
'SERVER_PORT' => 23,
'QUERY_STRING' => 'foo=bar',
'HTTP_AUTHORIZATION' => 'basic foo:bar',
]);
}
#[DataProvider('createProvider')]
public function testCreateFromBaseUri(Stringable|string $baseUri, Stringable|string $uri, string $expected): void
{
$newUri = Uri::parse($uri, $baseUri);
self::assertInstanceOf(Uri::class, $newUri);
self::assertSame($expected, $newUri->toString());
}
public static function createProvider(): array
{
$base_uri = 'http://a/b/c/d;p?q';
return [
'base uri' => [$base_uri, '', $base_uri],
'scheme' => [$base_uri, 'http://d/e/f', 'http://d/e/f'],
'path 1' => [$base_uri, 'g', 'http://a/b/c/g'],
'path 2' => [$base_uri, './g', 'http://a/b/c/g'],
'path 3' => [$base_uri, 'g/', 'http://a/b/c/g/'],
'path 4' => [$base_uri, '/g', 'http://a/g'],
'authority' => [$base_uri, '//g', 'http://g'],
'query' => [$base_uri, '?y', 'http://a/b/c/d;p?y'],
'path + query' => [$base_uri, 'g?y', 'http://a/b/c/g?y'],
'fragment' => [$base_uri, '#s', 'http://a/b/c/d;p?q#s'],
'path + fragment' => [$base_uri, 'g#s', 'http://a/b/c/g#s'],
'path + query + fragment' => [$base_uri, 'g?y#s', 'http://a/b/c/g?y#s'],
'single dot 1' => [$base_uri, '.', 'http://a/b/c/'],
'single dot 2' => [$base_uri, './', 'http://a/b/c/'],
'single dot 3' => [$base_uri, './g/.', 'http://a/b/c/g/'],
'single dot 4' => [$base_uri, 'g/./h', 'http://a/b/c/g/h'],
'double dot 1' => [$base_uri, '..', 'http://a/b/'],
'double dot 2' => [$base_uri, '../', 'http://a/b/'],
'double dot 3' => [$base_uri, '../g', 'http://a/b/g'],
'double dot 4' => [$base_uri, '../..', 'http://a/'],
'double dot 5' => [$base_uri, '../../', 'http://a/'],
'double dot 6' => [$base_uri, '../../g', 'http://a/g'],
'double dot 7' => [$base_uri, '../../../g', 'http://a/g'],
'double dot 8' => [$base_uri, '../../../../g', 'http://a/g'],
'double dot 9' => [$base_uri, 'g/../h' , 'http://a/b/c/h'],
'mulitple slashes' => [$base_uri, 'foo////g', 'http://a/b/c/foo////g'],
'complex path 1' => [$base_uri, ';x', 'http://a/b/c/;x'],
'complex path 2' => [$base_uri, 'g;x', 'http://a/b/c/g;x'],
'complex path 3' => [$base_uri, 'g;x?y#s', 'http://a/b/c/g;x?y#s'],
'complex path 4' => [$base_uri, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'],
'complex path 5' => [$base_uri, 'g;x=1/../y', 'http://a/b/c/y'],
'dot segments presence 1' => [$base_uri, '/./g', 'http://a/g'],
'dot segments presence 2' => [$base_uri, '/../g', 'http://a/g'],
'dot segments presence 3' => [$base_uri, 'g.', 'http://a/b/c/g.'],
'dot segments presence 4' => [$base_uri, '.g', 'http://a/b/c/.g'],
'dot segments presence 5' => [$base_uri, 'g..', 'http://a/b/c/g..'],
'dot segments presence 6' => [$base_uri, '..g', 'http://a/b/c/..g'],
'origin uri without path' => ['http://h:b@a', 'b/../y', 'http://h:b@a/y'],
'uri without auhtority' => ['mailto:f@a.b', 'b@c.d?subject=baz', 'mailto:b@c.d?subject=baz'],
'with PSR7 base URI' => [new NyholmUri($base_uri), 'g.', 'http://a/b/c/g.'],
'with PSR7 ref URI' => [$base_uri, new NyholmUri('g.'), 'http://a/b/c/g.'],
'with PSR7 base URI and ref URI' => [new NyholmUri($base_uri), new NyholmUri('g.'), 'http://a/b/c/g.'],
];
}
public function testCreateThrowExceptionWithBaseUriNotAbsolute(): void
{
self::assertNull(Uri::parse('/path/to/you', '//example.com'));
}
#[TestWith(['data:text/plain;charset=us-ascii,'])]
#[TestWith(['/path/to/you'])]
public function testCreateWithUriWithoutAuthority(string $expected): void
{
$uri = Uri::parse($expected);
self::assertInstanceOf(Uri::class, $uri);
self::assertSame($expected, $uri->toString());
}
public function testCreateWithAbsoluteUriWithoutBaseUri(): void
{
$expected = 'scheme://host/sky?q#f';
$uri = Uri::parse('scheme://host/path/../sky?q#f');
self::assertInstanceOf(Uri::class, $uri);
self::assertNotSame($expected, $uri->toString());
self::assertSame($expected, $uri->normalize()->toString());
}
public function testCreateFromComponentsWithNullPath(): void
{
self::assertSame('', Uri::fromComponents(['path' => null])->toString());
}
public function testICanBeInstantiateFromRFC6750(): void
{
$template = 'https://example.com/hotels/{hotel}/bookings/{booking}';
$params = ['booking' => '42', 'hotel' => 'Rest & Relax'];
self::assertSame(
'/hotels/Rest%20%26%20Relax/bookings/42',
Uri::fromTemplate($template, $params)->getPath()
);
}
#[Test]
#[TestWith(['uri' => ' '], 'uri containing only whitespaces')]
#[TestWith(['uri' => ' https://a/b?c'], 'uri starting with whitespaces')]
#[TestWith(['uri' => 'https://a/b?c '], 'uri ending with whitespaces')]
public function it_fails_parsing_uri_string_with_whitespace(string $uri): void
{
$this->expectException(InvalidArgumentException::class);
Uri::new($uri);
}
#[Test]
#[DataProvider('provideInvalidUri')]
public function it_fails_to_parse_with_new(Stringable|string $uri): void
{
self::assertNull(Uri::tryNew($uri));
}
public static function provideInvalidUri(): iterable
{
yield 'invalid URI' => ['uri' => 'http://example.com/ '];
}
#[Test]
#[TestWith(['uri' => ':', 'baseUri' => null], 'invalid URI')]
#[TestWith(['uri' => '', 'baseUri' => '/absolute/path'], 'invalid resolution with a non absolute URI')]
public function it_fails_to_parse_with_parse(Stringable|string $uri, Stringable|string|null $baseUri): void
{
self::assertNull(Uri::parse($uri, $baseUri));
}
#[Test]
public function it_can_create_an_new_instance_with_space_using_from_base_uri_bugfix175(): void
{
self::assertSame(
'http://www.example.com/path%20with%20space/',
Uri::fromBaseUri('http://www.example.com/path with space/')->toString()
);
self::assertSame(
'http://www.example.com/path%20with%20space/',
Uri::fromBaseUri('/path with space/', 'http://www.example.com')->toString()
);
}
}