Making the serializer configurable by transport

This commit is contained in:
Ryan Weaver 2019-03-21 10:47:28 -04:00 committed by Fabien Potencier
parent 07276642de
commit ef6f23e8b9
29 changed files with 75 additions and 174 deletions

View File

@ -8,11 +8,18 @@ CHANGELOG
* Not passing the project directory to the constructor of the `AssetsInstallCommand` is deprecated. This argument will
be mandatory in 5.0.
* Deprecated the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead
* Added the ability to specify a custom `serializer` option for each
transport under`framework.messenger.transports`.
* [BC Break] When using Messenger, the default transport changed from
using Symfony's serializer service to use `PhpSerializer`, which uses
PHP's native `serialize()` and `unserialize()` functions. To use the
original serialization method, set the `framework.messenger.serializer.id`
config option to `messenger.transport.symfony_serializer`.
original serialization method, set the `framework.messenger.defaut_serializer`
config option to `messenger.transport.symfony_serializer`. Or set the
`serializer` option under one specific `transport`.
* [BC Break] The `framework.messenger.serializer` config key changed to
`framework.messenger.default_serializer`, which holds the string service
id and `framework.messenger.symfony_serializer`, which configures the
options if you're using Symfony's serializer.
* Added information about deprecated aliases in `debug:autowiring`
* Added php ini session options `sid_length` and `sid_bits_per_character`
to the `session` section of the configuration

View File

@ -1107,29 +1107,19 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->end()
->arrayNode('serializer')
->scalarNode('default_serializer')
->defaultValue('messenger.transport.native_php_serializer')
->info('Service id to use as the default serializer for the transports.')
->end()
->arrayNode('symfony_serializer')
->addDefaultsIfNotSet()
->beforeNormalization()
->always()
->then(function ($config) {
if (false === $config) {
return ['id' => null];
}
if (\is_string($config)) {
return ['id' => $config];
}
return $config;
})
->end()
->children()
->scalarNode('id')->defaultValue('messenger.transport.native_php_serializer')->end()
->scalarNode('format')->defaultValue('json')->end()
->scalarNode('format')->defaultValue('json')->info('Serialization format for the messenger.transport.symfony_serializer service (which is not the serializer used by default).')->end()
->arrayNode('context')
->normalizeKeys(false)
->useAttributeAsKey('name')
->defaultValue([])
->info('Context array for the messenger.transport.symfony_serializer service (which is not the serializer used by default).')
->prototype('variable')->end()
->end()
->end()
@ -1146,6 +1136,7 @@ class Configuration implements ConfigurationInterface
->fixXmlConfig('option')
->children()
->scalarNode('dsn')->end()
->scalarNode('serializer')->defaultNull()->info('Service id of a custom serializer to use.')->end()
->arrayNode('options')
->normalizeKeys(false)
->defaultValue([])

View File

@ -78,7 +78,6 @@ use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
@ -1604,28 +1603,6 @@ class FrameworkExtension extends Extension
$loader->load('messenger.xml');
if (empty($config['transports'])) {
$container->removeDefinition('messenger.transport.symfony_serializer');
$container->removeDefinition('messenger.transport.amqp.factory');
} else {
if ('messenger.transport.symfony_serializer' === $config['serializer']['id']) {
if (!$this->isConfigEnabled($container, $serializerConfig)) {
throw new LogicException('The Messenger serializer cannot be enabled as the Serializer support is not available. Try enabling it or running "composer require symfony/serializer-pack".');
}
$container->getDefinition('messenger.transport.symfony_serializer')
->replaceArgument(1, $config['serializer']['format'])
->replaceArgument(2, $config['serializer']['context']);
}
if ($config['serializer']['id']) {
$container->setAlias('messenger.transport.serializer', $config['serializer']['id']);
} else {
$container->removeDefinition('messenger.transport.amqp.factory');
$container->removeDefinition(SerializerInterface::class);
}
}
if (null === $config['default_bus'] && 1 === \count($config['buses'])) {
$config['default_bus'] = key($config['buses']);
}
@ -1677,16 +1654,24 @@ class FrameworkExtension extends Extension
}
}
if (empty($config['transports'])) {
$container->removeDefinition('messenger.transport.symfony_serializer');
$container->removeDefinition('messenger.transport.amqp.factory');
} else {
$container->getDefinition('messenger.transport.symfony_serializer')
->replaceArgument(1, $config['symfony_serializer']['format'])
->replaceArgument(2, $config['symfony_serializer']['context']);
$container->setAlias('messenger.default_serializer', $config['default_serializer']);
}
$senderAliases = [];
$transportRetryReferences = [];
foreach ($config['transports'] as $name => $transport) {
if (0 === strpos($transport['dsn'], 'amqp://') && !$container->hasDefinition('messenger.transport.amqp.factory')) {
throw new LogicException('The default AMQP transport is not available. Make sure you have installed and enabled the Serializer component. Try enabling it or running "composer require symfony/serializer-pack".');
}
$serializerId = $transport['serializer'] ?? 'messenger.default_serializer';
$transportDefinition = (new Definition(TransportInterface::class))
->setFactory([new Reference('messenger.transport_factory'), 'createTransport'])
->setArguments([$transport['dsn'], $transport['options']])
->setArguments([$transport['dsn'], $transport['options'], new Reference($serializerId)])
->addTag('messenger.receiver', ['alias' => $name])
;
$container->setDefinition($transportId = 'messenger.transport.'.$name, $transportDefinition);

View File

@ -26,7 +26,7 @@
<argument /> <!-- Format -->
<argument type="collection" /> <!-- Context -->
</service>
<service id="Symfony\Component\Messenger\Transport\Serialization\SerializerInterface" alias="messenger.transport.serializer" />
<service id="Symfony\Component\Messenger\Transport\Serialization\SerializerInterface" alias="messenger.default_serializer" />
<service id="messenger.transport.native_php_serializer" class="Symfony\Component\Messenger\Transport\Serialization\PhpSerializer" />
@ -64,7 +64,6 @@
<service id="messenger.transport.amqp.factory" class="Symfony\Component\Messenger\Transport\AmqpExt\AmqpTransportFactory">
<tag name="messenger.transport_factory" />
<argument type="service" id="messenger.transport.serializer" />
</service>
<service id="messenger.transport.sync.factory" class="Symfony\Component\Messenger\Transport\Sync\SyncTransportFactory">

View File

@ -402,7 +402,8 @@
<xsd:complexType name="messenger">
<xsd:sequence>
<xsd:element name="serializer" type="messenger_serializer" minOccurs="0" />
<xsd:element name="default-serializer" type="xsd:string" minOccurs="0" />
<xsd:element name="symfony-serializer" type="messenger_symfony_serializer" minOccurs="0" />
<xsd:element name="encoder" type="xsd:string" minOccurs="0" />
<xsd:element name="decoder" type="xsd:string" minOccurs="0" />
<xsd:element name="routing" type="messenger_routing" minOccurs="0" maxOccurs="unbounded" />
@ -412,12 +413,11 @@
<xsd:attribute name="default-bus" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="messenger_serializer">
<xsd:complexType name="messenger_symfony_serializer">
<xsd:sequence>
<xsd:element name="context" type="metadata" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="format" type="xsd:string" />
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="messenger_routing">
@ -437,6 +437,7 @@
<xsd:element name="options" type="metadata" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="serializer" type="xsd:string" />
<xsd:attribute name="dsn" type="xsd:string" />
</xsd:complexType>

View File

@ -325,8 +325,8 @@ class ConfigurationTest extends TestCase
'enabled' => !class_exists(FullStack::class) && interface_exists(MessageBusInterface::class),
'routing' => [],
'transports' => [],
'serializer' => [
'id' => 'messenger.transport.native_php_serializer',
'default_serializer' => 'messenger.transport.native_php_serializer',
'symfony_serializer' => [
'format' => 'json',
'context' => [],
],

View File

@ -5,7 +5,7 @@ use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\FooMessage;
$container->loadFromExtension('framework', [
'messenger' => [
'serializer' => false,
'default_serializer' => false,
'routing' => [
FooMessage::class => ['sender.bar', 'sender.biz'],
BarMessage::class => 'sender.foo',

View File

@ -1,10 +0,0 @@
<?php
$container->loadFromExtension('framework', [
'messenger' => [
'serializer' => false,
'transports' => [
'default' => 'amqp://localhost/%2f/messages',
],
],
]);

View File

@ -3,7 +3,7 @@
$container->loadFromExtension('framework', [
'serializer' => true,
'messenger' => [
'serializer' => 'messenger.transport.symfony_serializer',
'default_serializer' => 'messenger.transport.symfony_serializer',
'routing' => [
'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage' => ['amqp', 'audit'],
'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage' => [

View File

@ -3,8 +3,8 @@
$container->loadFromExtension('framework', [
'serializer' => true,
'messenger' => [
'serializer' => [
'id' => 'messenger.transport.symfony_serializer',
'default_serializer' => 'messenger.transport.symfony_serializer',
'symfony_serializer' => [
'format' => 'csv',
'context' => ['enable_max_depth' => true],
],

View File

@ -1,13 +0,0 @@
<?php
$container->loadFromExtension('framework', [
'serializer' => [
'enabled' => false,
],
'messenger' => [
'serializer' => 'messenger.transport.symfony_serializer',
'transports' => [
'default' => 'amqp://localhost/%2f/messages',
],
],
]);

View File

@ -3,12 +3,13 @@
$container->loadFromExtension('framework', [
'serializer' => true,
'messenger' => [
'serializer' => 'messenger.transport.symfony_serializer',
'default_serializer' => 'messenger.transport.symfony_serializer',
'transports' => [
'default' => 'amqp://localhost/%2f/messages',
'customised' => [
'dsn' => 'amqp://localhost/%2f/messages?exchange_name=exchange_name',
'options' => ['queue' => ['name' => 'Queue']],
'serializer' => 'messenger.transport.native_php_serializer',
],
],
],

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:messenger>
<framework:serializer id="null" />
<framework:transport name="default" dsn="amqp://localhost/%2f/messages" />
</framework:messenger>
</framework:config>
</container>

View File

@ -8,7 +8,7 @@
<framework:config>
<framework:serializer enabled="true" />
<framework:messenger>
<framework:serializer id="messenger.transport.symfony_serializer" />
<framework:default-serializer>messenger.transport.symfony_serializer</framework:default-serializer>
<framework:routing message-class="Symfony\Component\Messenger\Tests\Fixtures\DummyMessage">
<framework:sender service="amqp" />
<framework:sender service="audit" />

View File

@ -8,11 +8,12 @@
<framework:config>
<framework:serializer enabled="true" />
<framework:messenger>
<framework:serializer id="messenger.transport.symfony_serializer" format="csv">
<framework:default-serializer>messenger.transport.symfony_serializer</framework:default-serializer>
<framework:symfony-serializer format="csv">
<framework:context>
<framework:enable_max_depth>true</framework:enable_max_depth>
</framework:context>
</framework:serializer>
</framework:symfony-serializer>
<framework:transport name="default" dsn="amqp://localhost/%2f/messages" />
</framework:messenger>
</framework:config>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:serializer enabled="false" />
<framework:messenger>
<framework:serializer id="messenger.transport.symfony_serializer" />
<framework:transport name="default" dsn="amqp://localhost/%2f/messages" />
</framework:messenger>
</framework:config>
</container>

View File

@ -8,9 +8,9 @@
<framework:config>
<framework:serializer enabled="true" />
<framework:messenger>
<framework:serializer id="messenger.transport.symfony_serializer" />
<framework:default-serializer>messenger.transport.symfony_serializer</framework:default-serializer>
<framework:transport name="default" dsn="amqp://localhost/%2f/messages" />
<framework:transport name="customised" dsn="amqp://localhost/%2f/messages?exchange_name=exchange_name">
<framework:transport name="customised" dsn="amqp://localhost/%2f/messages?exchange_name=exchange_name" serializer="messenger.transport.native_php_serializer">
<framework:options>
<framework:queue>
<framework:name>Queue</framework:name>

View File

@ -1,6 +1,6 @@
framework:
messenger:
serializer: false
default_serializer: false
routing:
'Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\FooMessage': ['sender.bar', 'sender.biz']
'Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\BarMessage': 'sender.foo'

View File

@ -1,5 +0,0 @@
framework:
messenger:
serializer: false
transports:
default: 'amqp://localhost/%2f/messages'

View File

@ -1,7 +1,7 @@
framework:
serializer: true
messenger:
serializer: messenger.transport.symfony_serializer
default_serializer: messenger.transport.symfony_serializer
routing:
'Symfony\Component\Messenger\Tests\Fixtures\DummyMessage': [amqp, audit]
'Symfony\Component\Messenger\Tests\Fixtures\SecondMessage':

View File

@ -1,8 +1,8 @@
framework:
serializer: true
messenger:
serializer:
id: messenger.transport.symfony_serializer
default_serializer: messenger.transport.symfony_serializer
symfony_serializer:
format: csv
context:
enable_max_depth: true

View File

@ -1,7 +0,0 @@
framework:
serializer:
enabled: false
messenger:
serializer: messenger.transport.symfony_serializer
transports:
default: 'amqp://localhost/%2f/messages'

View File

@ -1,7 +1,7 @@
framework:
serializer: true
messenger:
serializer: messenger.transport.symfony_serializer
default_serializer: messenger.transport.symfony_serializer
transports:
default: 'amqp://localhost/%2f/messages'
customised:
@ -9,3 +9,4 @@ framework:
options:
queue:
name: Queue
serializer: 'messenger.transport.native_php_serializer'

View File

@ -661,15 +661,18 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertTrue($container->hasDefinition('messenger.transport.default'));
$this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.receiver'));
$this->assertEquals([['alias' => 'default']], $container->getDefinition('messenger.transport.default')->getTag('messenger.receiver'));
$transportArguments = $container->getDefinition('messenger.transport.default')->getArguments();
$this->assertEquals(new Reference('messenger.default_serializer'), $transportArguments[2]);
$this->assertTrue($container->hasDefinition('messenger.transport.customised'));
$transportFactory = $container->getDefinition('messenger.transport.customised')->getFactory();
$transportArguments = $container->getDefinition('messenger.transport.customised')->getArguments();
$this->assertEquals([new Reference('messenger.transport_factory'), 'createTransport'], $transportFactory);
$this->assertCount(2, $transportArguments);
$this->assertCount(3, $transportArguments);
$this->assertSame('amqp://localhost/%2f/messages?exchange_name=exchange_name', $transportArguments[0]);
$this->assertSame(['queue' => ['name' => 'Queue']], $transportArguments[1]);
$this->assertEquals(['queue' => ['name' => 'Queue']], $transportArguments[1]);
$this->assertEquals(new Reference('messenger.transport.native_php_serializer'), $transportArguments[2]);
$this->assertTrue($container->hasDefinition('messenger.transport.amqp.factory'));
}
@ -693,29 +696,11 @@ abstract class FrameworkExtensionTest extends TestCase
], $sendersMapping[DummyMessage::class]->getValues());
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage The Messenger serializer cannot be enabled as the Serializer support is not available. Try enabling it or running "composer require symfony/serializer-pack".
*/
public function testMessengerTransportConfigurationWithoutSerializer()
{
$this->createContainerFromFile('messenger_transport_no_serializer');
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage The default AMQP transport is not available. Make sure you have installed and enabled the Serializer component. Try enabling it or running "composer require symfony/serializer-pack".
*/
public function testMessengerAMQPTransportConfigurationWithoutSerializer()
{
$this->createContainerFromFile('messenger_amqp_transport_no_serializer');
}
public function testMessengerTransportConfiguration()
{
$container = $this->createContainerFromFile('messenger_transport');
$this->assertSame('messenger.transport.symfony_serializer', (string) $container->getAlias('messenger.transport.serializer'));
$this->assertSame('messenger.transport.symfony_serializer', (string) $container->getAlias('messenger.default_serializer'));
$serializerTransportDefinition = $container->getDefinition('messenger.transport.symfony_serializer');
$this->assertSame('csv', $serializerTransportDefinition->getArgument(1));

View File

@ -4,10 +4,12 @@ CHANGELOG
4.3.0
-----
* [BC BREAK] The `TransportFactoryInterface::createTransport()` signature
changed: a required 3rd `SerializerInterface` argument was added.
* Added a new `SyncTransport` along with `ForceCallHandlersStamp` to
explicitly handle messages synchronously.
* Added optional parameter `prefetch_count` in connection configuration,
to setup channel prefetch count
to setup channel prefetch count.
* New classes: `RoutableMessageBus`, `AddBusNameStampMiddleware`
and `BusNameStamp` were added, which allow you to add a bus identifier
to the `Envelope` then find the correct bus when receiving from

View File

@ -21,9 +21,7 @@ class AmqpTransportFactoryTest extends TestCase
{
public function testSupportsOnlyAmqpTransports()
{
$factory = new AmqpTransportFactory(
$this->getMockBuilder(SerializerInterface::class)->getMock()
);
$factory = new AmqpTransportFactory();
$this->assertTrue($factory->supports('amqp://localhost', []));
$this->assertFalse($factory->supports('sqs://localhost', []));
@ -32,12 +30,11 @@ class AmqpTransportFactoryTest extends TestCase
public function testItCreatesTheTransport()
{
$factory = new AmqpTransportFactory(
$serializer = $this->getMockBuilder(SerializerInterface::class)->getMock()
);
$factory = new AmqpTransportFactory();
$serializer = $this->createMock(SerializerInterface::class);
$expectedTransport = new AmqpTransport(Connection::fromDsn('amqp://localhost', ['foo' => 'bar']), $serializer);
$this->assertEquals($expectedTransport, $factory->createTransport('amqp://localhost', ['foo' => 'bar']));
$this->assertEquals($expectedTransport, $factory->createTransport('amqp://localhost', ['foo' => 'bar'], $serializer));
}
}

View File

@ -11,7 +11,6 @@
namespace Symfony\Component\Messenger\Transport\AmqpExt;
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
@ -23,16 +22,9 @@ use Symfony\Component\Messenger\Transport\TransportInterface;
*/
class AmqpTransportFactory implements TransportFactoryInterface
{
private $serializer;
public function __construct(SerializerInterface $serializer = null)
public function createTransport(string $dsn, array $options, SerializerInterface $serializer): TransportInterface
{
$this->serializer = $serializer ?? new PhpSerializer();
}
public function createTransport(string $dsn, array $options): TransportInterface
{
return new AmqpTransport(Connection::fromDsn($dsn, $options), $this->serializer);
return new AmqpTransport(Connection::fromDsn($dsn, $options), $serializer);
}
public function supports(string $dsn, array $options): bool

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger\Transport;
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
@ -30,11 +31,11 @@ class TransportFactory implements TransportFactoryInterface
$this->factories = $factories;
}
public function createTransport(string $dsn, array $options): TransportInterface
public function createTransport(string $dsn, array $options, SerializerInterface $serializer): TransportInterface
{
foreach ($this->factories as $factory) {
if ($factory->supports($dsn, $options)) {
return $factory->createTransport($dsn, $options);
return $factory->createTransport($dsn, $options, $serializer);
}
}

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\Messenger\Transport;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
/**
* Creates a Messenger transport.
*
@ -20,7 +22,7 @@ namespace Symfony\Component\Messenger\Transport;
*/
interface TransportFactoryInterface
{
public function createTransport(string $dsn, array $options): TransportInterface;
public function createTransport(string $dsn, array $options, SerializerInterface $serializer): TransportInterface;
public function supports(string $dsn, array $options): bool;
}