From 47b4992cba94a03f3ce17e815499161aa116d1a8 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 31 Jul 2023 18:07:34 +0200 Subject: [PATCH] [PsrHttpMessageBridge] Support `php-http/discovery` for auto-detecting PSR-17 factories --- .../Bridge/PsrHttpMessage/CHANGELOG.md | 1 + .../PsrHttpMessage/Factory/PsrHttpFactory.php | 32 +++++++++-- .../Tests/Factory/PsrHttpFactoryTest.php | 54 +++++++++++-------- .../Bridge/PsrHttpMessage/composer.json | 8 ++- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Bridge/PsrHttpMessage/CHANGELOG.md b/src/Symfony/Bridge/PsrHttpMessage/CHANGELOG.md index 392833d4ccb95..b53760aa67624 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/CHANGELOG.md +++ b/src/Symfony/Bridge/PsrHttpMessage/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Import the bridge into the Symfony monorepo and synchronize releases * Remove `ArgumentValueResolverInterface` from `PsrServerRequestResolver` + * Support `php-http/discovery` for auto-detecting PSR-17 factories 2.3.1 ----- diff --git a/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php b/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php index 5e378d18d90c5..bf245602ab16f 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php +++ b/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php @@ -11,6 +11,8 @@ namespace Symfony\Bridge\PsrHttpMessage\Factory; +use Http\Discovery\Psr17Factory as DiscoveryPsr17Factory; +use Nyholm\Psr7\Factory\Psr17Factory as NyholmPsr17Factory; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestFactoryInterface; @@ -33,12 +35,34 @@ */ class PsrHttpFactory implements HttpMessageFactoryInterface { + private readonly ServerRequestFactoryInterface $serverRequestFactory; + private readonly StreamFactoryInterface $streamFactory; + private readonly UploadedFileFactoryInterface $uploadedFileFactory; + private readonly ResponseFactoryInterface $responseFactory; + public function __construct( - private readonly ServerRequestFactoryInterface $serverRequestFactory, - private readonly StreamFactoryInterface $streamFactory, - private readonly UploadedFileFactoryInterface $uploadedFileFactory, - private readonly ResponseFactoryInterface $responseFactory, + ServerRequestFactoryInterface $serverRequestFactory = null, + StreamFactoryInterface $streamFactory = null, + UploadedFileFactoryInterface $uploadedFileFactory = null, + ResponseFactoryInterface $responseFactory = null, ) { + if (null === $serverRequestFactory || null === $streamFactory || null === $uploadedFileFactory || null === $responseFactory) { + $psr17Factory = match (true) { + class_exists(DiscoveryPsr17Factory::class) => new DiscoveryPsr17Factory(), + class_exists(NyholmPsr17Factory::class) => new NyholmPsr17Factory(), + default => throw new \LogicException(sprintf('You cannot use the "%s" as no PSR-17 factories have been provided. Try running "composer require php-http/discovery psr/http-factory-implementation:*".', self::class)), + }; + + $serverRequestFactory ??= $psr17Factory; + $streamFactory ??= $psr17Factory; + $uploadedFileFactory ??= $psr17Factory; + $responseFactory ??= $psr17Factory; + } + + $this->serverRequestFactory = $serverRequestFactory; + $this->streamFactory = $streamFactory; + $this->uploadedFileFactory = $uploadedFileFactory; + $this->responseFactory = $responseFactory; } public function createRequest(Request $symfonyRequest): ServerRequestInterface diff --git a/src/Symfony/Bridge/PsrHttpMessage/Tests/Factory/PsrHttpFactoryTest.php b/src/Symfony/Bridge/PsrHttpMessage/Tests/Factory/PsrHttpFactoryTest.php index 238b17b7810b6..b0976cb468678 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/Tests/Factory/PsrHttpFactoryTest.php +++ b/src/Symfony/Bridge/PsrHttpMessage/Tests/Factory/PsrHttpFactoryTest.php @@ -14,7 +14,6 @@ use Nyholm\Psr7\Factory\Psr17Factory; use PHPUnit\Framework\TestCase; use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; -use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\File\UploadedFile; @@ -29,23 +28,17 @@ */ class PsrHttpFactoryTest extends TestCase { - private HttpMessageFactoryInterface $factory; private string $tmpDir; - protected function buildHttpMessageFactory(): HttpMessageFactoryInterface - { - $factory = new Psr17Factory(); - - return new PsrHttpFactory($factory, $factory, $factory, $factory); - } - protected function setUp(): void { - $this->factory = $this->buildHttpMessageFactory(); $this->tmpDir = sys_get_temp_dir(); } - public function testCreateRequest() + /** + * @dataProvider provideFactories + */ + public function testCreateRequest(PsrHttpFactory $factory) { $stdClass = new \stdClass(); $request = new Request( @@ -83,7 +76,7 @@ public function testCreateRequest() ); $request->headers->set(' X-Broken', 'abc'); - $psrRequest = $this->factory->createRequest($request); + $psrRequest = $factory->createRequest($request); $this->assertSame('Content', $psrRequest->getBody()->__toString()); @@ -130,7 +123,7 @@ public function testGetContentCanBeCalledAfterRequestCreation() $header = ['HTTP_HOST' => 'dunglas.fr']; $request = new Request([], [], [], [], [], $header, 'Content'); - $psrRequest = $this->factory->createRequest($request); + $psrRequest = self::buildHttpMessageFactory()->createRequest($request); $this->assertSame('Content', $psrRequest->getBody()->__toString()); $this->assertSame('Content', $request->getContent()); @@ -144,7 +137,10 @@ private function createUploadedFile(string $content, string $originalName, strin return new UploadedFile($path, $originalName, $mimeType, $error, true); } - public function testCreateResponse() + /** + * @dataProvider provideFactories + */ + public function testCreateResponse(PsrHttpFactory $factory) { $response = new Response( 'Response content.', @@ -156,7 +152,7 @@ public function testCreateResponse() ); $response->headers->setCookie(new Cookie('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT'), '/', null, false, true, false, 'lax')); - $psrResponse = $this->factory->createResponse($response); + $psrResponse = $factory->createResponse($response); $this->assertSame('Response content.', $psrResponse->getBody()->__toString()); $this->assertSame(202, $psrResponse->getStatusCode()); $this->assertSame(['3.4'], $psrResponse->getHeader('x-symfony')); @@ -179,7 +175,7 @@ public function testCreateResponseFromStreamed() flush(); }); - $psrResponse = $this->factory->createResponse($response); + $psrResponse = self::buildHttpMessageFactory()->createResponse($response); $this->assertSame("Line 1\nLine 2\n", $psrResponse->getBody()->__toString()); } @@ -191,7 +187,7 @@ public function testCreateResponseFromBinaryFile() $response = new BinaryFileResponse($path); - $psrResponse = $this->factory->createResponse($response); + $psrResponse = self::buildHttpMessageFactory()->createResponse($response); $this->assertSame('Binary', $psrResponse->getBody()->__toString()); } @@ -207,7 +203,7 @@ public function testCreateResponseFromBinaryFileWithRange() $response = new BinaryFileResponse($path, 200, ['Content-Type' => 'plain/text']); $response->prepare($request); - $psrResponse = $this->factory->createResponse($response); + $psrResponse = self::buildHttpMessageFactory()->createResponse($response); $this->assertSame('inar', $psrResponse->getBody()->__toString()); $this->assertSame('bytes 1-4/6', $psrResponse->getHeaderLine('Content-Range')); @@ -237,7 +233,7 @@ public function testUploadErrNoFile() 'Content' ); - $psrRequest = $this->factory->createRequest($request); + $psrRequest = self::buildHttpMessageFactory()->createRequest($request); $uploadedFiles = $psrRequest->getUploadedFiles(); @@ -256,7 +252,7 @@ public function testJsonContent() 'CONTENT_TYPE' => 'application/json', ]; $request = new Request([], [], [], [], [], $headers, '{"city":"Paris","country":"France"}'); - $psrRequest = $this->factory->createRequest($request); + $psrRequest = self::buildHttpMessageFactory()->createRequest($request); $this->assertSame(['city' => 'Paris', 'country' => 'France'], $psrRequest->getParsedBody()); } @@ -272,7 +268,7 @@ public function testEmptyJsonContent() 'CONTENT_TYPE' => 'application/json', ]; $request = new Request([], [], [], [], [], $headers, '{}'); - $psrRequest = $this->factory->createRequest($request); + $psrRequest = self::buildHttpMessageFactory()->createRequest($request); $this->assertSame([], $psrRequest->getParsedBody()); } @@ -288,8 +284,22 @@ public function testWrongJsonContent() 'CONTENT_TYPE' => 'application/json', ]; $request = new Request([], [], [], [], [], $headers, '{"city":"Paris"'); - $psrRequest = $this->factory->createRequest($request); + $psrRequest = self::buildHttpMessageFactory()->createRequest($request); $this->assertNull($psrRequest->getParsedBody()); } + + public static function provideFactories(): \Generator + { + yield 'Discovery' => [new PsrHttpFactory()]; + yield 'incomplete dependencies' => [new PsrHttpFactory(responseFactory: new Psr17Factory())]; + yield 'Nyholm' => [self::buildHttpMessageFactory()]; + } + + private static function buildHttpMessageFactory(): PsrHttpFactory + { + $factory = new Psr17Factory(); + + return new PsrHttpFactory($factory, $factory, $factory, $factory); + } } diff --git a/src/Symfony/Bridge/PsrHttpMessage/composer.json b/src/Symfony/Bridge/PsrHttpMessage/composer.json index 612de1228a1e0..9169a4b1ee9f9 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/composer.json +++ b/src/Symfony/Bridge/PsrHttpMessage/composer.json @@ -27,13 +27,17 @@ "symfony/framework-bundle": "^6.2|^7.0", "symfony/http-kernel": "^6.2|^7.0", "nyholm/psr7": "^1.1", + "php-http/discovery": "^1.15", "psr/log": "^1.1.4|^2|^3" }, "conflict": { + "php-http/discovery": "<1.15", "symfony/http-kernel": "<6.2" }, - "suggest": { - "nyholm/psr7": "For a super lightweight PSR-7/17 implementation" + "config": { + "allow-plugins": { + "php-http/discovery": false + } }, "autoload": { "psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" }, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy