diff --git a/Neos.Flow/Classes/Http/BaseUriProvider.php b/Neos.Flow/Classes/Http/BaseUriProvider.php index 22ea717d62..ca3cf35b4e 100644 --- a/Neos.Flow/Classes/Http/BaseUriProvider.php +++ b/Neos.Flow/Classes/Http/BaseUriProvider.php @@ -32,7 +32,7 @@ class BaseUriProvider /** * Get the configured framework base URI. * - * @return Uri|null + * @return UriInterface|null */ private function getConfiguredBaseUri(): ?UriInterface { diff --git a/Neos.Flow/Classes/Http/UriHelper.php b/Neos.Flow/Classes/Http/UriHelper.php new file mode 100644 index 0000000000..fc73e8872e --- /dev/null +++ b/Neos.Flow/Classes/Http/UriHelper.php @@ -0,0 +1,37 @@ +getQuery() === '') { + $mergedQuery = $queryParameters; + } else { + $queryParametersFromUri = []; + parse_str($uri->getQuery(), $queryParametersFromUri); + $mergedQuery = Arrays::arrayMergeRecursiveOverrule($queryParametersFromUri, $queryParameters); + } + return $uri->withQuery(http_build_query($mergedQuery, '', '&')); + } +} diff --git a/Neos.Flow/Tests/Unit/Http/UriHelperTest.php b/Neos.Flow/Tests/Unit/Http/UriHelperTest.php new file mode 100644 index 0000000000..e68800cf13 --- /dev/null +++ b/Neos.Flow/Tests/Unit/Http/UriHelperTest.php @@ -0,0 +1,54 @@ + 123]) + ); + } + + /** @test */ + public function nestedQueryParametersAreMergedCorrectly() + { + self::assertEquals( + new Uri('http://localhost/index?param1=foo¶m2[a]=bar¶m2[b]=huhu¶m3=123'), + UriHelper::withAdditionalQueryParameters( + new Uri('http://localhost/index?param1=foo¶m2[a]=bar'), + [ + 'param2' => [ + 'b' => 'huhu' + ], + 'param3' => 123, + ] + ) + ); + } +} diff --git a/Neos.Flow/Tests/Unit/Http/UriTest.php b/Neos.Flow/Tests/Unit/Http/UriTest.php deleted file mode 100644 index 9e2031c11a..0000000000 --- a/Neos.Flow/Tests/Unit/Http/UriTest.php +++ /dev/null @@ -1,191 +0,0 @@ -getScheme() === 'http' && - $uri->getUserInfo() === 'username:password' && - $uri->getHost() === 'subdomain.domain.com' && - $uri->getPort() === 8080 && - $uri->getPath() === '/path1/path2/index.php' && - urldecode($uri->getQuery()) === 'argument1=value1&argument2=value2&argument3[subargument1]=subvalue1' && - $uri->getFragment() === 'anchor' - ); - - self::assertTrue($check, 'The valid and complete URI has not been correctly transformed to an URI object'); - } - - /** - * Uri strings - */ - public function uriStrings() - { - return [ - ['http://flow.neos.io/x'], - ['http://flow.neos.io/foo/bar?baz=1&quux=true'], - ['https://robert@localhost/arabica/coffee.html'], - ['http://127.0.0.1/bar.baz.com/foo.js'], - ['http://localhost:8080?foo=bar'], - ['http://localhost:443#hashme!x'], - ['http://[3b00:f59:1008::212:183:20]'], - ['http://[3b00:f59:1008::212:183:20]:443#hashme!x'], - ]; - } - - /** - * Checks round trips for various URIs - * - * @dataProvider uriStrings - * @test - */ - public function urisCanBeConvertedForthAndBackWithoutLoss(string $uriString) - { - $uri = new Uri($uriString); - self::assertSame($uriString, (string)$uri); - } - - /** - * Checks round trips for various URIs - * - * @test - */ - public function settingSchemeAndHostOnUriDoesNotConfuseToString() - { - $uri = new Uri('/no/scheme/or/host'); - $uri = $uri->withScheme('http') - ->withHost('localhost'); - self::assertSame('http://localhost/no/scheme/or/host', (string)$uri); - } - - /** - * @test - */ - public function toStringOmitsStandardPorts() - { - $uri = new Uri('http://flow.neos.io'); - self::assertSame('http://flow.neos.io', (string)$uri); - self::assertNull($uri->getPort()); - - $uri = new Uri('https://flow.neos.io'); - self::assertSame('https://flow.neos.io', (string)$uri); - self::assertNull($uri->getPort()); - } - - /** - * @test - */ - public function constructorParsesArgumentsWithSpecialCharactersCorrectly() - { - $uriString = 'http://www.neos.io/path1/?argumentäöü1=' . urlencode('valueåø€œ'); - $uri = new Uri($uriString); - - $check = ( - $uri->getScheme() === 'http' && - $uri->getHost() === 'www.neos.io' && - $uri->getPath() === '/path1/' && - $uri->getQuery() === 'argument%C3%A4%C3%B6%C3%BC1=value%C3%A5%C3%B8%E2%82%AC%C5%93' - ); - self::assertTrue($check, 'The URI with special arguments has not been correctly transformed to an URI object'); - } - - /** - * URIs for testing host parsing - */ - public function hostTestUris() - { - return [ - ['http://www.neos.io/about/project', 'www.neos.io'], - ['http://flow.neos.io/foo', 'flow.neos.io'], - ['http://[3b00:f59:1008::212:183:20]', '[3b00:f59:1008::212:183:20]'], - ]; - } - - /** - * @dataProvider hostTestUris - * @test - */ - public function constructorParsesHostCorrectly(string $uriString, string $expectedHost) - { - $uri = new Uri($uriString); - self::assertSame($expectedHost, $uri->getHost()); - } - - /** - * @dataProvider hostTestUris - * @test - */ - public function settingValidHostPassesRegexCheck(string $uriString, string $plainHost) - { - $uri = (new Uri(''))->withHost($plainHost); - self::assertEquals($plainHost, $uri->getHost()); - } - - public function uriStringTestUris() - { - return [ - ['http://username:password@subdomain.domain.com:1234/pathx1/pathx2/index.php?argument1=value1&argument2=value2&argument3%5Bsubargument1%5D=subvalue1#anchorman'], - ['http://username:password@[2a00:f48:1008::212:183:10]:1234/pathx1/pathx2/index.php?argument1=value1&argument2=value2&argument3%5Bsubargument1%5D=subvalue1#anchorman'], - ]; - } - /** - * Checks if a complete URI with all parts is transformed into an object correctly. - * - * @test - * @dataProvider uriStringTestUris - */ - public function stringRepresentationIsCorrect(string $uriString) - { - $uri = new Uri($uriString); - self::assertEquals($uriString, (string)$uri, 'The string representation of the URI is not equal to the original URI string.'); - } - - /** - * @test - */ - public function constructingWithNotAStringThrowsException() - { - $error = null; - try { - new Uri(['foo']); - } catch (\Throwable $error) { - } - $this->assertNotEmpty($error); - } - - /** - * @test - */ - public function unparsableUriStringThrowsException() - { - $this->expectException(\InvalidArgumentException::class); - new Uri('http:////localhost'); - } -} diff --git a/Neos.Http.Factories/Classes/PsrHttpFactory.php b/Neos.Http.Factories/Classes/PsrHttpFactory.php index 27f0ecd68f..25f3a2a40c 100644 --- a/Neos.Http.Factories/Classes/PsrHttpFactory.php +++ b/Neos.Http.Factories/Classes/PsrHttpFactory.php @@ -10,6 +10,11 @@ use Psr\Http\Message\UploadedFileFactoryInterface; use Psr\Http\Message\UriFactoryInterface; +/** + * A factory that implements all interfaces of PSR 17 + * + * This factory can be used to simply create Requests, Uris and Streams without having to inject the traits yourself. + */ class PsrHttpFactory implements ServerRequestFactoryInterface, RequestFactoryInterface, ResponseFactoryInterface, UriFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface { diff --git a/Neos.Http.Factories/Classes/UriFactoryTrait.php b/Neos.Http.Factories/Classes/UriFactoryTrait.php index 6144747458..57eb3e9adf 100644 --- a/Neos.Http.Factories/Classes/UriFactoryTrait.php +++ b/Neos.Http.Factories/Classes/UriFactoryTrait.php @@ -4,7 +4,6 @@ namespace Neos\Http\Factories; use GuzzleHttp\Psr7\Uri; -use Psr\Http\Message\UriFactoryInterface; use Psr\Http\Message\UriInterface; /**