Skip to content

Commit e872db1

Browse files
committed
[PsrHttpMessageBridge] Support php-http/discovery for auto-detecting PSR-17 factories
1 parent 139419d commit e872db1

File tree

4 files changed

+63
-29
lines changed

4 files changed

+63
-29
lines changed

src/Symfony/Bridge/PsrHttpMessage/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Import the bridge into the Symfony monorepo and synchronize releases
88
* Remove `ArgumentValueResolverInterface` from `PsrServerRequestResolver`
9+
* Support `php-http/discovery` for auto-detecting PSR-17 factories
910

1011
2.3.1
1112
-----

src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Bridge\PsrHttpMessage\Factory;
1313

14+
use Http\Discovery\Psr17Factory as DiscoveryPsr17Factory;
15+
use Nyholm\Psr7\Factory\Psr17Factory as NyholmPsr17Factory;
1416
use Psr\Http\Message\ResponseFactoryInterface;
1517
use Psr\Http\Message\ResponseInterface;
1618
use Psr\Http\Message\ServerRequestFactoryInterface;
@@ -33,12 +35,34 @@
3335
*/
3436
class PsrHttpFactory implements HttpMessageFactoryInterface
3537
{
38+
private readonly ServerRequestFactoryInterface $serverRequestFactory;
39+
private readonly StreamFactoryInterface $streamFactory;
40+
private readonly UploadedFileFactoryInterface $uploadedFileFactory;
41+
private readonly ResponseFactoryInterface $responseFactory;
42+
3643
public function __construct(
37-
private readonly ServerRequestFactoryInterface $serverRequestFactory,
38-
private readonly StreamFactoryInterface $streamFactory,
39-
private readonly UploadedFileFactoryInterface $uploadedFileFactory,
40-
private readonly ResponseFactoryInterface $responseFactory,
44+
ServerRequestFactoryInterface $serverRequestFactory = null,
45+
StreamFactoryInterface $streamFactory = null,
46+
UploadedFileFactoryInterface $uploadedFileFactory = null,
47+
ResponseFactoryInterface $responseFactory = null,
4148
) {
49+
if (null === $serverRequestFactory || null === $streamFactory || null === $uploadedFileFactory || null === $responseFactory) {
50+
$psr17Factory = match (true) {
51+
class_exists(DiscoveryPsr17Factory::class) => new DiscoveryPsr17Factory(),
52+
class_exists(NyholmPsr17Factory::class) => new NyholmPsr17Factory(),
53+
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)),
54+
};
55+
56+
$serverRequestFactory ??= $psr17Factory;
57+
$streamFactory ??= $psr17Factory;
58+
$uploadedFileFactory ??= $psr17Factory;
59+
$responseFactory ??= $psr17Factory;
60+
}
61+
62+
$this->serverRequestFactory = $serverRequestFactory;
63+
$this->streamFactory = $streamFactory;
64+
$this->uploadedFileFactory = $uploadedFileFactory;
65+
$this->responseFactory = $responseFactory;
4266
}
4367

4468
public function createRequest(Request $symfonyRequest): ServerRequestInterface

src/Symfony/Bridge/PsrHttpMessage/Tests/Factory/PsrHttpFactoryTest.php

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use Nyholm\Psr7\Factory\Psr17Factory;
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
17-
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
1817
use Symfony\Component\HttpFoundation\BinaryFileResponse;
1918
use Symfony\Component\HttpFoundation\Cookie;
2019
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -29,23 +28,17 @@
2928
*/
3029
class PsrHttpFactoryTest extends TestCase
3130
{
32-
private HttpMessageFactoryInterface $factory;
3331
private string $tmpDir;
3432

35-
protected function buildHttpMessageFactory(): HttpMessageFactoryInterface
36-
{
37-
$factory = new Psr17Factory();
38-
39-
return new PsrHttpFactory($factory, $factory, $factory, $factory);
40-
}
41-
4233
protected function setUp(): void
4334
{
44-
$this->factory = $this->buildHttpMessageFactory();
4535
$this->tmpDir = sys_get_temp_dir();
4636
}
4737

48-
public function testCreateRequest()
38+
/**
39+
* @dataProvider provideFactories
40+
*/
41+
public function testCreateRequest(PsrHttpFactory $factory)
4942
{
5043
$stdClass = new \stdClass();
5144
$request = new Request(
@@ -83,7 +76,7 @@ public function testCreateRequest()
8376
);
8477
$request->headers->set(' X-Broken', 'abc');
8578

86-
$psrRequest = $this->factory->createRequest($request);
79+
$psrRequest = $factory->createRequest($request);
8780

8881
$this->assertSame('Content', $psrRequest->getBody()->__toString());
8982

@@ -130,7 +123,7 @@ public function testGetContentCanBeCalledAfterRequestCreation()
130123
$header = ['HTTP_HOST' => 'dunglas.fr'];
131124
$request = new Request([], [], [], [], [], $header, 'Content');
132125

133-
$psrRequest = $this->factory->createRequest($request);
126+
$psrRequest = self::buildHttpMessageFactory()->createRequest($request);
134127

135128
$this->assertSame('Content', $psrRequest->getBody()->__toString());
136129
$this->assertSame('Content', $request->getContent());
@@ -144,7 +137,10 @@ private function createUploadedFile(string $content, string $originalName, strin
144137
return new UploadedFile($path, $originalName, $mimeType, $error, true);
145138
}
146139

147-
public function testCreateResponse()
140+
/**
141+
* @dataProvider provideFactories
142+
*/
143+
public function testCreateResponse(PsrHttpFactory $factory)
148144
{
149145
$response = new Response(
150146
'Response content.',
@@ -156,7 +152,7 @@ public function testCreateResponse()
156152
);
157153
$response->headers->setCookie(new Cookie('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT'), '/', null, false, true, false, 'lax'));
158154

159-
$psrResponse = $this->factory->createResponse($response);
155+
$psrResponse = $factory->createResponse($response);
160156
$this->assertSame('Response content.', $psrResponse->getBody()->__toString());
161157
$this->assertSame(202, $psrResponse->getStatusCode());
162158
$this->assertSame(['3.4'], $psrResponse->getHeader('x-symfony'));
@@ -179,7 +175,7 @@ public function testCreateResponseFromStreamed()
179175
flush();
180176
});
181177

182-
$psrResponse = $this->factory->createResponse($response);
178+
$psrResponse = self::buildHttpMessageFactory()->createResponse($response);
183179

184180
$this->assertSame("Line 1\nLine 2\n", $psrResponse->getBody()->__toString());
185181
}
@@ -191,7 +187,7 @@ public function testCreateResponseFromBinaryFile()
191187

192188
$response = new BinaryFileResponse($path);
193189

194-
$psrResponse = $this->factory->createResponse($response);
190+
$psrResponse = self::buildHttpMessageFactory()->createResponse($response);
195191

196192
$this->assertSame('Binary', $psrResponse->getBody()->__toString());
197193
}
@@ -207,7 +203,7 @@ public function testCreateResponseFromBinaryFileWithRange()
207203
$response = new BinaryFileResponse($path, 200, ['Content-Type' => 'plain/text']);
208204
$response->prepare($request);
209205

210-
$psrResponse = $this->factory->createResponse($response);
206+
$psrResponse = self::buildHttpMessageFactory()->createResponse($response);
211207

212208
$this->assertSame('inar', $psrResponse->getBody()->__toString());
213209
$this->assertSame('bytes 1-4/6', $psrResponse->getHeaderLine('Content-Range'));
@@ -237,7 +233,7 @@ public function testUploadErrNoFile()
237233
'Content'
238234
);
239235

240-
$psrRequest = $this->factory->createRequest($request);
236+
$psrRequest = self::buildHttpMessageFactory()->createRequest($request);
241237

242238
$uploadedFiles = $psrRequest->getUploadedFiles();
243239

@@ -256,7 +252,7 @@ public function testJsonContent()
256252
'CONTENT_TYPE' => 'application/json',
257253
];
258254
$request = new Request([], [], [], [], [], $headers, '{"city":"Paris","country":"France"}');
259-
$psrRequest = $this->factory->createRequest($request);
255+
$psrRequest = self::buildHttpMessageFactory()->createRequest($request);
260256

261257
$this->assertSame(['city' => 'Paris', 'country' => 'France'], $psrRequest->getParsedBody());
262258
}
@@ -272,7 +268,7 @@ public function testEmptyJsonContent()
272268
'CONTENT_TYPE' => 'application/json',
273269
];
274270
$request = new Request([], [], [], [], [], $headers, '{}');
275-
$psrRequest = $this->factory->createRequest($request);
271+
$psrRequest = self::buildHttpMessageFactory()->createRequest($request);
276272

277273
$this->assertSame([], $psrRequest->getParsedBody());
278274
}
@@ -288,8 +284,22 @@ public function testWrongJsonContent()
288284
'CONTENT_TYPE' => 'application/json',
289285
];
290286
$request = new Request([], [], [], [], [], $headers, '{"city":"Paris"');
291-
$psrRequest = $this->factory->createRequest($request);
287+
$psrRequest = self::buildHttpMessageFactory()->createRequest($request);
292288

293289
$this->assertNull($psrRequest->getParsedBody());
294290
}
291+
292+
public static function provideFactories(): \Generator
293+
{
294+
yield 'Discovery' => [new PsrHttpFactory()];
295+
yield 'incomplete dependencies' => [new PsrHttpFactory(responseFactory: new Psr17Factory())];
296+
yield 'Nyholm' => [self::buildHttpMessageFactory()];
297+
}
298+
299+
private static function buildHttpMessageFactory(): PsrHttpFactory
300+
{
301+
$factory = new Psr17Factory();
302+
303+
return new PsrHttpFactory($factory, $factory, $factory, $factory);
304+
}
295305
}

src/Symfony/Bridge/PsrHttpMessage/composer.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,13 @@
2727
"symfony/framework-bundle": "^6.2|^7.0",
2828
"symfony/http-kernel": "^6.2|^7.0",
2929
"nyholm/psr7": "^1.1",
30+
"php-http/discovery": "^1.15",
3031
"psr/log": "^1.1.4|^2|^3"
3132
},
3233
"conflict": {
34+
"php-http/discovery": "<1.15",
3335
"symfony/http-kernel": "<6.2"
3436
},
35-
"suggest": {
36-
"nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
37-
},
3837
"autoload": {
3938
"psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" },
4039
"exclude-from-classmap": [

0 commit comments

Comments
 (0)
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