Skip to content

Commit eedda81

Browse files
committed
[FrameworkBundle] Integrate PsrHttpMessageBridge
1 parent 61023a7 commit eedda81

File tree

12 files changed

+202
-2
lines changed

12 files changed

+202
-2
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use Doctrine\DBAL\Connection;
1616
use Psr\Log\LogLevel;
1717
use Seld\JsonLint\JsonParser;
18+
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
19+
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
1820
use Symfony\Bundle\FullStack;
1921
use Symfony\Component\Asset\Package;
2022
use Symfony\Component\AssetMapper\AssetMapper;
@@ -192,6 +194,7 @@ public function getConfigTreeBuilder(): TreeBuilder
192194
$this->addHtmlSanitizerSection($rootNode, $enableIfStandalone);
193195
$this->addWebhookSection($rootNode, $enableIfStandalone);
194196
$this->addRemoteEventSection($rootNode, $enableIfStandalone);
197+
$this->addPsrHttpMessageBridgeSection($rootNode, $willBeAvailable);
195198

196199
return $treeBuilder;
197200
}
@@ -2582,4 +2585,24 @@ private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable
25822585
->end()
25832586
;
25842587
}
2588+
2589+
private function addPsrHttpMessageBridgeSection(ArrayNodeDefinition $rootNode, callable $willBeAvailable): void
2590+
{
2591+
$enableMode = 'canBeEnabled';
2592+
if (!class_exists(FullStack::class)
2593+
&& $willBeAvailable('symfony/psr-http-message-bridge', HttpFoundationFactoryInterface::class)
2594+
&& 0 === (new \ReflectionClass(PsrHttpFactory::class))->getConstructor()->getNumberOfRequiredParameters()
2595+
) {
2596+
$enableMode = 'canBeDisabled';
2597+
}
2598+
2599+
$rootNode
2600+
->children()
2601+
->arrayNode('psr_http_message_bridge')
2602+
->info('PSR HTTP message bridge configuration')
2603+
->{$enableMode}()
2604+
->end()
2605+
->end()
2606+
;
2607+
}
25852608
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use Psr\Http\Client\ClientInterface;
2626
use Psr\Log\LoggerAwareInterface;
2727
use Symfony\Bridge\Monolog\Processor\DebugProcessor;
28+
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
29+
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
2830
use Symfony\Bridge\Twig\Extension\CsrfExtension;
2931
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
3032
use Symfony\Bundle\FrameworkBundle\Routing\RouteLoaderInterface;
@@ -576,6 +578,17 @@ public function load(array $configs, ContainerBuilder $container)
576578
$this->registerHtmlSanitizerConfiguration($config['html_sanitizer'], $container, $loader);
577579
}
578580

581+
if ($this->readConfigEnabled('psr_http_message_bridge', $container, $config['psr_http_message_bridge'])) {
582+
if (!interface_exists(HttpFoundationFactoryInterface::class)) {
583+
throw new LogicException('PSR HTTP Message support cannot be enabled as the bridge is not installed. Try running "composer require symfony/psr-http-message-bridge".');
584+
}
585+
if ((new \ReflectionClass(PsrHttpFactory::class))->getConstructor()->getNumberOfRequiredParameters() > 0) {
586+
throw new LogicException('PSR HTTP Message support cannot be enabled for version 2 or earlier. Please update symfony/psr-http-message-bridge to 6.4 or wire all services manually.');
587+
}
588+
589+
$loader->load('psr_http_message_bridge.php');
590+
}
591+
579592
$this->addAnnotatedClassesToCompile([
580593
'**\\Controller\\',
581594
'**\\Entity\\',
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Psr\Http\Message\ResponseFactoryInterface;
15+
use Psr\Http\Message\ServerRequestFactoryInterface;
16+
use Psr\Http\Message\StreamFactoryInterface;
17+
use Psr\Http\Message\UploadedFileFactoryInterface;
18+
use Symfony\Bridge\PsrHttpMessage\ArgumentValueResolver\PsrServerRequestResolver;
19+
use Symfony\Bridge\PsrHttpMessage\EventListener\PsrResponseListener;
20+
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
21+
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
22+
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
23+
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
24+
25+
return static function (ContainerConfigurator $container) {
26+
$container->services()
27+
->set('psr_http_message_bridge.http_foundation_factory', HttpFoundationFactory::class)
28+
->alias(HttpFoundationFactoryInterface::class, 'psr_http_message_bridge.http_foundation_factory')
29+
30+
->set('psr_http_message_bridge.psr_http_factory', PsrHttpFactory::class)
31+
->args([
32+
service(ServerRequestFactoryInterface::class)->nullOnInvalid(),
33+
service(StreamFactoryInterface::class)->nullOnInvalid(),
34+
service(UploadedFileFactoryInterface::class)->nullOnInvalid(),
35+
service(ResponseFactoryInterface::class)->nullOnInvalid(),
36+
])
37+
->alias(HttpMessageFactoryInterface::class, 'psr_http_message_bridge.psr_http_factory')
38+
39+
->set('psr_http_message_bridge.psr_server_request_resolver', PsrServerRequestResolver::class)
40+
->args([service('psr_http_message_bridge.psr_http_factory')])
41+
->tag('controller.argument_value_resolver', ['priority' => -100])
42+
43+
->set('psr_http_message_bridge.psr_response_listener', PsrResponseListener::class)
44+
->args([
45+
service('psr_http_message_bridge.http_foundation_factory'),
46+
])
47+
->tag('kernel.event_subscriber')
48+
;
49+
};

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\DBAL\Connection;
1515
use PHPUnit\Framework\TestCase;
16+
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
1617
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration;
1718
use Symfony\Bundle\FullStack;
1819
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
@@ -46,7 +47,7 @@ public function testDefaultConfig()
4647
$this->assertEquals(self::getBundleDefaultConfig(), $config);
4748
}
4849

49-
public function getTestValidSessionName()
50+
public function getTestValidSessionName(): array
5051
{
5152
return [
5253
[null],
@@ -526,7 +527,7 @@ public function testEnabledLockNeedsResources()
526527
]);
527528
}
528529

529-
protected static function getBundleDefaultConfig()
530+
protected static function getBundleDefaultConfig(): array
530531
{
531532
return [
532533
'http_method_override' => false,
@@ -784,6 +785,9 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
784785
'remote-event' => [
785786
'enabled' => false,
786787
],
788+
'psr_http_message_bridge' => [
789+
'enabled' => !class_exists(FullStack::class) && 0 === (new \ReflectionClass(PsrHttpFactory::class))->getConstructor()->getNumberOfRequiredParameters(),
790+
],
787791
];
788792
}
789793
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller;
13+
14+
use Psr\Http\Message\ResponseFactoryInterface;
15+
use Psr\Http\Message\ResponseInterface;
16+
use Psr\Http\Message\ServerRequestInterface;
17+
use Psr\Http\Message\StreamFactoryInterface;
18+
19+
final class PsrHttpMessageController
20+
{
21+
public function __construct(
22+
private readonly ResponseFactoryInterface&StreamFactoryInterface $factory,
23+
) {
24+
}
25+
26+
public function __invoke(ServerRequestInterface $request): ResponseInterface
27+
{
28+
$responsePayload = json_encode([
29+
'message' => sprintf('Hello %s!', $request->getQueryParams()['name'] ?? 'World'),
30+
], \JSON_THROW_ON_ERROR);
31+
32+
return $this->factory->createResponse()
33+
->withHeader('Content-Type', 'application/json')
34+
->withBody($this->factory->createStream($responsePayload))
35+
;
36+
}
37+
}

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ http_client_call:
6565
path: /http_client_call
6666
defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\HttpClientController::index }
6767

68+
psr_http_message_bridge:
69+
path: /psr_http
70+
controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\PsrHttpMessageController
71+
6872
uid:
6973
resource: "../../Controller/UidController.php"
7074
type: "attribute"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
13+
14+
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
15+
16+
class PsrHttpMessageBridgeTest extends AbstractWebTestCase
17+
{
18+
public function testBridgeIntegration()
19+
{
20+
if ((new \ReflectionClass(PsrHttpFactory::class))->getConstructor()->getNumberOfRequiredParameters() > 0) {
21+
$this->expectException(\LogicException::class);
22+
$this->expectExceptionMessage('PSR HTTP Message support cannot be enabled for version 2 or earlier. Please update symfony/psr-http-message-bridge to 6.4 or wire all services manually.');
23+
}
24+
25+
$client = $this->createClient(['test_case' => 'PsrHttpMessageBridge', 'root_config' => 'config.yml', 'debug' => true]);
26+
$client->request('GET', '/psr_http?name=Symfony');
27+
28+
$this->assertResponseIsSuccessful();
29+
$this->assertResponseHeaderSame('content-type', 'application/json');
30+
$this->assertJsonStringEqualsJsonString('{"message":"Hello Symfony!"}', $client->getResponse()->getContent());
31+
}
32+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
13+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle;
14+
15+
return [
16+
new FrameworkBundle(),
17+
new TestBundle(),
18+
];
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
imports:
2+
- { resource: ../config/default.yml }
3+
- { resource: services.yml }
4+
5+
framework:
6+
http_method_override: false
7+
profiler: ~
8+
psr_http_message_bridge:
9+
enabled: true
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
_emailtest_bundle:
2+
resource: '@TestBundle/Resources/config/routing.yml'

0 commit comments

Comments
 (0)