Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ matrix:
- php: 7.4
env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" XDEBUG_MODE=coverage
- php: 8.0
- php: 8.0
env: VENDORS=minimal

# Latest commit to master
- php: 7.4
Expand All @@ -38,7 +40,7 @@ matrix:
before_install:
- if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi
- if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; fi;
- if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi;
- if ! [ $DEPENDENCIES == "minimal" ]; then composer remove --dev --no-update nyholm/psr7 laminas/laminas-diactoros; fi;

install:
# To be removed when this issue will be resolved: https://github.com/composer/composer/issues/5355
Expand Down
64 changes: 64 additions & 0 deletions Bundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PsrHttpMessage\Bundle\DependencyInjection;

use Laminas\Diactoros\RequestFactory;
use Laminas\Diactoros\ServerRequestFactory;
use Nyholm\Psr7\Factory\Psr17Factory;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
* @author Alexander M. Turek <me@derrabus.de>
*/
final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('psr_http_message');
$rootNode = $treeBuilder->getRootNode();

$rootNode->children()
->arrayNode('message_factories')
->{class_exists(Psr17Factory::class) || class_exists(ServerRequestFactory::class) ? 'canBeDisabled' : 'canBeEnabled'}()
->children()
->enumNode('implementation')
->info('The PSR-7 implementation to use. By default, the bundle will configure the Nyholm implementation if it\'s available.')
->values(['nyholm', 'diactoros'])
->defaultValue(class_exists(Psr17Factory::class) || !class_exists(ServerRequestFactory::class) ? 'nyholm' : 'diactoros')
->validate()
->ifTrue(function ($value) {
return 'nyholm' === $value && !class_exists(Psr17Factory::class);
})->thenInvalid('Cannot configure Nyholm\'s PSR-7 implementation. Try running "composer require nyholm/psr7".')
->end()
->validate()
->ifTrue(function ($value) {
return 'diactoros' === $value && !class_exists(RequestFactory::class);
})->thenInvalid('Cannot configure Diactoros. Try running "composer require laminas/laminas-diactoros".')
->end()
->end()
->end()
->end()
->arrayNode('message_converters')
->info('Converters that enable controllers to operate on PSR-7 messages')
->canBeEnabled()
->end()
->integerNode('response_buffer')
->info('The maximum output buffering size for each iteration when sending the response')
->min(1)
->defaultValue(16372)
->end()
;

return $treeBuilder;
}
}
52 changes: 52 additions & 0 deletions Bundle/DependencyInjection/PsrHttpMessageExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PsrHttpMessage\Bundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;

/**
* @author Alexander M. Turek <me@derrabus.de>
*/
final class PsrHttpMessageExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config'));
$loader->load('http_foundation.php');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if I'm reading things correctly, out of the box (with no config), the bundle would just give you a service for HttpFoundationFactory, correct? That seems to make sense :)


$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);

$container->setParameter('psr_http_message.response_buffer', $config['response_buffer']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a parameter? Or could we pass it directly into the psr_http_message.http_foundation_factory service?


if ($config['message_converters']['enabled']) {
$loader->load('message_converters.php');
}

switch ($config['message_factories']['enabled'] ? $config['message_factories']['implementation'] : 'disabled') {
case 'nyholm':
$loader->load('nyholm.php');
$loader->load('psr_factories.php');
break;
case 'diactoros':
$loader->load('diactoros.php');
$loader->load('psr_factories.php');
break;
default:
$container->removeDefinition('psr_http_message.server_request_resolver');
break;
}
}
}
21 changes: 21 additions & 0 deletions Bundle/PsrHttpMessageBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PsrHttpMessage\Bundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

/**
* @author Alexander M. Turek <me@derrabus.de>
*/
final class PsrHttpMessageBundle extends Bundle
{
}
37 changes: 37 additions & 0 deletions Bundle/Resources/config/diactoros.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Laminas\Diactoros\RequestFactory;
use Laminas\Diactoros\ResponseFactory;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\Diactoros\StreamFactory;
use Laminas\Diactoros\UploadedFileFactory;
use Laminas\Diactoros\UriFactory;

return static function (ContainerConfigurator $container) {
$container->services()
->set('psr_http_message.diactoros.request_factory', RequestFactory::class)
->set('psr_http_message.diactoros.response_factory', ResponseFactory::class)
->set('psr_http_message.diactoros.server_request_factory', ServerRequestFactory::class)
->set('psr_http_message.diactoros.stream_factory', StreamFactory::class)
->set('psr_http_message.diactoros.uploaded_file_factory', UploadedFileFactory::class)
->set('psr_http_message.diactoros.uri_factory', UriFactory::class)

->alias('psr_http_message.psr.request_factory', 'psr_http_message.diactoros.request_factory')
->alias('psr_http_message.psr.response_factory', 'psr_http_message.diactoros.response_factory')
->alias('psr_http_message.psr.server_request_factory', 'psr_http_message.diactoros.server_request_factory')
->alias('psr_http_message.psr.stream_factory', 'psr_http_message.diactoros.stream_factory')
->alias('psr_http_message.psr.uploaded_file_factory', 'psr_http_message.diactoros.uploaded_file_factory')
->alias('psr_http_message.psr.uri_factory', 'psr_http_message.diactoros.uri_factory')
;
};
24 changes: 24 additions & 0 deletions Bundle/Resources/config/http_foundation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;

return static function (ContainerConfigurator $container) {
$container->services()
->set('psr_http_message.http_foundation_factory', HttpFoundationFactory::class)
->args(['%psr_http_message.response_buffer%'])

->alias(HttpFoundationFactoryInterface::class, 'psr_http_message.http_foundation_factory')
;
};
28 changes: 28 additions & 0 deletions Bundle/Resources/config/message_converters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Bridge\PsrHttpMessage\ArgumentValueResolver\PsrServerRequestResolver;
use Symfony\Bridge\PsrHttpMessage\EventListener\PsrResponseListener;
use Symfony\Component\DependencyInjection\Reference;

return static function (ContainerConfigurator $container) {
$container->services()
->set('psr_http_message.response_listener', PsrResponseListener::class)
->args([new Reference('psr_http_message.http_foundation_factory')])
->tag('kernel.event_subscriber')

->set('psr_http_message.server_request_resolver', PsrServerRequestResolver::class)
->args([new Reference('psr_http_message.psr_http_factory')])
->tag('controller.argument_value_resolver')
;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to enable these automatically (and then maybe allow them to be DISABALED)? I'm trying to avoid users almost ever needing the config file. The first could always be enabled (the service is available, is there a downside)? The second could be enabled if/once psr_http_message.psr_http_factory is present. I'm trying to give the user more, with less config. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downside is runtime overhead on the hotpath yes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's not do that then ;)

};
27 changes: 27 additions & 0 deletions Bundle/Resources/config/nyholm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Nyholm\Psr7\Factory\Psr17Factory;

return static function (ContainerConfigurator $container) {
$container->services()
->set('psr_http_message.nyholm.factory', Psr17Factory::class)

->alias('psr_http_message.psr.request_factory', 'psr_http_message.nyholm.factory')
->alias('psr_http_message.psr.response_factory', 'psr_http_message.nyholm.factory')
->alias('psr_http_message.psr.server_request_factory', 'psr_http_message.nyholm.factory')
->alias('psr_http_message.psr.stream_factory', 'psr_http_message.nyholm.factory')
->alias('psr_http_message.psr.uploaded_file_factory', 'psr_http_message.nyholm.factory')
->alias('psr_http_message.psr.uri_factory', 'psr_http_message.nyholm.factory')
;
};
43 changes: 43 additions & 0 deletions Bundle/Resources/config/psr_factories.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
use Symfony\Component\DependencyInjection\Reference;

return static function (ContainerConfigurator $container) {
$container->services()
->set('psr_http_message.psr_http_factory', PsrHttpFactory::class)
->args([
new Reference('psr_http_message.psr.server_request_factory'),
new Reference('psr_http_message.psr.stream_factory'),
new Reference('psr_http_message.psr.uploaded_file_factory'),
new Reference('psr_http_message.psr.response_factory'),
])

->alias(HttpMessageFactoryInterface::class, 'psr_http_message.psr_http_factory')

->alias(RequestFactoryInterface::class, 'psr_http_message.psr.request_factory')
->alias(ResponseFactoryInterface::class, 'psr_http_message.psr.response_factory')
->alias(ServerRequestFactoryInterface::class, 'psr_http_message.psr.server_request_factory')
->alias(StreamFactoryInterface::class, 'psr_http_message.psr.stream_factory')
->alias(UploadedFileFactoryInterface::class, 'psr_http_message.psr.uploaded_file_factory')
->alias(UriFactoryInterface::class, 'psr_http_message.psr.uri_factory')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a soft conflict between these and https://github.com/symfony/recipes/blob/master/nyholm/psr7/1.0/config/packages/nyholm_psr7.yaml

should we remove the recipe for nyholm/psr7 in favor of this bundle?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was about to send a PR, but then: ppl won't be able to add only one package to have working psr17 factories. If they install the bridge, they won't have the implementation. And if we they install the implementation and we remove the recipe, they won't have the aliases :(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you see derrabus#1? It's an attempt to support any PSR-17 implementation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then again, if we just leave everything as is, not much will break. The Nyholm recipe will steal the aliases, but they point to the same stateless classes anyway.

What we could do:

  • We could comment out the aliases in the Nyholm recipe and add a note that the developer should either un-comment them or install the bundle for a better integration.
  • Add a pack that depends on the bridge and the Nyholm implementation and remove the Nyholm recipe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ajgarlag Thank you! Let's merge this PR first and move your PR to this repo afterwards.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd like to give the pack approach a try before merging, because I'm not sure this bundle is the best approach, especially since we're still talking about a pack here...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, go ahead.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@derrabus I could split my PR into two commits:

  1. Conditionally add PSR-17 aliases in a Compiler pass if not defined previously.
  2. Support any PSR-17 implementation.

I think that the first one could be included here. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wait for Nicolas first.

;
};
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
CHANGELOG
=========

# 2.2.0 (TBA)

* Added a bundle that integrates the bridge into Symfony applications

# 2.1.0 (2021-02-17)

* Added a `PsrResponseListener` to automatically convert PSR-7 responses returned by controllers
Expand Down
4 changes: 4 additions & 0 deletions Tests/Factory/PsrHttpFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class PsrHttpFactoryTest extends AbstractHttpMessageFactoryTest
{
protected function buildHttpMessageFactory(): HttpMessageFactoryInterface
{
if (!class_exists(Psr17Factory::class)) {
self::markTestSkipped('This test requires nyholm/psr7.');
}

$factory = new Psr17Factory();

return new PsrHttpFactory($factory, $factory, $factory, $factory);
Expand Down
Loading