From 1e7af4d35e0404fdaacd788399e1a57eccf15000 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 28 Oct 2018 13:15:46 +0100 Subject: [PATCH] [Messenger] make senders and handlers subscribing to parent interfaces receive *all* matching messages, wildcard included --- .../Controller/ControllerTrait.php | 5 +- .../Compiler/UnusedTagsPass.php | 1 - .../DependencyInjection/Configuration.php | 2 +- .../FrameworkExtension.php | 56 +++---- .../Resources/config/messenger.xml | 17 +-- .../Fixtures/php/messenger_multiple_buses.php | 4 +- .../Fixtures/xml/messenger_multiple_buses.xml | 4 +- .../Fixtures/yml/messenger_multiple_buses.yml | 4 +- .../FrameworkExtensionTest.php | 31 ++-- src/Symfony/Component/Messenger/CHANGELOG.md | 18 +-- .../DependencyInjection/MessengerPass.php | 122 +++++---------- src/Symfony/Component/Messenger/Envelope.php | 5 + .../Messenger/Handler/ChainHandler.php | 48 ------ .../Messenger/Handler/HandlersLocator.php | 63 ++++++++ .../Handler/HandlersLocatorInterface.php | 29 ++++ .../Locator/AbstractHandlerLocator.php | 50 ------ .../Locator/ContainerHandlerLocator.php | 38 ----- .../Handler/Locator/HandlerLocator.php | 38 ----- .../Locator/HandlerLocatorInterface.php | 27 ---- .../Middleware/HandleMessageMiddleware.php | 20 ++- .../Middleware/LoggingMiddleware.php | 8 +- .../Middleware/SendMessageMiddleware.php | 29 ++-- .../DependencyInjection/MessengerPassTest.php | 142 ++++++------------ .../Tests/Handler/ChainHandlerTest.php | 48 ------ .../Locator/ContainerHandlerLocatorTest.php | 59 -------- .../HandleMessageMiddlewareTest.php | 10 +- .../Middleware/SendMessageMiddlewareTest.php | 43 ++---- .../AmqpExt/Fixtures/long_receiver.php | 2 +- .../Locator/ContainerSenderLocatorTest.php | 92 ------------ .../Sender/Locator/SenderLocatorTest.php | 44 ------ .../Transport/Sender/SendersLocatorTest.php | 33 ++++ .../Transport/Sender/ChainSender.php | 44 ------ .../Sender/Locator/AbstractSenderLocator.php | 45 ------ .../Sender/Locator/ContainerSenderLocator.php | 43 ------ .../Sender/Locator/SenderLocator.php | 48 ------ .../Sender/Locator/SenderLocatorInterface.php | 29 ---- .../Transport/Sender/SendersLocator.php | 60 ++++++++ .../Sender/SendersLocatorInterface.php | 35 +++++ 38 files changed, 419 insertions(+), 977 deletions(-) delete mode 100644 src/Symfony/Component/Messenger/Handler/ChainHandler.php create mode 100644 src/Symfony/Component/Messenger/Handler/HandlersLocator.php create mode 100644 src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php delete mode 100644 src/Symfony/Component/Messenger/Handler/Locator/AbstractHandlerLocator.php delete mode 100644 src/Symfony/Component/Messenger/Handler/Locator/ContainerHandlerLocator.php delete mode 100644 src/Symfony/Component/Messenger/Handler/Locator/HandlerLocator.php delete mode 100644 src/Symfony/Component/Messenger/Handler/Locator/HandlerLocatorInterface.php delete mode 100644 src/Symfony/Component/Messenger/Tests/Handler/ChainHandlerTest.php delete mode 100644 src/Symfony/Component/Messenger/Tests/Handler/Locator/ContainerHandlerLocatorTest.php delete mode 100644 src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/ContainerSenderLocatorTest.php delete mode 100644 src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/SenderLocatorTest.php create mode 100644 src/Symfony/Component/Messenger/Tests/Transport/Sender/SendersLocatorTest.php delete mode 100644 src/Symfony/Component/Messenger/Transport/Sender/ChainSender.php delete mode 100644 src/Symfony/Component/Messenger/Transport/Sender/Locator/AbstractSenderLocator.php delete mode 100644 src/Symfony/Component/Messenger/Transport/Sender/Locator/ContainerSenderLocator.php delete mode 100644 src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocator.php delete mode 100644 src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocatorInterface.php create mode 100644 src/Symfony/Component/Messenger/Transport/Sender/SendersLocator.php create mode 100644 src/Symfony/Component/Messenger/Transport/Sender/SendersLocatorInterface.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php index 2ae669b102611..451b83931a843 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php @@ -391,14 +391,15 @@ protected function isCsrfTokenValid(string $id, ?string $token): bool /** * Dispatches a message to the bus. * - * @param object $message The message to dispatch + * @param object|Envelope $message The message or the message pre-wrapped in an envelope * * @final */ protected function dispatchMessage($message): Envelope { if (!$this->container->has('message_bus')) { - throw new \LogicException('The message bus is not enabled in your application. Try running "composer require symfony/messenger".'); + $message = class_exists(Envelope::class) ? 'You need to define the "messenger.default_bus" configuration option.' : 'Try running "composer require symfony/messenger".'; + throw new \LogicException('The message bus is not enabled in your application. '.$message); } return $this->container->get('message_bus')->dispatch($message); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index 00328b4b5fbee..d9e7f866cd56e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -40,7 +40,6 @@ class UnusedTagsPass implements CompilerPassInterface 'kernel.event_subscriber', 'kernel.fragment_renderer', 'messenger.bus', - 'messenger.sender', 'messenger.receiver', 'messenger.message_handler', 'monolog.logger', diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 0e1438d19784c..b98f7914be3aa 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1077,7 +1077,7 @@ function ($a) { ->end() ->end() ->end() - ->scalarNode('default_bus')->defaultValue(null)->end() + ->scalarNode('default_bus')->defaultNull()->end() ->arrayNode('buses') ->defaultValue(array('messenger.bus.default' => array('default_middleware' => true, 'middleware' => array()))) ->useAttributeAsKey('name') diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 3e17081246b60..29e772db42e67 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -36,6 +36,8 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -68,7 +70,6 @@ use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; -use Symfony\Component\Messenger\Transport\Sender\ChainSender; use Symfony\Component\Messenger\Transport\TransportFactoryInterface; use Symfony\Component\Messenger\Transport\TransportInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -1491,7 +1492,7 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont private function registerMessengerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $serializerConfig, array $validationConfig) { if (!interface_exists(MessageBusInterface::class)) { - throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed.'); + throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".'); } $loader->load('messenger.xml'); @@ -1502,7 +1503,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder } else { if ('messenger.transport.symfony_serializer' === $config['serializer']['id']) { if (!$this->isConfigEnabled($container, $serializerConfig)) { - throw new LogicException('The default Messenger serializer cannot be enabled as the Serializer support is not available. Try enable it or install it by running "composer require symfony/serializer-pack".'); + throw new LogicException('The default 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') @@ -1517,17 +1518,13 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder } } - if (null === $config['default_bus']) { - if (\count($config['buses']) > 1) { - throw new LogicException(sprintf('You need to define a default bus with the "default_bus" configuration. Possible values: %s', implode(', ', array_keys($config['buses'])))); - } - + if (null === $config['default_bus'] && 1 === \count($config['buses'])) { $config['default_bus'] = key($config['buses']); } $defaultMiddleware = array( 'before' => array(array('id' => 'logging')), - 'after' => array(array('id' => 'route_messages'), array('id' => 'call_message_handler')), + 'after' => array(array('id' => 'send_message'), array('id' => 'handle_message')), ); foreach ($config['buses'] as $busId => $bus) { $middleware = $bus['middleware']; @@ -1562,51 +1559,44 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder } } - if (!$container->hasAlias('message_bus')) { - throw new LogicException(sprintf('The default bus named "%s" is not defined. Define it or change the default bus name.', $config['default_bus'])); - } - $senderAliases = array(); 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 enable it or install it by running "composer require symfony/serializer-pack".'); + 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".'); } $transportDefinition = (new Definition(TransportInterface::class)) ->setFactory(array(new Reference('messenger.transport_factory'), 'createTransport')) ->setArguments(array($transport['dsn'], $transport['options'])) ->addTag('messenger.receiver', array('alias' => $name)) - ->addTag('messenger.sender', array('alias' => $name)) ; $container->setDefinition($transportId = 'messenger.transport.'.$name, $transportDefinition); $senderAliases[$name] = $transportId; } - $messageToSenderIdMapping = array(); - $messageToSendAndHandleMapping = array(); + $messageToSendersMapping = array(); + $messagesToSendAndHandle = array(); foreach ($config['routing'] as $message => $messageConfiguration) { if ('*' !== $message && !class_exists($message) && !interface_exists($message, false)) { - throw new LogicException(sprintf('Messenger routing configuration contains a mistake: message "%s" does not exist. It needs to match an existing class or interface.', $message)); + throw new LogicException(sprintf('Invalid Messenger routing configuration: class or interface "%s" not found.', $message)); } + $senders = array_map(function ($sender) use ($senderAliases) { + return new Reference($senderAliases[$sender] ?? $sender); + }, $messageConfiguration['senders']); - if (1 < \count($messageConfiguration['senders'])) { - $senders = array_map(function ($sender) use ($senderAliases) { - return new Reference($senderAliases[$sender] ?? $sender); - }, $messageConfiguration['senders']); - $chainSenderDefinition = new Definition(ChainSender::class, array($senders)); - $chainSenderDefinition->addTag('messenger.sender'); - $chainSenderId = '.messenger.chain_sender.'.$message; - $container->setDefinition($chainSenderId, $chainSenderDefinition); - $messageToSenderIdMapping[$message] = $chainSenderId; - } else { - $messageToSenderIdMapping[$message] = $messageConfiguration['senders'][0]; - } + $sendersId = 'messenger.senders.'.$message; + $sendersDefinition = $container->register($sendersId, RewindableGenerator::class) + ->setFactory('current') + ->addArgument(array(new IteratorArgument($senders))); + $messageToSendersMapping[$message] = new Reference($sendersId); - $messageToSendAndHandleMapping[$message] = $messageConfiguration['send_and_handle']; + $messagesToSendAndHandle[$message] = $messageConfiguration['send_and_handle']; } - $container->getDefinition('messenger.asynchronous.routing.sender_locator')->replaceArgument(1, $messageToSenderIdMapping); - $container->getDefinition('messenger.middleware.route_messages')->replaceArgument(1, $messageToSendAndHandleMapping); + $container->getDefinition('messenger.senders_locator') + ->replaceArgument(0, $messageToSendersMapping) + ->replaceArgument(1, $messagesToSendAndHandle) + ; } private function registerCacheConfiguration(array $config, ContainerBuilder $container) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml index 09d6189556da1..19b234d591fa7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml @@ -8,13 +8,12 @@ - - - + + + - - - + + @@ -25,7 +24,7 @@ - + @@ -48,10 +47,6 @@ - - - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php index 1e0b402760daa..f500ff9573916 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php @@ -13,8 +13,8 @@ 'messenger.bus.queries' => array( 'default_middleware' => false, 'middleware' => array( - 'route_messages', - 'call_message_handler', + 'send_message', + 'handle_message', ), ), ), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml index e42e4eac30886..db946e45e5ebb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml @@ -18,8 +18,8 @@ - - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml index 23894ac01b441..0e67039733272 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml @@ -9,5 +9,5 @@ framework: messenger.bus.queries: default_middleware: false middleware: - - "route_messages" - - "call_message_handler" + - "send_message" + - "handle_message" diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index f746196d732d8..dd946c472d182 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -544,9 +544,7 @@ public function testMessengerTransports() $container = $this->createContainerFromFile('messenger_transports'); $this->assertTrue($container->hasDefinition('messenger.transport.default')); $this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.receiver')); - $this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.sender')); $this->assertEquals(array(array('alias' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.receiver')); - $this->assertEquals(array(array('alias' => 'default')), $container->getDefinition('messenger.transport.default')->getTag('messenger.sender')); $this->assertTrue($container->hasDefinition('messenger.transport.customised')); $transportFactory = $container->getDefinition('messenger.transport.customised')->getFactory(); @@ -563,28 +561,21 @@ public function testMessengerTransports() public function testMessengerRouting() { $container = $this->createContainerFromFile('messenger_routing'); - $senderLocatorDefinition = $container->getDefinition('messenger.asynchronous.routing.sender_locator'); - $sendMessageMiddlewareDefinition = $container->getDefinition('messenger.middleware.route_messages'); + $senderLocatorDefinition = $container->getDefinition('messenger.senders_locator'); - $messageToSenderIdsMapping = array( - DummyMessage::class => '.messenger.chain_sender.'.DummyMessage::class, - SecondMessage::class => '.messenger.chain_sender.'.SecondMessage::class, - '*' => 'amqp', - ); $messageToSendAndHandleMapping = array( DummyMessage::class => false, SecondMessage::class => true, '*' => false, ); - $this->assertSame($messageToSenderIdsMapping, $senderLocatorDefinition->getArgument(1)); - $this->assertSame($messageToSendAndHandleMapping, $sendMessageMiddlewareDefinition->getArgument(1)); - $this->assertEquals(array(new Reference('messenger.transport.amqp'), new Reference('audit')), $container->getDefinition('.messenger.chain_sender.'.DummyMessage::class)->getArgument(0)); + $this->assertSame($messageToSendAndHandleMapping, $senderLocatorDefinition->getArgument(1)); + $this->assertEquals(array(new Reference('messenger.transport.amqp'), new Reference('audit')), $container->getDefinition('messenger.senders.'.DummyMessage::class)->getArgument(0)[0]->getValues()); } /** * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException - * @expectedExceptionMessage The default Messenger serializer cannot be enabled as the Serializer support is not available. Try enable it or install it by running "composer require symfony/serializer-pack". + * @expectedExceptionMessage The default 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() { @@ -593,7 +584,7 @@ public function testMessengerTransportConfigurationWithoutSerializer() /** * @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 enable it or install it by running "composer require symfony/serializer-pack". + * @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() { @@ -619,22 +610,22 @@ public function testMessengerWithMultipleBuses() $this->assertSame(array(), $container->getDefinition('messenger.bus.commands')->getArgument(0)); $this->assertEquals(array( array('id' => 'logging'), - array('id' => 'route_messages'), - array('id' => 'call_message_handler'), + array('id' => 'send_message'), + array('id' => 'handle_message'), ), $container->getParameter('messenger.bus.commands.middleware')); $this->assertTrue($container->has('messenger.bus.events')); $this->assertSame(array(), $container->getDefinition('messenger.bus.events')->getArgument(0)); $this->assertEquals(array( array('id' => 'logging'), array('id' => 'with_factory', 'arguments' => array('foo', true, array('bar' => 'baz'))), - array('id' => 'route_messages'), - array('id' => 'call_message_handler'), + array('id' => 'send_message'), + array('id' => 'handle_message'), ), $container->getParameter('messenger.bus.events.middleware')); $this->assertTrue($container->has('messenger.bus.queries')); $this->assertSame(array(), $container->getDefinition('messenger.bus.queries')->getArgument(0)); $this->assertEquals(array( - array('id' => 'route_messages', 'arguments' => array()), - array('id' => 'call_message_handler', 'arguments' => array()), + array('id' => 'send_message', 'arguments' => array()), + array('id' => 'handle_message', 'arguments' => array()), ), $container->getParameter('messenger.bus.queries.middleware')); $this->assertTrue($container->hasAlias('message_bus')); diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index 366d0f519651a..eeb44795eacc0 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -6,38 +6,34 @@ CHANGELOG * The component is not experimental anymore * All the changes below are BC BREAKS + * Senders and handlers subscribing to parent interfaces now receive *all* matching messages, wildcard included * `MessageBusInterface::dispatch()`, `MiddlewareInterface::handle()` and `SenderInterface::send()` return `Envelope` * `MiddlewareInterface::handle()` now require an `Envelope` as first argument and a `StackInterface` as second * `EnvelopeAwareInterface` has been removed * The signature of `Amqp*` classes changed to take a `Connection` as a first argument and an optional `Serializer` as a second argument. - * `SenderLocator` has been renamed to `ContainerSenderLocator` - Be careful as there is still a `SenderLocator` class, but it does not rely on a `ContainerInterface` to find senders. - Instead, it accepts the sender instance itself instead of its identifier in the container. * `MessageSubscriberInterface::getHandledMessages()` return value has changed. The value of an array item needs to be an associative array or the method name. * `StampInterface` replaces `EnvelopeItemInterface` and doesn't extend `Serializable` anymore * The `ConsumeMessagesCommand` class now takes an instance of `Psr\Container\ContainerInterface` as first constructor argument * The `EncoderInterface` and `DecoderInterface` have been replaced by a unified `Symfony\Component\Messenger\Transport\Serialization\SerializerInterface`. - * The locator passed to `ContainerHandlerLocator` should not prefix its keys by "handler." anymore - * The `AbstractHandlerLocator::getHandler()` method uses `?callable` as return type * Renamed `EnvelopeItemInterface` to `StampInterface` * `Envelope`'s constructor and `with()` method now accept `StampInterface` objects as variadic parameters * Renamed and moved `ReceivedMessage`, `ValidationConfiguration` and `SerializerConfiguration` in the `Stamp` namespace - * Removed the `WrapIntoReceivedMessage` - * `SenderLocatorInterface::getSenderForMessage()` has been replaced by `getSender(Envelope $envelope)` + * Removed the `WrapIntoReceivedMessage` class * `MessengerDataCollector::getMessages()` returns an iterable, not just an array anymore - * `AbstractHandlerLocator` is now internal - * `HandlerLocatorInterface::resolve()` has been replaced by `getHandler(Envelope $envelope): ?callable` and shouldn't throw when no handlers are found - * `SenderLocatorInterface::getSenderForMessage()` has been replaced by `getSender(Envelope $envelope)` + * `HandlerLocatorInterface::resolve()` has been removed, use `HandlersLocator::getHandlers()` instead + * `SenderLocatorInterface::getSenderForMessage()` has been removed, use `SendersLocator::getSenders()` instead * Classes in the `Middleware\Enhancers` sub-namespace have been moved to the `Middleware` one * Classes in the `Asynchronous\Routing` sub-namespace have been moved to the `Transport\Sender\Locator` sub-namespace * The `Asynchronous/Middleware/SendMessageMiddleware` class has been moved to the `Middleware` namespace - * `SenderInterface` and `ChainSender` classes have been moved to the `Transport\Sender` sub-namespace + * `SenderInterface` has been moved to the `Transport\Sender` sub-namespace + * The `ChainHandler` and `ChainSender` classes have been removed * `ReceiverInterface` and its implementations have been moved to the `Transport\Receiver` sub-namespace * `ActivationMiddlewareDecorator` has been renamed `ActivationMiddleware` * `AllowNoHandlerMiddleware` has been removed in favor of a new constructor argument on `HandleMessageMiddleware` + * The `ContainerHandlerLocator`, `AbstractHandlerLocator`, `SenderLocator` and `AbstractSenderLocator` classes have been removed 4.1.0 ----- diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index a4d0c7dd7b7aa..a22a085bd0f50 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\DependencyInjection; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; @@ -20,12 +21,10 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\Messenger\Handler\ChainHandler; -use Symfony\Component\Messenger\Handler\Locator\ContainerHandlerLocator; +use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Component\Messenger\TraceableMessageBus; use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; -use Symfony\Component\Messenger\Transport\Sender\SenderInterface; /** * @author Samuel Roze @@ -38,14 +37,12 @@ class MessengerPass implements CompilerPassInterface private $handlerTag; private $busTag; - private $senderTag; private $receiverTag; - public function __construct(string $handlerTag = 'messenger.message_handler', string $busTag = 'messenger.bus', string $senderTag = 'messenger.sender', string $receiverTag = 'messenger.receiver') + public function __construct(string $handlerTag = 'messenger.message_handler', string $busTag = 'messenger.bus', string $receiverTag = 'messenger.receiver') { $this->handlerTag = $handlerTag; $this->busTag = $busTag; - $this->senderTag = $senderTag; $this->receiverTag = $receiverTag; } @@ -54,10 +51,6 @@ public function __construct(string $handlerTag = 'messenger.message_handler', st */ public function process(ContainerBuilder $container) { - if (!$container->has('message_bus')) { - return; - } - $busIds = array(); foreach ($container->findTaggedServiceIds($this->busTag) as $busId => $tags) { $busIds[] = $busId; @@ -72,8 +65,9 @@ public function process(ContainerBuilder $container) } } - $this->registerReceivers($container, $busIds); - $this->registerSenders($container); + if ($container->hasDefinition('messenger.receiver_locator')) { + $this->registerReceivers($container, $busIds); + } $this->registerHandlers($container, $busIds); } @@ -96,45 +90,41 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) $handles = $this->guessHandledClasses($r, $serviceId); } - $priority = $tag['priority'] ?? 0; + $message = null; $handlerBuses = (array) ($tag['bus'] ?? $busIds); - foreach ($handles as $messageClass => $method) { + foreach ($handles as $message => $method) { $buses = $handlerBuses; - if (\is_int($messageClass)) { - $messageClass = $method; + if (\is_int($message)) { + $message = $method; $method = '__invoke'; } - if (\is_array($messageClass)) { - $messagePriority = $messageClass[1]; - $messageClass = $messageClass[0]; + if (\is_array($message)) { + list($message, $priority) = $message; } else { - $messagePriority = $priority; + $priority = $tag['priority'] ?? 0; } if (\is_array($method)) { if (isset($method['bus'])) { if (!\in_array($method['bus'], $busIds)) { - $messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageSubscriberInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method); + $messageLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageSubscriberInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method); - throw new RuntimeException(sprintf('Invalid configuration %s for message "%s": bus "%s" does not exist.', $messageClassLocation, $messageClass, $method['bus'])); + throw new RuntimeException(sprintf('Invalid configuration %s for message "%s": bus "%s" does not exist.', $messageLocation, $message, $method['bus'])); } $buses = array($method['bus']); } - if (isset($method['priority'])) { - $messagePriority = $method['priority']; - } - + $priority = $method['priority'] ?? $priority; $method = $method['method'] ?? '__invoke'; } - if (!\class_exists($messageClass) && !\interface_exists($messageClass, false)) { - $messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageSubscriberInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method); + if ('*' !== $message && !class_exists($message) && !interface_exists($message, false)) { + $messageLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageSubscriberInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method); - throw new RuntimeException(sprintf('Invalid handler service "%s": message class "%s" %s does not exist.', $serviceId, $messageClass, $messageClassLocation)); + throw new RuntimeException(sprintf('Invalid handler service "%s": class or interface "%s" %s not found.', $serviceId, $message, $messageLocation)); } if (!$r->hasMethod($method)) { @@ -144,15 +134,19 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) if ('__invoke' !== $method) { $wrapperDefinition = (new Definition('callable'))->addArgument(array(new Reference($serviceId), $method))->setFactory('Closure::fromCallable'); - $definitions[$definitionId = '.messenger.method_on_object_wrapper.'.ContainerBuilder::hash($messageClass.':'.$messagePriority.':'.$serviceId.':'.$method)] = $wrapperDefinition; + $definitions[$definitionId = '.messenger.method_on_object_wrapper.'.ContainerBuilder::hash($message.':'.$priority.':'.$serviceId.':'.$method)] = $wrapperDefinition; } else { $definitionId = $serviceId; } foreach ($buses as $handlerBus) { - $handlersByBusAndMessage[$handlerBus][$messageClass][$messagePriority][] = $definitionId; + $handlersByBusAndMessage[$handlerBus][$message][$priority][] = $definitionId; } } + + if (null === $message) { + throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::getHandledMessages()" must return one or more messages.', $serviceId, $r->getName())); + } } } @@ -165,29 +159,24 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) $handlersLocatorMappingByBus = array(); foreach ($handlersByBusAndMessage as $bus => $handlersByMessage) { - foreach ($handlersByMessage as $message => $handlersIds) { - if (1 === \count($handlersIds)) { - $handlersLocatorMappingByBus[$bus][$message] = new Reference(current($handlersIds)); - } else { - $chainHandler = new Definition(ChainHandler::class, array(array_map(function (string $handlerId): Reference { - return new Reference($handlerId); - }, $handlersIds))); - $chainHandler->setPrivate(true); - $serviceId = '.messenger.chain_handler.'.ContainerBuilder::hash($bus.$message); - $definitions[$serviceId] = $chainHandler; - $handlersLocatorMappingByBus[$bus][$message] = new Reference($serviceId); - } + foreach ($handlersByMessage as $message => $handlerIds) { + $handlers = array_map(function (string $handlerId) { return new Reference($handlerId); }, $handlerIds); + $handlersId = "messenger.handlers.$bus.$message"; + $definitions[$handlersId] = (new Definition(RewindableGenerator::class)) + ->setFactory('current') + ->addArgument(array($handlers)); + $handlersLocatorMappingByBus[$bus][$message] = new Reference($handlersId); } } $container->addDefinitions($definitions); foreach ($busIds as $bus) { - $container->register($resolverName = "$bus.messenger.handler_resolver", ContainerHandlerLocator::class) - ->setArgument(0, ServiceLocatorTagPass::register($container, $handlersLocatorMappingByBus[$bus] ?? array())) + $container->register($locatorId = $bus.'.messenger.handlers_locator', HandlersLocator::class) + ->setArgument(0, $handlersLocatorMappingByBus[$bus] ?? array()) ; - if ($container->has($callMessageHandlerId = "$bus.middleware.call_message_handler")) { - $container->getDefinition($callMessageHandlerId) - ->replaceArgument(0, new Reference($resolverName)) + if ($container->has($handleMessageId = $bus.'.middleware.handle_message')) { + $container->getDefinition($handleMessageId) + ->replaceArgument(0, new Reference($locatorId)) ; } } @@ -206,11 +195,7 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) private function guessHandledClasses(\ReflectionClass $handlerClass, string $serviceId): iterable { if ($handlerClass->implementsInterface(MessageSubscriberInterface::class)) { - if (!$handledMessages = $handlerClass->getName()::getHandledMessages()) { - throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::getHandledMessages()" must return one or more messages.', $serviceId, $handlerClass->getName())); - } - - return $handledMessages; + return $handlerClass->getName()::getHandledMessages(); } try { @@ -273,27 +258,6 @@ private function registerReceivers(ContainerBuilder $container, array $busIds) $container->getDefinition('messenger.receiver_locator')->replaceArgument(0, $receiverMapping); } - private function registerSenders(ContainerBuilder $container) - { - $senderLocatorMapping = array(); - foreach ($container->findTaggedServiceIds($this->senderTag) as $id => $tags) { - $senderClass = $container->findDefinition($id)->getClass(); - if (!is_subclass_of($senderClass, SenderInterface::class)) { - throw new RuntimeException(sprintf('Invalid sender "%s": class "%s" must implement interface "%s".', $id, $senderClass, SenderInterface::class)); - } - - $senderLocatorMapping[$id] = new Reference($id); - - foreach ($tags as $tag) { - if (isset($tag['alias'])) { - $senderLocatorMapping[$tag['alias']] = $senderLocatorMapping[$id]; - } - } - } - - $container->getDefinition('messenger.sender_locator')->replaceArgument(0, $senderLocatorMapping); - } - private function registerBusToCollector(ContainerBuilder $container, string $busId) { $container->setDefinition( @@ -315,18 +279,12 @@ private function registerBusMiddleware(ContainerBuilder $container, string $busI } if (!$container->has($messengerMiddlewareId)) { - throw new RuntimeException(sprintf('Invalid middleware "%s": define such service to be able to use it.', $id)); + throw new RuntimeException(sprintf('Invalid middleware: service "%s" not found.', $id)); } if (($definition = $container->findDefinition($messengerMiddlewareId))->isAbstract()) { $childDefinition = new ChildDefinition($messengerMiddlewareId); - $count = \count($definition->getArguments()); - foreach (array_values($arguments ?? array()) as $key => $argument) { - // Parent definition can provide default arguments. - // Replace each explicitly or add if not set: - $key < $count ? $childDefinition->replaceArgument($key, $argument) : $childDefinition->addArgument($argument); - } - + $childDefinition->setArguments($arguments); $container->setDefinition($messengerMiddlewareId = $busId.'.middleware.'.$id, $childDefinition); } elseif ($arguments) { throw new RuntimeException(sprintf('Invalid middleware factory "%s": a middleware factory must be an abstract definition.', $id)); diff --git a/src/Symfony/Component/Messenger/Envelope.php b/src/Symfony/Component/Messenger/Envelope.php index 71a117c5bf6d1..c43f23f417845 100644 --- a/src/Symfony/Component/Messenger/Envelope.php +++ b/src/Symfony/Component/Messenger/Envelope.php @@ -74,4 +74,9 @@ public function getMessage() { return $this->message; } + + public function getMessageName(): string + { + return \get_class($this->message); + } } diff --git a/src/Symfony/Component/Messenger/Handler/ChainHandler.php b/src/Symfony/Component/Messenger/Handler/ChainHandler.php deleted file mode 100644 index 00e2458255dc0..0000000000000 --- a/src/Symfony/Component/Messenger/Handler/ChainHandler.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Handler; - -use Symfony\Component\Messenger\Exception\InvalidArgumentException; - -/** - * Represents a collection of message handlers. - * - * @author Samuel Roze - * - * @experimental in 4.2 - */ -class ChainHandler -{ - /** - * @var callable[] - */ - private $handlers; - - /** - * @param callable[] $handlers - */ - public function __construct(array $handlers) - { - if (!$handlers) { - throw new InvalidArgumentException('A collection of message handlers requires at least one handler.'); - } - - $this->handlers = $handlers; - } - - public function __invoke($message) - { - foreach ($this->handlers as $handler) { - $handler($message); - } - } -} diff --git a/src/Symfony/Component/Messenger/Handler/HandlersLocator.php b/src/Symfony/Component/Messenger/Handler/HandlersLocator.php new file mode 100644 index 0000000000000..ffed08d7d2a08 --- /dev/null +++ b/src/Symfony/Component/Messenger/Handler/HandlersLocator.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Handler; + +/** + * Maps a message to a list of handlers. + * + * @author Nicolas Grekas + * + * @experimental in 4.2 + */ +class HandlersLocator implements HandlersLocatorInterface +{ + private $handlers; + + /** + * @param callable[][] $handlers + */ + public function __construct(array $handlers) + { + $this->handlers = $handlers; + } + + /** + * {@inheritdoc} + */ + public function getHandlers(string $name): iterable + { + $seen = array(); + + foreach (self::listTypes($name) as $type) { + foreach ($this->handlers[$type] ?? array() as $handler) { + if (!\in_array($handler, $seen, true)) { + yield $seen[] = $handler; + } + } + } + } + + /** + * @internal + */ + public static function listTypes(string $class): array + { + if (!class_exists($class, false)) { + return array($class => $class, '*' => '*'); + } + + return array($class => $class) + + class_parents($class) + + class_implements($class) + + array('*' => '*'); + } +} diff --git a/src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php b/src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php new file mode 100644 index 0000000000000..4f9b8e2bd45ae --- /dev/null +++ b/src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Handler; + +/** + * Maps a message to a list of handlers. + * + * @author Nicolas Grekas + * + * @experimental in 4.2 + */ +interface HandlersLocatorInterface +{ + /** + * Returns the handlers for the given message name. + * + * @return iterable|callable[] + */ + public function getHandlers(string $name): iterable; +} diff --git a/src/Symfony/Component/Messenger/Handler/Locator/AbstractHandlerLocator.php b/src/Symfony/Component/Messenger/Handler/Locator/AbstractHandlerLocator.php deleted file mode 100644 index 0a96037e021f1..0000000000000 --- a/src/Symfony/Component/Messenger/Handler/Locator/AbstractHandlerLocator.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Handler\Locator; - -use Symfony\Component\Messenger\Envelope; - -/** - * @author Miha Vrhovnik - * @author Samuel Roze - * - * @internal - * - * @experimental in 4.2 - */ -abstract class AbstractHandlerLocator implements HandlerLocatorInterface -{ - public function getHandler(Envelope $envelope): ?callable - { - $class = \get_class($envelope->getMessage()); - - if ($handler = $this->getHandlerByName($class)) { - return $handler; - } - - foreach (class_parents($class) as $name) { - if ($handler = $this->getHandlerByName($name)) { - return $handler; - } - } - - foreach (class_implements($class) as $name) { - if ($handler = $this->getHandlerByName($name)) { - return $handler; - } - } - - return null; - } - - abstract protected function getHandlerByName(string $name): ?callable; -} diff --git a/src/Symfony/Component/Messenger/Handler/Locator/ContainerHandlerLocator.php b/src/Symfony/Component/Messenger/Handler/Locator/ContainerHandlerLocator.php deleted file mode 100644 index d94bbc482cc77..0000000000000 --- a/src/Symfony/Component/Messenger/Handler/Locator/ContainerHandlerLocator.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Handler\Locator; - -use Psr\Container\ContainerInterface; - -/** - * @author Miha Vrhovnik - * @author Samuel Roze - * - * @experimental in 4.2 - */ -class ContainerHandlerLocator extends AbstractHandlerLocator -{ - private $container; - - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - protected function getHandlerByName(string $name): ?callable - { - return $this->container->has($name) ? $this->container->get($name) : null; - } -} diff --git a/src/Symfony/Component/Messenger/Handler/Locator/HandlerLocator.php b/src/Symfony/Component/Messenger/Handler/Locator/HandlerLocator.php deleted file mode 100644 index 59ae29a4c4e78..0000000000000 --- a/src/Symfony/Component/Messenger/Handler/Locator/HandlerLocator.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Handler\Locator; - -/** - * @author Samuel Roze - * - * @experimental in 4.2 - */ -class HandlerLocator extends AbstractHandlerLocator -{ - /** - * Maps a message (its class) to a given handler. - */ - private $messageToHandlerMapping; - - public function __construct(array $messageToHandlerMapping = array()) - { - $this->messageToHandlerMapping = $messageToHandlerMapping; - } - - /** - * {@inheritdoc} - */ - protected function getHandlerByName(string $name): ?callable - { - return $this->messageToHandlerMapping[$name] ?? null; - } -} diff --git a/src/Symfony/Component/Messenger/Handler/Locator/HandlerLocatorInterface.php b/src/Symfony/Component/Messenger/Handler/Locator/HandlerLocatorInterface.php deleted file mode 100644 index e902d39079e80..0000000000000 --- a/src/Symfony/Component/Messenger/Handler/Locator/HandlerLocatorInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Handler\Locator; - -use Symfony\Component\Messenger\Envelope; - -/** - * @author Samuel Roze - * - * @experimental in 4.2 - */ -interface HandlerLocatorInterface -{ - /** - * Returns the handler for the given message. - */ - public function getHandler(Envelope $envelope): ?callable; -} diff --git a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php index 510631c466cd2..8096020c15c50 100644 --- a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php @@ -13,7 +13,7 @@ use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Exception\NoHandlerForMessageException; -use Symfony\Component\Messenger\Handler\Locator\HandlerLocatorInterface; +use Symfony\Component\Messenger\Handler\HandlersLocatorInterface; /** * @author Samuel Roze @@ -22,12 +22,12 @@ */ class HandleMessageMiddleware implements MiddlewareInterface { - private $messageHandlerLocator; + private $handlersLocator; private $allowNoHandlers; - public function __construct(HandlerLocatorInterface $messageHandlerLocator, bool $allowNoHandlers = false) + public function __construct(HandlersLocatorInterface $handlersLocator, bool $allowNoHandlers = false) { - $this->messageHandlerLocator = $messageHandlerLocator; + $this->handlersLocator = $handlersLocator; $this->allowNoHandlers = $allowNoHandlers; } @@ -38,10 +38,14 @@ public function __construct(HandlerLocatorInterface $messageHandlerLocator, bool */ public function handle(Envelope $envelope, StackInterface $stack): Envelope { - if (null !== $handler = $this->messageHandlerLocator->getHandler($envelope)) { - $handler($envelope->getMessage()); - } elseif (!$this->allowNoHandlers) { - throw new NoHandlerForMessageException(sprintf('No handler for message "%s".', \get_class($envelope->getMessage()))); + $handler = null; + $message = $envelope->getMessage(); + $name = $envelope->getMessageName(); + foreach ($this->handlersLocator->getHandlers($name) as $handler) { + $handler($message); + } + if (null === $handler && !$this->allowNoHandlers) { + throw new NoHandlerForMessageException(sprintf('No handler for message "%s".', $name)); } return $stack->next()->handle($envelope, $stack); diff --git a/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php b/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php index 16010b3be546d..37b03ea703246 100644 --- a/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/LoggingMiddleware.php @@ -36,20 +36,20 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope $message = $envelope->getMessage(); $context = array( 'message' => $message, - 'name' => \get_class($message), + 'name' => $envelope->getMessageName(), ); - $this->logger->debug('Starting handling message {name}', $context); + $this->logger->debug('Starting handling message "{name}"', $context); try { $envelope = $stack->next()->handle($envelope, $stack); } catch (\Throwable $e) { $context['exception'] = $e; - $this->logger->warning('An exception occurred while handling message {name}', $context); + $this->logger->warning('An exception occurred while handling message "{name}"', $context); throw $e; } - $this->logger->debug('Finished handling message {name}', $context); + $this->logger->debug('Finished handling message "{name}"', $context); return $envelope; } diff --git a/src/Symfony/Component/Messenger/Middleware/SendMessageMiddleware.php b/src/Symfony/Component/Messenger/Middleware/SendMessageMiddleware.php index 64608d2d5a189..f5e7a90a53fbb 100644 --- a/src/Symfony/Component/Messenger/Middleware/SendMessageMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/SendMessageMiddleware.php @@ -13,8 +13,7 @@ use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Stamp\ReceivedStamp; -use Symfony\Component\Messenger\Transport\Sender\Locator\AbstractSenderLocator; -use Symfony\Component\Messenger\Transport\Sender\Locator\SenderLocatorInterface; +use Symfony\Component\Messenger\Transport\Sender\SendersLocatorInterface; /** * @author Samuel Roze @@ -24,13 +23,11 @@ */ class SendMessageMiddleware implements MiddlewareInterface { - private $senderLocator; - private $messagesToSendAndHandleMapping; + private $sendersLocator; - public function __construct(SenderLocatorInterface $senderLocator, array $messagesToSendAndHandleMapping = array()) + public function __construct(SendersLocatorInterface $sendersLocator) { - $this->senderLocator = $senderLocator; - $this->messagesToSendAndHandleMapping = $messagesToSendAndHandleMapping; + $this->sendersLocator = $sendersLocator; } /** @@ -39,21 +36,21 @@ public function __construct(SenderLocatorInterface $senderLocator, array $messag public function handle(Envelope $envelope, StackInterface $stack): Envelope { if ($envelope->get(ReceivedStamp::class)) { - // It's a received message. Do not send it back: + // it's a received message, do not send it back return $stack->next()->handle($envelope, $stack); } + $handle = false; + $sender = null; - $sender = $this->senderLocator->getSender($envelope); - - if ($sender) { + foreach ($this->sendersLocator->getSenders($envelope->getMessageName(), $handle) as $sender) { $envelope = $sender->send($envelope); + } - if (!AbstractSenderLocator::getValueFromMessageRouting($this->messagesToSendAndHandleMapping, $envelope)) { - // message has no corresponding handler - return $envelope; - } + if (null === $sender || $handle) { + return $stack->next()->handle($envelope, $stack); } - return $stack->next()->handle($envelope, $stack); + // message should only be sent and not be handled by the next middleware + return $envelope; } } diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index 0795b2b179c33..7e89ba0991afd 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Messenger\Tests\DependencyInjection; use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -23,7 +23,7 @@ use Symfony\Component\Messenger\DataCollector\MessengerDataCollector; use Symfony\Component\Messenger\DependencyInjection\MessengerPass; use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Handler\ChainHandler; +use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Component\Messenger\MessageBusInterface; @@ -39,7 +39,6 @@ use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler; use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; use Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver; -use Symfony\Component\Messenger\Transport\AmqpExt\AmqpSender; use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; class MessengerPassTest extends TestCase @@ -64,14 +63,14 @@ public function testProcess() $this->assertFalse($container->hasDefinition('messenger.middleware.debug.logging')); - $handlerLocatorDefinition = $container->getDefinition($container->getDefinition("$busId.messenger.handler_resolver")->getArgument(0)); - $this->assertSame(ServiceLocator::class, $handlerLocatorDefinition->getClass()); + $handlersLocatorDefinition = $container->getDefinition($busId.'.messenger.handlers_locator'); + $this->assertSame(HandlersLocator::class, $handlersLocatorDefinition->getClass()); $this->assertEquals( array( - DummyMessage::class => new ServiceClosureArgument(new Reference(DummyHandler::class)), - SecondMessage::class => new ServiceClosureArgument(new Reference(MissingArgumentTypeHandler::class)), + DummyMessage::class => new Reference("messenger.handlers.$busId.".DummyMessage::class), + SecondMessage::class => new Reference("messenger.handlers.$busId.".SecondMessage::class), ), - $handlerLocatorDefinition->getArgument(0) + $handlersLocatorDefinition->getArgument(0) ); $this->assertEquals( @@ -84,12 +83,12 @@ public function testProcessHandlersByBus() { $container = $this->getContainerBuilder($commandBusId = 'command_bus'); $container->register($queryBusId = 'query_bus', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); - $container->register('messenger.middleware.call_message_handler', HandleMessageMiddleware::class) + $container->register('messenger.middleware.handle_message', HandleMessageMiddleware::class) ->addArgument(null) ->setAbstract(true) ; - $middlewareHandlers = array(array('id' => 'call_message_handler')); + $middlewareHandlers = array(array('id' => 'handle_message')); $container->setParameter($commandBusId.'.middleware', $middlewareHandlers); $container->setParameter($queryBusId.'.middleware', $middlewareHandlers); @@ -104,24 +103,24 @@ public function testProcessHandlersByBus() (new ResolveClassPass())->process($container); (new MessengerPass())->process($container); - $commandBusHandlerLocatorDefinition = $container->getDefinition($container->getDefinition("$commandBusId.messenger.handler_resolver")->getArgument(0)); - $this->assertSame(ServiceLocator::class, $commandBusHandlerLocatorDefinition->getClass()); + $commandBusHandlersLocatorDefinition = $container->getDefinition($commandBusId.'.messenger.handlers_locator'); + $this->assertSame(HandlersLocator::class, $commandBusHandlersLocatorDefinition->getClass()); $this->assertEquals( array( - DummyCommand::class => new ServiceClosureArgument(new Reference(DummyCommandHandler::class)), - MultipleBusesMessage::class => new ServiceClosureArgument(new Reference(MultipleBusesMessageHandler::class)), + MultipleBusesMessage::class => new Reference("messenger.handlers.$commandBusId.".MultipleBusesMessage::class), + DummyCommand::class => new Reference("messenger.handlers.$commandBusId.".DummyCommand::class), ), - $commandBusHandlerLocatorDefinition->getArgument(0) + $commandBusHandlersLocatorDefinition->getArgument(0) ); - $queryBusHandlerLocatorDefinition = $container->getDefinition($container->getDefinition("$queryBusId.messenger.handler_resolver")->getArgument(0)); - $this->assertSame(ServiceLocator::class, $queryBusHandlerLocatorDefinition->getClass()); + $queryBusHandlersLocatorDefinition = $container->getDefinition($queryBusId.'.messenger.handlers_locator'); + $this->assertSame(HandlersLocator::class, $queryBusHandlersLocatorDefinition->getClass()); $this->assertEquals( array( - DummyQuery::class => new ServiceClosureArgument(new Reference(DummyQueryHandler::class)), - MultipleBusesMessage::class => new ServiceClosureArgument(new Reference(MultipleBusesMessageHandler::class)), + DummyQuery::class => new Reference("messenger.handlers.$queryBusId.".DummyQuery::class), + MultipleBusesMessage::class => new Reference("messenger.handlers.$queryBusId.".MultipleBusesMessage::class), ), - $queryBusHandlerLocatorDefinition->getArgument(0) + $queryBusHandlersLocatorDefinition->getArgument(0) ); } @@ -153,18 +152,16 @@ public function testGetClassesFromTheHandlerSubscriberInterface() (new MessengerPass())->process($container); - $handlerLocatorDefinition = $container->getDefinition($container->getDefinition("$busId.messenger.handler_resolver")->getArgument(0)); - $handlerMapping = $handlerLocatorDefinition->getArgument(0); + $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); - $this->assertArrayHasKey(DummyMessage::class, $handlerMapping); - $this->assertEquals(new ServiceClosureArgument(new Reference(HandlerWithMultipleMessages::class)), $handlerMapping[DummyMessage::class]); + $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); + $this->assertEquals(new Reference("messenger.handlers.$busId.".DummyMessage::class), $handlersMapping[DummyMessage::class]); - $this->assertArrayHasKey(SecondMessage::class, $handlerMapping); - $handlerReference = (string) $handlerMapping[SecondMessage::class]->getValues()[0]; - $definition = $container->getDefinition($handlerReference); + $this->assertArrayHasKey(SecondMessage::class, $handlersMapping); + $handlersDefinition = $container->getDefinition($handlersMapping[SecondMessage::class]); - $this->assertSame(ChainHandler::class, $definition->getClass()); - $this->assertEquals(array(new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class)), $definition->getArgument(0)); + $this->assertSame(RewindableGenerator::class, $handlersDefinition->getClass()); + $this->assertEquals(array(new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class)), $handlersDefinition->getArgument(0)[0]); } public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() @@ -181,22 +178,20 @@ public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() (new MessengerPass())->process($container); - $handlerLocatorDefinition = $container->getDefinition($container->getDefinition("$busId.messenger.handler_resolver")->getArgument(0)); - $handlerMapping = $handlerLocatorDefinition->getArgument(0); + $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); - $this->assertArrayHasKey(DummyMessage::class, $handlerMapping); - $this->assertArrayHasKey(SecondMessage::class, $handlerMapping); + $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); + $this->assertArrayHasKey(SecondMessage::class, $handlersMapping); - $dummyHandlerReference = (string) $handlerMapping[DummyMessage::class]->getValues()[0]; + $dummyHandlerReference = $container->getDefinition($handlersMapping[DummyMessage::class])->getArgument(0)[0][0]; $dummyHandlerDefinition = $container->getDefinition($dummyHandlerReference); $this->assertSame('callable', $dummyHandlerDefinition->getClass()); $this->assertEquals(array(new Reference(HandlerMappingMethods::class), 'dummyMethod'), $dummyHandlerDefinition->getArgument(0)); $this->assertSame(array('Closure', 'fromCallable'), $dummyHandlerDefinition->getFactory()); - $secondHandlerReference = (string) $handlerMapping[SecondMessage::class]->getValues()[0]; + $secondHandlerReference = $container->getDefinition($handlersMapping[SecondMessage::class])->getArgument(0)[0][1]; $secondHandlerDefinition = $container->getDefinition($secondHandlerReference); - $this->assertSame(ChainHandler::class, $secondHandlerDefinition->getClass()); - $this->assertEquals(new Reference(PrioritizedHandler::class), $secondHandlerDefinition->getArgument(0)[1]); + $this->assertSame(PrioritizedHandler::class, $secondHandlerDefinition->getClass()); } /** @@ -257,26 +252,6 @@ public function testItRegistersMultipleReceiversAndSetsTheReceiverNamesOnTheComm $this->assertSame(array('message_bus'), $container->getDefinition('console.command.messenger_consume_messages')->getArgument(4)); } - public function testItRegistersSenders() - { - $container = $this->getContainerBuilder(); - $container->register(AmqpSender::class, AmqpSender::class)->addTag('messenger.sender', array('alias' => 'amqp')); - - (new MessengerPass())->process($container); - - $this->assertEquals(array('amqp' => new Reference(AmqpSender::class), AmqpSender::class => new Reference(AmqpSender::class)), $container->getDefinition('messenger.sender_locator')->getArgument(0)); - } - - public function testItRegistersSenderWithoutTagName() - { - $container = $this->getContainerBuilder(); - $container->register(AmqpSender::class, AmqpSender::class)->addTag('messenger.sender'); - - (new MessengerPass())->process($container); - - $this->assertEquals(array(AmqpSender::class => new Reference(AmqpSender::class)), $container->getDefinition('messenger.sender_locator')->getArgument(0)); - } - public function testItShouldNotThrowIfGeneratorIsReturnedInsteadOfArray() { $container = $this->getContainerBuilder($busId = 'message_bus'); @@ -287,15 +262,14 @@ public function testItShouldNotThrowIfGeneratorIsReturnedInsteadOfArray() (new MessengerPass())->process($container); - $handlerLocatorDefinition = $container->getDefinition($container->getDefinition("$busId.messenger.handler_resolver")->getArgument(0)); - $handlerMapping = $handlerLocatorDefinition->getArgument(0); + $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); - $this->assertArrayHasKey(DummyMessage::class, $handlerMapping); - $firstReference = $handlerMapping[DummyMessage::class]->getValues()[0]; + $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); + $firstReference = $container->getDefinition($handlersMapping[DummyMessage::class])->getArgument(0)[0][0]; $this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'dummyMethod'), $container->getDefinition($firstReference)->getArgument(0)); - $this->assertArrayHasKey(SecondMessage::class, $handlerMapping); - $secondReference = $handlerMapping[SecondMessage::class]->getValues()[0]; + $this->assertArrayHasKey(SecondMessage::class, $handlersMapping); + $secondReference = $container->getDefinition($handlersMapping[SecondMessage::class])->getArgument(0)[0][0]; $this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'secondMessage'), $container->getDefinition($secondReference)->getArgument(0)); } @@ -311,18 +285,16 @@ public function testItRegistersHandlersOnDifferentBuses() (new MessengerPass())->process($container); - $eventsHandlerLocatorDefinition = $container->getDefinition($container->getDefinition($eventsBusId.'.messenger.handler_resolver')->getArgument(0)); - $eventsHandlerMapping = $eventsHandlerLocatorDefinition->getArgument(0); + $eventsHandlerMapping = $container->getDefinition($eventsBusId.'.messenger.handlers_locator')->getArgument(0); $this->assertEquals(array(DummyMessage::class), array_keys($eventsHandlerMapping)); - $firstReference = $eventsHandlerMapping[DummyMessage::class]->getValues()[0]; + $firstReference = $container->getDefinition($eventsHandlerMapping[DummyMessage::class])->getArgument(0)[0][0]; $this->assertEquals(array(new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForEvents'), $container->getDefinition($firstReference)->getArgument(0)); - $commandsHandlerLocatorDefinition = $container->getDefinition($container->getDefinition($commandsBusId.'.messenger.handler_resolver')->getArgument(0)); - $commandsHandlerMapping = $commandsHandlerLocatorDefinition->getArgument(0); + $commandsHandlerMapping = $container->getDefinition($commandsBusId.'.messenger.handlers_locator')->getArgument(0); $this->assertEquals(array(DummyMessage::class), array_keys($commandsHandlerMapping)); - $firstReference = $commandsHandlerMapping[DummyMessage::class]->getValues()[0]; + $firstReference = $container->getDefinition($commandsHandlerMapping[DummyMessage::class])->getArgument(0)[0][0]; $this->assertEquals(array(new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForCommands'), $container->getDefinition($firstReference)->getArgument(0)); } @@ -343,20 +315,7 @@ public function testItThrowsAnExceptionOnUnknownBus() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Invalid sender "app.messenger.sender": class "Symfony\Component\Messenger\Tests\DependencyInjection\InvalidSender" must implement interface "Symfony\Component\Messenger\Transport\Sender\SenderInterface". - */ - public function testItDoesNotRegisterInvalidSender() - { - $container = $this->getContainerBuilder(); - $container->register('app.messenger.sender', InvalidSender::class) - ->addTag('messenger.sender'); - - (new MessengerPass())->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler": message class "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" used as argument type in method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler::__invoke()" does not exist. + * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler": class or interface "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" used as argument type in method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler::__invoke()" not found. */ public function testUndefinedMessageClassForHandler() { @@ -371,7 +330,7 @@ public function testUndefinedMessageClassForHandler() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaHandlerInterface": message class "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" used as argument type in method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaHandlerInterface::__invoke()" does not exist. + * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaHandlerInterface": class or interface "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" used as argument type in method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaHandlerInterface::__invoke()" not found. */ public function testUndefinedMessageClassForHandlerImplementingMessageHandlerInterface() { @@ -386,7 +345,7 @@ public function testUndefinedMessageClassForHandlerImplementingMessageHandlerInt /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaSubscriberInterface": message class "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" returned by method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaSubscriberInterface::getHandledMessages()" does not exist. + * @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaSubscriberInterface": class or interface "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" returned by method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandlerViaSubscriberInterface::getHandledMessages()" not found. */ public function testUndefinedMessageClassForHandlerImplementingMessageSubscriberInterface() { @@ -498,7 +457,7 @@ public function testRegistersMiddlewareFromServices() $container->setParameter($middlewareParameter = $fooBusId.'.middleware', array( array('id' => UselessMiddleware::class), - array('id' => 'middleware_with_factory', 'arguments' => array('foo', 'bar')), + array('id' => 'middleware_with_factory', 'arguments' => array('index_0' => 'foo', 'bar')), array('id' => 'middleware_with_factory_using_default'), )); @@ -529,7 +488,7 @@ public function testRegistersMiddlewareFromServices() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Invalid middleware "not_defined_middleware": define such service to be able to use it. + * @expectedExceptionMessage Invalid middleware: service "not_defined_middleware" not found. */ public function testCannotRegistersAnUndefinedMiddleware() { @@ -561,14 +520,14 @@ public function testItRegistersTheDebugCommand() $container = $this->getContainerBuilder($commandBusId = 'command_bus'); $container->register($queryBusId = 'query_bus', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); $container->register($emptyBus = 'empty_bus', MessageBusInterface::class)->setArgument(0, array())->addTag('messenger.bus'); - $container->register('messenger.middleware.call_message_handler', HandleMessageMiddleware::class) + $container->register('messenger.middleware.handle_message', HandleMessageMiddleware::class) ->addArgument(null) ->setAbstract(true) ; $container->register('console.command.messenger_debug', DebugCommand::class)->addArgument(array()); - $middlewareHandlers = array(array('id' => 'call_message_handler')); + $middlewareHandlers = array(array('id' => 'handle_message')); $container->setParameter($commandBusId.'.middleware', $middlewareHandlers); $container->setParameter($queryBusId.'.middleware', $middlewareHandlers); @@ -606,11 +565,6 @@ private function getContainerBuilder(string $busId = 'message_bus'): ContainerBu $container->setAlias('message_bus', $busId); } - $container - ->register('messenger.sender_locator', ServiceLocator::class) - ->addArgument(new Reference('service_container')) - ; - $container->register('messenger.receiver_locator', ServiceLocator::class) ->addArgument(new Reference('service_container')) ; diff --git a/src/Symfony/Component/Messenger/Tests/Handler/ChainHandlerTest.php b/src/Symfony/Component/Messenger/Tests/Handler/ChainHandlerTest.php deleted file mode 100644 index 5dc293e7e26b4..0000000000000 --- a/src/Symfony/Component/Messenger/Tests/Handler/ChainHandlerTest.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Tests\Handler; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Messenger\Handler\ChainHandler; -use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; - -class ChainHandlerTest extends TestCase -{ - public function testItCallsTheHandlers() - { - $message = new DummyMessage('Hey'); - - $handler1 = $this->createPartialMock(\stdClass::class, array('__invoke')); - $handler1 - ->expects($this->once()) - ->method('__invoke') - ->with($message) - ; - $handler2 = $this->createPartialMock(\stdClass::class, array('__invoke')); - $handler2 - ->expects($this->once()) - ->method('__invoke') - ->with($message) - ; - - (new ChainHandler(array($handler1, $handler2)))($message); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage A collection of message handlers requires at least one handler. - */ - public function testInvalidArgumentExceptionOnEmptyHandlers() - { - new ChainHandler(array()); - } -} diff --git a/src/Symfony/Component/Messenger/Tests/Handler/Locator/ContainerHandlerLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Handler/Locator/ContainerHandlerLocatorTest.php deleted file mode 100644 index e9147e50b7b57..0000000000000 --- a/src/Symfony/Component/Messenger/Tests/Handler/Locator/ContainerHandlerLocatorTest.php +++ /dev/null @@ -1,59 +0,0 @@ -set(DummyMessage::class, $handler); - - $locator = new ContainerHandlerLocator($container); - $resolvedHandler = $locator->getHandler(new Envelope(new DummyMessage('Hey'))); - - $this->assertSame($handler, $resolvedHandler); - } - - public function testNoHandlersReturnsNull() - { - $locator = new ContainerHandlerLocator(new Container()); - $this->assertNull($locator->getHandler(new Envelope(new DummyMessage('Hey')))); - } - - public function testGetHandlerViaInterface() - { - $handler = function () {}; - - $container = new Container(); - $container->set(DummyMessageInterface::class, $handler); - - $locator = new ContainerHandlerLocator($container); - $resolvedHandler = $locator->getHandler(new Envelope(new DummyMessage('Hey'))); - - $this->assertSame($handler, $resolvedHandler); - } - - public function testGetHandlerViaParentClass() - { - $handler = function () {}; - - $container = new Container(); - $container->set(DummyMessage::class, $handler); - - $locator = new ContainerHandlerLocator($container); - $resolvedHandler = $locator->getHandler(new Envelope(new ChildDummyMessage('Hey'))); - - $this->assertSame($handler, $resolvedHandler); - } -} diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php index b4ea0d5b37d0f..92512c9b8ce99 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Messenger\Tests\Middleware; use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Handler\Locator\HandlerLocator; +use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Middleware\StackMiddleware; use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase; @@ -27,8 +27,8 @@ public function testItCallsTheHandlerAndNextMiddleware() $handler = $this->createPartialMock(\stdClass::class, array('__invoke')); - $middleware = new HandleMessageMiddleware(new HandlerLocator(array( - DummyMessage::class => $handler, + $middleware = new HandleMessageMiddleware(new HandlersLocator(array( + DummyMessage::class => array($handler), ))); $handler->expects($this->once())->method('__invoke')->with($message); @@ -42,14 +42,14 @@ public function testItCallsTheHandlerAndNextMiddleware() */ public function testThrowsNoHandlerException() { - $middleware = new HandleMessageMiddleware(new HandlerLocator(array())); + $middleware = new HandleMessageMiddleware(new HandlersLocator(array())); $middleware->handle(new Envelope(new DummyMessage('Hey')), new StackMiddleware()); } public function testAllowNoHandlers() { - $middleware = new HandleMessageMiddleware(new HandlerLocator(array()), true); + $middleware = new HandleMessageMiddleware(new HandlersLocator(array()), true); $this->assertInstanceOf(Envelope::class, $middleware->handle(new Envelope(new DummyMessage('Hey')), new StackMiddleware())); } diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php index da377dfb950f6..50c14617e87f5 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php @@ -18,8 +18,8 @@ use Symfony\Component\Messenger\Tests\Fixtures\ChildDummyMessage; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessageInterface; -use Symfony\Component\Messenger\Transport\Sender\Locator\SenderLocatorInterface; use Symfony\Component\Messenger\Transport\Sender\SenderInterface; +use Symfony\Component\Messenger\Transport\Sender\SendersLocator; class SendMessageMiddlewareTest extends MiddlewareTestCase { @@ -29,7 +29,7 @@ public function testItSendsTheMessageToAssignedSender() $envelope = new Envelope($message); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender)); + $middleware = new SendMessageMiddleware(new SendersLocator(array(DummyMessage::class => array($sender)))); $sender->expects($this->once())->method('send')->with($envelope)->willReturn($envelope); @@ -38,10 +38,10 @@ public function testItSendsTheMessageToAssignedSender() public function testItSendsTheMessageToAssignedSenderWithPreWrappedMessage() { - $envelope = new Envelope(new DummyMessage('Hey')); + $envelope = new Envelope(new ChildDummyMessage('Hey')); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender)); + $middleware = new SendMessageMiddleware(new SendersLocator(array(DummyMessage::class => array($sender)))); $sender->expects($this->once())->method('send')->with($envelope)->willReturn($envelope); @@ -54,9 +54,9 @@ public function testItAlsoCallsTheNextMiddlewareBasedOnTheMessageClass() $envelope = new Envelope($message); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + $middleware = new SendMessageMiddleware(new SendersLocator(array('*' => array($sender)), array( DummyMessage::class => true, - )); + ))); $sender->expects($this->once())->method('send')->with($envelope)->willReturn($envelope); @@ -69,9 +69,9 @@ public function testItAlsoCallsTheNextMiddlewareBasedOnTheMessageParentClass() $envelope = new Envelope($message); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + $middleware = new SendMessageMiddleware(new SendersLocator(array('*' => array($sender)), array( DummyMessage::class => true, - )); + ))); $sender->expects($this->once())->method('send')->with($envelope)->willReturn($envelope); @@ -84,9 +84,9 @@ public function testItAlsoCallsTheNextMiddlewareBasedOnTheMessageInterface() $envelope = new Envelope($message); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + $middleware = new SendMessageMiddleware(new SendersLocator(array('*' => array($sender)), array( DummyMessageInterface::class => true, - )); + ))); $sender->expects($this->once())->method('send')->with($envelope)->willReturn($envelope); @@ -99,9 +99,9 @@ public function testItAlsoCallsTheNextMiddlewareBasedOnWildcard() $envelope = new Envelope($message); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender), array( + $middleware = new SendMessageMiddleware(new SendersLocator(array('*' => array($sender)), array( '*' => true, - )); + ))); $sender->expects($this->once())->method('send')->with($envelope)->willReturn($envelope); @@ -113,7 +113,7 @@ public function testItCallsTheNextMiddlewareWhenNoSenderForThisMessage() $message = new DummyMessage('Hey'); $envelope = new Envelope($message); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator(null)); + $middleware = new SendMessageMiddleware(new SendersLocator(array())); $middleware->handle($envelope, $this->getStackMock()); } @@ -124,25 +124,10 @@ public function testItSkipsReceivedMessages() $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $middleware = new SendMessageMiddleware(new InMemorySenderLocator($sender)); + $middleware = new SendMessageMiddleware(new SendersLocator(array('*' => array($sender)))); $sender->expects($this->never())->method('send'); $middleware->handle($envelope, $this->getStackMock()); } } - -class InMemorySenderLocator implements SenderLocatorInterface -{ - private $sender; - - public function __construct(?SenderInterface $sender) - { - $this->sender = $sender; - } - - public function getSender(Envelope $envelope): ?SenderInterface - { - return $this->sender; - } -} diff --git a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php index 3f8612262e068..4e6937744d4b4 100644 --- a/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php +++ b/src/Symfony/Component/Messenger/Tests/Transport/AmqpExt/Fixtures/long_receiver.php @@ -32,7 +32,7 @@ $worker = new Worker($receiver, new class() implements MessageBusInterface { public function dispatch($envelope): Envelope { - echo 'Get envelope with message: '.get_class($envelope->getMessage())."\n"; + echo 'Get envelope with message: '.$envelope->getMessageName()."\n"; echo sprintf("with stamps: %s\n", json_encode(array_keys($envelope->all()), JSON_PRETTY_PRINT)); sleep(30); diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/ContainerSenderLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/ContainerSenderLocatorTest.php deleted file mode 100644 index 49525e2ac69f4..0000000000000 --- a/src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/ContainerSenderLocatorTest.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Tests\Transport\Sender\Locator; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Tests\Fixtures\ChildDummyMessage; -use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; -use Symfony\Component\Messenger\Tests\Fixtures\DummyMessageInterface; -use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; -use Symfony\Component\Messenger\Transport\Sender\Locator\ContainerSenderLocator; -use Symfony\Component\Messenger\Transport\Sender\SenderInterface; - -class ContainerSenderLocatorTest extends TestCase -{ - public function testItReturnsTheSenderBasedOnTheMessageClass() - { - $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $container = new Container(); - $container->set('my_amqp_sender', $sender); - - $locator = new ContainerSenderLocator($container, array( - DummyMessage::class => 'my_amqp_sender', - )); - - $this->assertSame($sender, $locator->getSender(new Envelope(new DummyMessage('Hello')))); - $this->assertNull($locator->getSender(new Envelope(new SecondMessage()))); - } - - public function testItReturnsTheSenderBasedOnTheMessageParentClass() - { - $container = new Container(); - - $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $container->set('my_amqp_sender', $sender); - - $apiSender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $container->set('my_api_sender', $apiSender); - - $locator = new ContainerSenderLocator($container, array( - DummyMessageInterface::class => 'my_api_sender', - DummyMessage::class => 'my_amqp_sender', - )); - - $this->assertSame($sender, $locator->getSender(new Envelope(new ChildDummyMessage('Hello')))); - $this->assertNull($locator->getSender(new Envelope(new SecondMessage()))); - } - - public function testItReturnsTheSenderBasedOnTheMessageInterface() - { - $container = new Container(); - - $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $container->set('my_amqp_sender', $sender); - - $locator = new ContainerSenderLocator($container, array( - DummyMessageInterface::class => 'my_amqp_sender', - )); - - $this->assertSame($sender, $locator->getSender(new Envelope(new DummyMessage('Hello')))); - $this->assertNull($locator->getSender(new Envelope(new SecondMessage()))); - } - - public function testItSupportsAWildcardInsteadOfTheMessageClass() - { - $container = new Container(); - - $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $container->set('my_amqp_sender', $sender); - - $apiSender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $container->set('my_api_sender', $apiSender); - - $locator = new ContainerSenderLocator($container, array( - DummyMessage::class => 'my_amqp_sender', - '*' => 'my_api_sender', - )); - - $this->assertSame($sender, $locator->getSender(new Envelope(new DummyMessage('Hello')))); - $this->assertSame($apiSender, $locator->getSender(new Envelope(new SecondMessage()))); - } -} diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/SenderLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/SenderLocatorTest.php deleted file mode 100644 index 0063a603b3651..0000000000000 --- a/src/Symfony/Component/Messenger/Tests/Transport/Sender/Locator/SenderLocatorTest.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Tests\Transport\Sender\Locator; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Exception\RuntimeException; -use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; -use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; -use Symfony\Component\Messenger\Transport\Sender\Locator\SenderLocator; -use Symfony\Component\Messenger\Transport\Sender\SenderInterface; - -class SenderLocatorTest extends TestCase -{ - public function testItReturnsTheSenderBasedOnTheMessageClass() - { - $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); - $locator = new SenderLocator(array( - DummyMessage::class => $sender, - )); - - $this->assertSame($sender, $locator->getSender(new Envelope(new DummyMessage('Hello')))); - $this->assertNull($locator->getSender(new Envelope(new SecondMessage()))); - } - - public function testItThrowsExceptionIfConfigurationIsWrong() - { - $locator = new SenderLocator(array( - DummyMessage::class => 'amqp', - )); - - $this->expectException(RuntimeException::class); - $locator->getSender(new Envelope(new DummyMessage('Hello'))); - } -} diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sender/SendersLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sender/SendersLocatorTest.php new file mode 100644 index 0000000000000..504461052d2b9 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Transport/Sender/SendersLocatorTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Tests\Transport\Sender; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Exception\RuntimeException; +use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; +use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; +use Symfony\Component\Messenger\Transport\Sender\SendersLocator; +use Symfony\Component\Messenger\Transport\Sender\SenderInterface; + +class SendersLocatorTest extends TestCase +{ + public function testItReturnsTheSenderBasedOnTheMessageClass() + { + $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); + $locator = new SendersLocator(array( + DummyMessage::class => array($sender), + )); + + $this->assertSame(array($sender), iterator_to_array($locator->getSenders(DummyMessage::class))); + $this->assertSame(array(), iterator_to_array($locator->getSenders(SecondMessage::class))); + } +} diff --git a/src/Symfony/Component/Messenger/Transport/Sender/ChainSender.php b/src/Symfony/Component/Messenger/Transport/Sender/ChainSender.php deleted file mode 100644 index 3db0fac7bf82d..0000000000000 --- a/src/Symfony/Component/Messenger/Transport/Sender/ChainSender.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Transport\Sender; - -use Symfony\Component\Messenger\Envelope; - -/** - * @author Tobias Schultze - * - * @experimental in 4.2 - */ -class ChainSender implements SenderInterface -{ - private $senders; - - /** - * @param SenderInterface[] $senders - */ - public function __construct(iterable $senders) - { - $this->senders = $senders; - } - - /** - * {@inheritdoc} - */ - public function send(Envelope $envelope): Envelope - { - foreach ($this->senders as $sender) { - $envelope = $sender->send($envelope); - } - - return $envelope; - } -} diff --git a/src/Symfony/Component/Messenger/Transport/Sender/Locator/AbstractSenderLocator.php b/src/Symfony/Component/Messenger/Transport/Sender/Locator/AbstractSenderLocator.php deleted file mode 100644 index cb734c8b1db71..0000000000000 --- a/src/Symfony/Component/Messenger/Transport/Sender/Locator/AbstractSenderLocator.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Transport\Sender\Locator; - -use Symfony\Component\Messenger\Envelope; - -/** - * @author Samuel Roze - * - * @internal - * - * @experimental in 4.2 - */ -abstract class AbstractSenderLocator implements SenderLocatorInterface -{ - public static function getValueFromMessageRouting(array $mapping, Envelope $envelope) - { - if (isset($mapping[$class = \get_class($envelope->getMessage())])) { - return $mapping[$class]; - } - - foreach (class_parents($class) as $name) { - if (isset($mapping[$name])) { - return $mapping[$name]; - } - } - - foreach (class_implements($class) as $name) { - if (isset($mapping[$name])) { - return $mapping[$name]; - } - } - - return $mapping['*'] ?? null; - } -} diff --git a/src/Symfony/Component/Messenger/Transport/Sender/Locator/ContainerSenderLocator.php b/src/Symfony/Component/Messenger/Transport/Sender/Locator/ContainerSenderLocator.php deleted file mode 100644 index b5904b3ced7ea..0000000000000 --- a/src/Symfony/Component/Messenger/Transport/Sender/Locator/ContainerSenderLocator.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Transport\Sender\Locator; - -use Psr\Container\ContainerInterface; -use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Transport\Sender\SenderInterface; - -/** - * @author Samuel Roze - * - * @experimental in 4.2 - */ -class ContainerSenderLocator extends AbstractSenderLocator -{ - private $senderServiceLocator; - private $messageToSenderIdMapping; - - public function __construct(ContainerInterface $senderServiceLocator, array $messageToSenderIdMapping) - { - $this->senderServiceLocator = $senderServiceLocator; - $this->messageToSenderIdMapping = $messageToSenderIdMapping; - } - - /** - * {@inheritdoc} - */ - public function getSender(Envelope $envelope): ?SenderInterface - { - $senderId = self::getValueFromMessageRouting($this->messageToSenderIdMapping, $envelope); - - return $senderId ? $this->senderServiceLocator->get($senderId) : null; - } -} diff --git a/src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocator.php b/src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocator.php deleted file mode 100644 index b902756267cd1..0000000000000 --- a/src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocator.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Transport\Sender\Locator; - -use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Exception\RuntimeException; -use Symfony\Component\Messenger\Transport\Sender\SenderInterface; - -/** - * @author Fabien Potencier - * - * @experimental in 4.2 - */ -class SenderLocator extends AbstractSenderLocator -{ - private $messageToSenderMapping; - - public function __construct(array $messageToSenderMapping) - { - $this->messageToSenderMapping = $messageToSenderMapping; - } - - /** - * {@inheritdoc} - */ - public function getSender(Envelope $envelope): ?SenderInterface - { - $sender = self::getValueFromMessageRouting($this->messageToSenderMapping, $envelope); - if (null === $sender) { - return null; - } - - if (!$sender instanceof SenderInterface) { - throw new RuntimeException(sprintf('The sender instance provided for message "%s" should be of type "%s" but got "%s".', \get_class($envelope->getMessage()), SenderInterface::class, \is_object($sender) ? \get_class($sender) : \gettype($sender))); - } - - return $sender; - } -} diff --git a/src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocatorInterface.php b/src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocatorInterface.php deleted file mode 100644 index c60943edf3d7e..0000000000000 --- a/src/Symfony/Component/Messenger/Transport/Sender/Locator/SenderLocatorInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Messenger\Transport\Sender\Locator; - -use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Transport\Sender\SenderInterface; - -/** - * @author Samuel Roze - * @author Tobias Schultze - * - * @experimental in 4.2 - */ -interface SenderLocatorInterface -{ - /** - * Gets the sender (if applicable) for the given message object. - */ - public function getSender(Envelope $envelope): ?SenderInterface; -} diff --git a/src/Symfony/Component/Messenger/Transport/Sender/SendersLocator.php b/src/Symfony/Component/Messenger/Transport/Sender/SendersLocator.php new file mode 100644 index 0000000000000..a3e2d2bad7981 --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/Sender/SendersLocator.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport\Sender; + +use Symfony\Component\Messenger\Exception\RuntimeException; +use Symfony\Component\Messenger\Handler\HandlersLocator; +use Symfony\Component\Messenger\Transport\Sender\SenderInterface; + +/** + * Maps a message to a list of senders. + * + * @author Fabien Potencier + * + * @experimental in 4.2 + */ +class SendersLocator implements SendersLocatorInterface +{ + private $senders; + private $sendAndHandle; + + /** + * @param SenderInterface[][] $senders + * @param bool[] $sendAndHandle + */ + public function __construct(array $senders, array $sendAndHandle = array()) + { + $this->senders = $senders; + $this->sendAndHandle = $sendAndHandle; + } + + /** + * {@inheritdoc} + */ + public function getSenders(string $name, ?bool &$handle = false): iterable + { + $handle = false; + $sender = null; + $seen = array(); + + foreach (HandlersLocator::listTypes($name) as $type) { + foreach ($this->senders[$type] ?? array() as $sender) { + if (!\in_array($sender, $seen, true)) { + yield $seen[] = $sender; + } + } + $handle = $handle ?: $this->sendAndHandle[$type] ?? false; + } + + $handle = $handle || null === $sender; + } +} diff --git a/src/Symfony/Component/Messenger/Transport/Sender/SendersLocatorInterface.php b/src/Symfony/Component/Messenger/Transport/Sender/SendersLocatorInterface.php new file mode 100644 index 0000000000000..d61dea2248b45 --- /dev/null +++ b/src/Symfony/Component/Messenger/Transport/Sender/SendersLocatorInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Transport\Sender; + +use Symfony\Component\Messenger\Transport\Sender\SenderInterface; + +/** + * Maps a message to a list of senders. + * + * @author Samuel Roze + * @author Tobias Schultze + * + * @experimental in 4.2 + */ +interface SendersLocatorInterface +{ + /** + * Gets the senders for the given message name. + * + * @param bool|null &$handle True after calling the method when the next middleware + * should also get the message; false otherwise + * + * @return iterable|SenderInterface[] + */ + public function getSenders(string $name, ?bool &$handle = false): iterable; +}