diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
index 4d89c1bffc00c..4e30aa5394084 100644
--- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
+++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
@@ -10,6 +10,7 @@ CHANGELOG
* Deprecate `get()`, `has()`, `getDoctrine()`, and `dispatchMessage()` in `AbstractController`, use method/constructor injection instead
* Add `MicroKernelTrait::getBundlesPath` method to get bundles config path
* Deprecate the `cache.adapter.doctrine` service
+ * Add support for resetting container services after each messenger message.
5.3
---
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index d52b8dfb0dcb9..52f81c0a32421 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -1333,6 +1333,10 @@ function ($a) {
->fixXmlConfig('option')
->children()
->scalarNode('dsn')->end()
+ ->booleanNode('reset_on_message')
+ ->defaultFalse()
+ ->info('Reset container services after each message. Turn it on when the transport is async and run in a worker.')
+ ->end()
->scalarNode('serializer')->defaultNull()->info('Service id of a custom serializer to use.')->end()
->arrayNode('options')
->normalizeKeys(false)
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 04526e82932c0..6ddf0c3a053a9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -2013,6 +2013,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
$senderAliases = [];
$transportRetryReferences = [];
+ $transportNamesForResetServices = [];
foreach ($config['transports'] as $name => $transport) {
$serializerId = $transport['serializer'] ?? 'messenger.default_serializer';
$transportDefinition = (new Definition(TransportInterface::class))
@@ -2041,6 +2042,18 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
$transportRetryReferences[$name] = new Reference($retryServiceId);
}
+ if ($transport['reset_on_message']) {
+ $transportNamesForResetServices[] = $name;
+ }
+ }
+
+ if ($transportNamesForResetServices) {
+ $container
+ ->getDefinition('messenger.listener.reset_services')
+ ->replaceArgument(1, $transportNamesForResetServices)
+ ;
+ } else {
+ $container->removeDefinition('messenger.listener.reset_services');
}
$senderReferences = [];
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php
index d7953122fbe51..c15294c38ccda 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php
@@ -18,6 +18,7 @@
use Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory;
use Symfony\Component\Messenger\EventListener\AddErrorDetailsStampListener;
use Symfony\Component\Messenger\EventListener\DispatchPcntlSignalListener;
+use Symfony\Component\Messenger\EventListener\ResetServicesListener;
use Symfony\Component\Messenger\EventListener\SendFailedMessageForRetryListener;
use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener;
use Symfony\Component\Messenger\EventListener\StopWorkerOnCustomStopExceptionListener;
@@ -195,6 +196,12 @@
->tag('kernel.event_subscriber')
->set('messenger.listener.stop_worker_on_stop_exception_listener', StopWorkerOnCustomStopExceptionListener::class)
+
+ ->set('messenger.listener.reset_services', ResetServicesListener::class)
+ ->args([
+ service('services_resetter'),
+ abstract_arg('receivers names'),
+ ])
->tag('kernel.event_subscriber')
->set('messenger.routable_message_bus', RoutableMessageBus::class)
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index 5082c3356a673..47c7edf34f7d5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -505,6 +505,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php
index 90c5def3ac100..1c8b56683d2e5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php
@@ -11,6 +11,7 @@
'default' => 'amqp://localhost/%2f/messages',
'customised' => [
'dsn' => 'amqp://localhost/%2f/messages?exchange_name=exchange_name',
+ 'reset_on_message' => true,
'options' => ['queue' => ['name' => 'Queue']],
'serializer' => 'messenger.transport.native_php_serializer',
'retry_strategy' => [
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml
index b0510d580ceaf..dfa3232997c52 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml
@@ -10,7 +10,7 @@
-
+
Queue
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml
index d00f4a65dd37c..fb2827729d5e2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml
@@ -8,6 +8,7 @@ framework:
default: 'amqp://localhost/%2f/messages'
customised:
dsn: 'amqp://localhost/%2f/messages?exchange_name=exchange_name'
+ reset_on_message: true
options:
queue:
name: Queue
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index 3315664ec80ba..15aee24a5b701 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -723,6 +723,7 @@ public function testMessenger()
$this->assertTrue($container->hasDefinition('messenger.transport.redis.factory'));
$this->assertTrue($container->hasDefinition('messenger.transport_factory'));
$this->assertSame(TransportFactory::class, $container->getDefinition('messenger.transport_factory')->getClass());
+ $this->assertFalse($container->hasDefinition('messenger.listener.reset_services'));
}
public function testMessengerMultipleFailureTransports()
@@ -867,6 +868,9 @@ public function testMessengerTransports()
return array_shift($values);
}, $failureTransports);
$this->assertEquals($expectedTransportsByFailureTransports, $failureTransportsReferences);
+
+ $this->assertTrue($container->hasDefinition('messenger.listener.reset_services'));
+ $this->assertSame(['customised'], $container->getDefinition('messenger.listener.reset_services')->getArgument(1));
}
public function testMessengerRouting()
diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md
index fed0bc8b76f93..889207a17400d 100644
--- a/src/Symfony/Component/Messenger/CHANGELOG.md
+++ b/src/Symfony/Component/Messenger/CHANGELOG.md
@@ -5,6 +5,7 @@ CHANGELOG
---
* Add `StopWorkerExceptionInterface` and its implementation `StopWorkerException` to stop the worker.
+ * Add support for resetting container services after each messenger message.
5.3
---
diff --git a/src/Symfony/Component/Messenger/EventListener/ResetServicesListener.php b/src/Symfony/Component/Messenger/EventListener/ResetServicesListener.php
new file mode 100644
index 0000000000000..19b7ebe94c967
--- /dev/null
+++ b/src/Symfony/Component/Messenger/EventListener/ResetServicesListener.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Messenger\EventListener;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
+use Symfony\Component\Messenger\Event\AbstractWorkerMessageEvent;
+use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
+use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
+use Symfony\Component\Messenger\Event\WorkerRunningEvent;
+
+/**
+ * @author Grégoire Pineau
+ */
+class ResetServicesListener implements EventSubscriberInterface
+{
+ private $servicesResetter;
+ private $receiversName;
+
+ public function __construct(ServicesResetter $servicesResetter, array $receiversName)
+ {
+ $this->servicesResetter = $servicesResetter;
+ $this->receiversName = $receiversName;
+ }
+
+ public function resetServices(AbstractWorkerMessageEvent $event)
+ {
+ if (!\in_array($event->getReceiverName(), $this->receiversName, true)) {
+ return;
+ }
+
+ $this->servicesResetter->reset();
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ WorkerMessageHandledEvent::class => ['resetServices'],
+ WorkerMessageFailedEvent::class => ['resetServices'],
+ WorkerRunningEvent::class => ['resetServices'],
+ ];
+ }
+}
diff --git a/src/Symfony/Component/Messenger/Tests/EventListener/ResetServicesListenerTest.php b/src/Symfony/Component/Messenger/Tests/EventListener/ResetServicesListenerTest.php
new file mode 100644
index 0000000000000..a14fe113cfde3
--- /dev/null
+++ b/src/Symfony/Component/Messenger/Tests/EventListener/ResetServicesListenerTest.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\Tests\EventListener;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
+use Symfony\Component\Messenger\Envelope;
+use Symfony\Component\Messenger\Event\AbstractWorkerMessageEvent;
+use Symfony\Component\Messenger\EventListener\ResetServicesListener;
+
+class ResetServicesListenerTest extends TestCase
+{
+ public function provideTests(): iterable
+ {
+ yield ['foo', true];
+ yield ['bar', false];
+ }
+
+ /** @dataProvider provideTests */
+ public function test(string $receiverName, bool $shouldReset)
+ {
+ $servicesResetter = $this->createMock(ServicesResetter::class);
+ $servicesResetter->expects($shouldReset ? $this->once() : $this->never())->method('reset');
+
+ $event = new class(new Envelope(new \stdClass()), $receiverName) extends AbstractWorkerMessageEvent {};
+
+ $resetListener = new ResetServicesListener($servicesResetter, ['foo']);
+ $resetListener->resetServices($event);
+ }
+}