diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 772c8fe1ffeb7..aac1d4a9fd42a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1466,9 +1466,75 @@ function ($a) { }) ->end() ->fixXmlConfig('option') + ->validate() + ->ifTrue(static fn (array $transportConf) => \count(array_intersect(['serializer', 'incoming_message_serializer', 'outgoing_message_serializer'], array_keys($transportConf))) > 1) + ->thenInvalid('Only one of "serializer", "incoming_message_serializer" and "outgoing_message_serializer" could be used.') + ->end() ->children() ->scalarNode('dsn')->end() - ->scalarNode('serializer')->defaultNull()->info('Service id of a custom serializer to use.')->end() + ->arrayNode('serializer') + ->info('Transport serializer configuration for messages produced and handled by this application.') + ->beforeNormalization() + ->ifString() + ->then(static fn (string $serviceId) => ['service_id' => $serviceId]) + ->end() + ->children() + ->scalarNode('service_id')->info('Service id of a custom transport serializer to use (incompatible with "format", "context" and "serializer" options).')->defaultNull()->end() + ->scalarNode('format')->info('Serialization format for this transport\'s serializer (will use Symfony serializer).')->end() + ->arrayNode('context') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->defaultValue([]) + ->info('Context serialization array for this transport\'s serializer (will use Symfony serializer).') + ->prototype('variable')->end() + ->end() + ->scalarNode('serializer')->info('Service id of a custom Symfony serializer to use.')->end() + ->end() + ->validate() + ->ifTrue(static fn (array $serializerConf) => !(isset($serializerConf['service_id']) xor (isset($serializerConf['format']) || [] !== $serializerConf['context'] || isset($serializerConf['serializer'])))) + ->thenInvalid('Either "service_id" OR at least one of "format" or "context" or "serializer" should be provided.') + ->end() + ->end() + ->arrayNode('incoming_message_serializer') + ->info('Transport serializer configuration for messages coming from other applications (uses Symfony serializer).') + ->children() + ->scalarNode('messageClass') + ->info('The class in which the message should be deserialized (incompatible with "messageClassResolver" option).') + ->validate() + ->ifTrue(static fn ($class) => $class && !class_exists($class)) + ->thenInvalid('The "messageClass" should be a valid class.') + ->end() + ->end() + ->scalarNode('messageClassResolver')->info('Service id of a message class resolver to use (incompatible with "messageClass" option).')->end() + ->scalarNode('format')->info('Serialization format for this transport\'s serializer.')->end() + ->arrayNode('context') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->defaultValue([]) + ->info('Context serialization array for this transport\'s serializer.') + ->prototype('variable')->end() + ->end() + ->scalarNode('serializer')->info('Service id of a custom Symfony serializer to use.')->end() + ->end() + ->validate() + ->ifTrue(static fn (array $incomingMessageConf) => !(isset($incomingMessageConf['messageClass']) xor isset($incomingMessageConf['messageClassResolver']))) + ->thenInvalid('A message class OR a message class resolver should be provided.') + ->end() + ->end() + ->arrayNode('outgoing_message_serializer') + ->info('Transport serializer configuration for sending messages to other applications (uses Symfony serializer).') + ->children() + ->scalarNode('format')->info('Serialization format for this transport\'s serializer.')->end() + ->arrayNode('context') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->defaultValue([]) + ->info('Context serialization array for this transport\'s serializer.') + ->prototype('variable')->end() + ->end() + ->scalarNode('serializer')->info('Service id of a custom Symfony serializer to use.')->end() + ->end() + ->end() ->arrayNode('options') ->normalizeKeys(false) ->defaultValue([]) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 358ec36f10bea..01a59bf609f88 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -120,6 +120,9 @@ use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Middleware\RouterContextMiddleware; +use Symfony\Component\Messenger\Transport\Serialization\IncomingMessageSerializer; +use Symfony\Component\Messenger\Transport\Serialization\OutgoingMessageSerializer; +use Symfony\Component\Messenger\Transport\Serialization\Serializer; use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface; use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; @@ -2086,7 +2089,47 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $transportRetryReferences = []; $transportRateLimiterReferences = []; foreach ($config['transports'] as $name => $transport) { - $serializerId = $transport['serializer'] ?? 'messenger.default_serializer'; + if (isset($transport['serializer'])) { + $basicSerializerConf = $transport['serializer']; + + $serializerId = $basicSerializerConf['service_id'] ?? "messenger.transport.{$name}.serializer"; + + if (($basicSerializerConf['serializer'] ?? null) || ($basicSerializerConf['format'] ?? null) || [] !== $basicSerializerConf['context']) { + $serializerDefinition = (new Definition(Serializer::class)) + ->setArguments([ + new Reference($basicSerializerConf['serializer'] ?? 'serializer'), + $basicSerializerConf['format'] ?? $config['serializer']['symfony_serializer']['format'], + $basicSerializerConf['context'] ?: $config['serializer']['symfony_serializer']['context'], + ]); + + $container->setDefinition($serializerId, $serializerDefinition); + } + } elseif (isset($transport['incoming_message_serializer'])) { + $incomingSerializerConf = $transport['incoming_message_serializer']; + + $serializerDefinition = (new Definition(IncomingMessageSerializer::class)) + ->setArguments([ + $incomingSerializerConf['messageClass'] ?? new Reference($incomingSerializerConf['messageClassResolver']), + new Reference($incomingSerializerConf['serializer'] ?? 'serializer'), + $incomingSerializerConf['format'] ?? $config['serializer']['symfony_serializer']['format'], + $incomingSerializerConf['context'] ?: $config['serializer']['symfony_serializer']['context'], + ]); + + $container->setDefinition($serializerId = "messenger.transport.{$name}.serializer", $serializerDefinition); + } elseif (isset($transport['outgoing_message_serializer'])) { + $outgoingSerializerConf = $transport['outgoing_message_serializer']; + + $serializerDefinition = (new Definition(OutgoingMessageSerializer::class)) + ->setArguments([ + new Reference($outgoingSerializerConf['serializer'] ?? 'serializer'), + $outgoingSerializerConf['format'] ?? $config['serializer']['symfony_serializer']['format'], + $outgoingSerializerConf['context'] ?: $config['serializer']['symfony_serializer']['context'], + ]); + + $container->setDefinition($serializerId = "messenger.transport.{$name}.serializer", $serializerDefinition); + } + + $serializerId ??= 'messenger.default_serializer'; $transportDefinition = (new Definition(TransportInterface::class)) ->setFactory([new Reference('messenger.transport_factory'), 'createTransport']) ->setArguments([$transport['dsn'], $transport['options'] + ['transport_name' => $name], new Reference($serializerId)]) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer.php new file mode 100644 index 0000000000000..22fd14d394216 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer.php @@ -0,0 +1,64 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'serializer' => true, + 'messenger' => [ + 'serializer' => [ + 'symfony_serializer' => [ + 'format' => 'xml', + 'context' => ['default' => 'context'], + ] + ], + 'transports' => [ + 'default_serializer' => ['dsn' => 'null://'], + 'custom_serializer_short_notation' => [ + 'dsn' => 'null://', + 'serializer' => 'messenger.transport.native_php_serializer', + ], + 'custom_serializer_long_notation' => [ + 'dsn' => 'null://', + 'serializer' => [ + 'service_id' => 'messenger.transport.native_php_serializer', + ], + ], + 'symfony_serializer_with_context' => [ + 'dsn' => 'null://', + 'serializer' => [ + 'format' => 'json', + 'context' => ['some' => 'context'], + 'serializer' => 'my_fancy_serializer', + ], + ], + 'incoming_message_transport' => [ + 'dsn' => 'null://', + 'incoming_message_serializer' => [ + 'messageClass' => BarMessage::class, + 'format' => 'json', + 'context' => ['some' => 'context'], + 'serializer' => 'my_fancy_serializer', + ], + ], + 'incoming_message_transport_with_default_serializer' => [ + 'dsn' => 'null://', + 'incoming_message_serializer' => [ + 'messageClassResolver' => 'some_message_class_resolver_id', + ], + ], + 'outgoing_message_transport' => [ + 'dsn' => 'null://', + 'outgoing_message_serializer' => [ + 'format' => 'json', + 'context' => ['some' => 'context'], + 'serializer' => 'my_fancy_serializer', + ], + ], + 'outgoing_message_transport_with_default_serializer' => [ + 'dsn' => 'null://', + 'outgoing_message_serializer' => [], + ], + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_1.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_1.php new file mode 100644 index 0000000000000..869db0f98f26b --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_1.php @@ -0,0 +1,15 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'serializer' => true, + 'messenger' => [ + 'transports' => [ + 'invalid_transport' => [ + 'dsn' => 'null://', + 'serializer' => 'messenger.transport.native_php_serializer', + 'outgoing_message_serializer' => [] + ] + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_2.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_2.php new file mode 100644 index 0000000000000..b51b50a351c8a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_2.php @@ -0,0 +1,17 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'serializer' => true, + 'messenger' => [ + 'transports' => [ + 'invalid_transport' => [ + 'dsn' => 'null://', + 'serializer' => [ + 'service_id' => 'messenger.transport.native_php_serializer', + 'format' => 'json' + ], + ] + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_3.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_3.php new file mode 100644 index 0000000000000..b4a6a7e16de02 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_3.php @@ -0,0 +1,14 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'serializer' => true, + 'messenger' => [ + 'transports' => [ + 'invalid_transport' => [ + 'dsn' => 'null://', + 'serializer' => [], + ] + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_4.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_4.php new file mode 100644 index 0000000000000..96e5e4d07f531 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_4.php @@ -0,0 +1,19 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'serializer' => true, + 'messenger' => [ + 'transports' => [ + 'invalid_transport' => [ + 'dsn' => 'null://', + 'incoming_message_serializer' => [ + 'messageClass' => BarMessage::class, + 'messageClassResolver' => 'foo', + ], + ] + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_5.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_5.php new file mode 100644 index 0000000000000..f46fc375be338 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transport_serializer_invalid_5.php @@ -0,0 +1,16 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'serializer' => true, + 'messenger' => [ + 'transports' => [ + 'invalid_transport' => [ + 'dsn' => 'null://', + 'incoming_message_serializer' => [ + 'messageClass' => 'foo', + ], + ] + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer.xml new file mode 100644 index 0000000000000..cf583ec809485 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + context + + + + + + + + + + + + + + + + context + + + + + + + + context + + + + + + + + + + + + context + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_1.xml new file mode 100644 index 0000000000000..dfd04dc43fef7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_1.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_2.xml new file mode 100644 index 0000000000000..f758f30b1a88c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_3.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_3.xml new file mode 100644 index 0000000000000..4e66c3487f03c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_3.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_4.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_4.xml new file mode 100644 index 0000000000000..def75bbcd251b --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_4.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_5.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_5.xml new file mode 100644 index 0000000000000..0975b13b3bfa7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_invalid_5.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer.yml new file mode 100644 index 0000000000000..eda1c7f12c4ec --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer.yml @@ -0,0 +1,44 @@ +framework: + http_method_override: false + serializer: true + messenger: + serializer: + symfony_serializer: + format: xml + context: {default: context} + transports: + default_serializer: + dsn: 'null://' + custom_serializer_short_notation: + dsn: 'null://' + serializer: 'messenger.transport.native_php_serializer' + custom_serializer_long_notation: + dsn: 'null://' + serializer: + service_id: 'messenger.transport.native_php_serializer' + symfony_serializer_with_context: + dsn: 'null://' + serializer: + format: 'json' + context: {some: context} + serializer: 'my_fancy_serializer' + incoming_message_transport: + dsn: 'null://' + incoming_message_serializer: + messageClass: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\BarMessage + format: 'json' + context: {some: context} + serializer: 'my_fancy_serializer' + incoming_message_transport_with_default_serializer: + dsn: 'null://' + incoming_message_serializer: + messageClassResolver: 'some_message_class_resolver_id' + outgoing_message_transport: + dsn: 'null://' + outgoing_message_serializer: + format: 'json' + context: {some: context} + serializer: 'my_fancy_serializer' + outgoing_message_transport_with_default_serializer: + dsn: 'null://' + outgoing_message_serializer: ~ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_1.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_1.yml new file mode 100644 index 0000000000000..1d88974672b6f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_1.yml @@ -0,0 +1,9 @@ +framework: + http_method_override: false + serializer: true + messenger: + transports: + invalid_transport: + dsn: 'null://' + serializer: 'messenger.transport.native_php_serializer' + outgoing_message_serializer: ~ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_2.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_2.yml new file mode 100644 index 0000000000000..2086945145672 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_2.yml @@ -0,0 +1,10 @@ +framework: + http_method_override: false + serializer: true + messenger: + transports: + invalid_transport: + dsn: 'null://' + serializer: + service_id: 'messenger.transport.native_php_serializer' + format: json diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_3.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_3.yml new file mode 100644 index 0000000000000..1c42ed1e16704 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_3.yml @@ -0,0 +1,8 @@ +framework: + http_method_override: false + serializer: true + messenger: + transports: + invalid_transport: + dsn: 'null://' + serializer: ~ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_4.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_4.yml new file mode 100644 index 0000000000000..5096657148ff8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_4.yml @@ -0,0 +1,10 @@ +framework: + http_method_override: false + serializer: true + messenger: + transports: + invalid_transport: + dsn: 'null://' + incoming_message_serializer: + messageClass: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\BarMessage + messageClassResolver: foo diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_5.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_5.yml new file mode 100644 index 0000000000000..44363a712bfde --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transport_serializer_invalid_5.yml @@ -0,0 +1,9 @@ +framework: + http_method_override: false + serializer: true + messenger: + transports: + invalid_transport: + dsn: 'null://' + incoming_message_serializer: + messageClass: foo diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index e3416357c162b..d224ab7f18bc2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -18,6 +18,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\BarMessage; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FullStack; @@ -57,6 +58,9 @@ use Symfony\Component\HttpClient\ScopingHttpClient; use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; use Symfony\Component\HttpKernel\Fragment\FragmentUriGeneratorInterface; +use Symfony\Component\Messenger\Transport\Serialization\IncomingMessageSerializer; +use Symfony\Component\Messenger\Transport\Serialization\OutgoingMessageSerializer; +use Symfony\Component\Messenger\Transport\Serialization\Serializer as MessengerTransportSerializer; use Symfony\Component\Messenger\Transport\TransportFactory; use Symfony\Component\Notifier\ChatterInterface; use Symfony\Component\Notifier\TexterInterface; @@ -1083,6 +1087,107 @@ public function testMessengerWithDisabledResetOnMessage() $this->createContainerFromFile('messenger_with_disabled_reset_on_message'); } + public function testMessengerTransportSerializerConfigurations() + { + $container = $this->createContainerFromFile('messenger_transport_serializer'); + + $transportDefinition = $container->getDefinition('messenger.transport.default_serializer'); + $this->assertEquals(new Reference('messenger.default_serializer'), $transportDefinition->getArguments()[2]); + + $transportDefinition = $container->getDefinition('messenger.transport.custom_serializer_short_notation'); + $this->assertEquals(new Reference('messenger.transport.native_php_serializer'), $transportDefinition->getArguments()[2]); + + $transportDefinition = $container->getDefinition('messenger.transport.custom_serializer_long_notation'); + $this->assertEquals(new Reference('messenger.transport.native_php_serializer'), $transportDefinition->getArguments()[2]); + + $transportDefinition = $container->getDefinition('messenger.transport.symfony_serializer_with_context'); + $this->assertEquals(new Reference($serializerId = 'messenger.transport.symfony_serializer_with_context.serializer'), $transportDefinition->getArguments()[2]); + $serializerDefinition = $container->getDefinition($serializerId); + self::assertSame(MessengerTransportSerializer::class, $serializerDefinition->getClass()); + self::assertEquals( + [ + new Reference('my_fancy_serializer'), + 'json', + ['some' => 'context'], + ], + $serializerDefinition->getArguments() + ); + + $transportDefinition = $container->getDefinition('messenger.transport.incoming_message_transport'); + $this->assertEquals(new Reference($serializerId = 'messenger.transport.incoming_message_transport.serializer'), $transportDefinition->getArguments()[2]); + $serializerDefinition = $container->getDefinition($serializerId); + self::assertSame(IncomingMessageSerializer::class, $serializerDefinition->getClass()); + self::assertEquals( + [ + BarMessage::class, + new Reference('my_fancy_serializer'), + 'json', + ['some' => 'context'], + ], + $serializerDefinition->getArguments() + ); + + $transportDefinition = $container->getDefinition('messenger.transport.incoming_message_transport_with_default_serializer'); + $this->assertEquals(new Reference($serializerId = 'messenger.transport.incoming_message_transport_with_default_serializer.serializer'), $transportDefinition->getArguments()[2]); + $serializerDefinition = $container->getDefinition($serializerId); + self::assertSame(IncomingMessageSerializer::class, $serializerDefinition->getClass()); + self::assertEquals( + [ + new Reference('some_message_class_resolver_id'), + new Reference('serializer'), + 'xml', + ['default' => 'context'], + ], + $serializerDefinition->getArguments() + ); + + $transportDefinition = $container->getDefinition('messenger.transport.outgoing_message_transport'); + $this->assertEquals(new Reference($serializerId = 'messenger.transport.outgoing_message_transport.serializer'), $transportDefinition->getArguments()[2]); + $serializerDefinition = $container->getDefinition($serializerId); + self::assertSame(OutgoingMessageSerializer::class, $serializerDefinition->getClass()); + self::assertEquals( + [ + new Reference('my_fancy_serializer'), + 'json', + ['some' => 'context'], + ], + $serializerDefinition->getArguments() + ); + + $transportDefinition = $container->getDefinition('messenger.transport.outgoing_message_transport_with_default_serializer'); + $this->assertEquals(new Reference($serializerId = 'messenger.transport.outgoing_message_transport_with_default_serializer.serializer'), $transportDefinition->getArguments()[2]); + $serializerDefinition = $container->getDefinition($serializerId); + self::assertSame(OutgoingMessageSerializer::class, $serializerDefinition->getClass()); + self::assertEquals( + [ + new Reference('serializer'), + 'xml', + ['default' => 'context'], + ], + $serializerDefinition->getArguments() + ); + } + + /** + * @dataProvider invalidMessengerTransportSerializerConfigurationsProvider + */ + public function testInvalidMessengerTransportSerializerConfigurations(string $configFile, string $errorMessage) + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage($errorMessage); + + $this->createContainerFromFile($configFile); + } + + public function invalidMessengerTransportSerializerConfigurationsProvider(): iterable + { + yield ['messenger_transport_serializer_invalid_1', 'Only one of "serializer", "incoming_message_serializer" and "outgoing_message_serializer" could be used']; + yield ['messenger_transport_serializer_invalid_2', 'Either "service_id" OR at least one of "format" or "context" or "serializer" should be provided.']; + yield ['messenger_transport_serializer_invalid_3', 'Either "service_id" OR at least one of "format" or "context" or "serializer" should be provided.']; + yield ['messenger_transport_serializer_invalid_4', 'A message class OR a message class resolver should be provided.']; + yield ['messenger_transport_serializer_invalid_5', 'The "messageClass" should be a valid class.']; + } + public function testTranslator() { $container = $this->createContainerFromFile('full'); diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/IncomingMessageClassResolverInterface.php b/src/Symfony/Component/Messenger/Transport/Serialization/IncomingMessageClassResolverInterface.php new file mode 100644 index 0000000000000..7ef0db2d1ce0d --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/Serialization/IncomingMessageClassResolverInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport\Serialization; + +interface IncomingMessageClassResolverInterface +{ + /** + * @return class-string + */ + public function __invoke(array $encodedEnvelope): string; +} diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/IncomingMessageSerializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/IncomingMessageSerializer.php new file mode 100644 index 0000000000000..069c88559ea70 --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/Serialization/IncomingMessageSerializer.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport\Serialization; + +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Serializer\SerializerInterface as SymfonySerializerInterface; + +final class IncomingMessageSerializer implements SerializerInterface +{ + private Serializer $decorated; + + public function __construct(private readonly IncomingMessageClassResolverInterface|string $messageClass, SymfonySerializerInterface $serializer, string $format, array $context) + { + $this->decorated = new Serializer($serializer, $format, $context); + } + + public function decode(array $encodedEnvelope): Envelope + { + $encodedEnvelope['headers']['type'] ??= $this->resolveType($encodedEnvelope); + + return $this->decorated->decode($encodedEnvelope); + } + + public function encode(Envelope $envelope): array + { + return $this->decorated->encode($envelope); + } + + private function resolveType(array $encodedEnvelope): string + { + return \is_string($this->messageClass) ? $this->messageClass : ($this->messageClass)($encodedEnvelope); + } +} diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/OutgoingMessageSerializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/OutgoingMessageSerializer.php new file mode 100644 index 0000000000000..7b01e593a18bb --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/Serialization/OutgoingMessageSerializer.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport\Serialization; + +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Serializer\SerializerInterface as SymfonySerializerInterface; + +final class OutgoingMessageSerializer implements SerializerInterface +{ + private Serializer $decorated; + + public function __construct(SymfonySerializerInterface $serializer, string $format, array $context) + { + $this->decorated = new Serializer($serializer, $format, $context); + } + + public function decode(array $encodedEnvelope): Envelope + { + throw new \LogicException(sprintf('Cannot use "%s" to decode message.', __CLASS__)); + } + + public function encode(Envelope $envelope): array + { + $encode = $this->decorated->encode(Envelope::wrap($envelope->getMessage())); + + unset($encode['headers']['type']); + + return $encode; + } +}