From f0b2acd67da80b3b1091f2cd0e9ce05cd25d5270 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Sun, 7 Apr 2019 10:12:18 +0200 Subject: [PATCH] Allows to register handlers on a specific transport (and get rid of this handler alias) --- src/Symfony/Component/Messenger/CHANGELOG.md | 6 + .../Messenger/Command/DebugCommand.php | 18 ++- .../DependencyInjection/MessengerPass.php | 49 +++--- .../Component/Messenger/HandleTrait.php | 2 +- .../Messenger/Handler/HandlerDescriptor.php | 82 ++++++++++ .../Messenger/Handler/HandlersLocator.php | 36 ++++- .../Handler/HandlersLocatorInterface.php | 2 +- .../Middleware/HandleMessageMiddleware.php | 19 ++- .../Messenger/Stamp/HandledStamp.php | 52 ++----- .../Messenger/Stamp/ReceivedStamp.php | 11 ++ .../Tests/Command/DebugCommandTest.php | 8 +- .../DependencyInjection/MessengerPassTest.php | 143 +++++++++++++----- .../Messenger/Tests/EnvelopeTest.php | 14 +- .../Messenger/Tests/HandleTraitTest.php | 4 +- .../Tests/Handler/HandleDescriptorTest.php | 46 ++++++ .../Tests/Handler/HandlersLocatorTest.php | 29 +++- .../Messenger/Tests/MessageBusTest.php | 4 +- .../HandleMessageMiddlewareTest.php | 25 ++- .../Middleware/SendMessageMiddlewareTest.php | 2 +- .../Messenger/Tests/RetryIntegrationTest.php | 5 +- .../Tests/Stamp/HandledStampTest.php | 47 +----- .../Component/Messenger/Tests/WorkerTest.php | 15 +- src/Symfony/Component/Messenger/Worker.php | 16 +- 23 files changed, 426 insertions(+), 209 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php create mode 100644 src/Symfony/Component/Messenger/Tests/Handler/HandleDescriptorTest.php diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index cfc4403b90324..969b01fcc3acf 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -81,6 +81,12 @@ CHANGELOG * Added a Doctrine transport. For example, use the `doctrine://default` DSN (this uses the `default` Doctrine entity manager) * [BC BREAK] The `getConnectionConfiguration` method on Amqp's `Connection` has been removed. * [BC BREAK] A `HandlerFailedException` exception will be thrown if one or more handler fails. + * [BC BREAK] The `HandlersLocationInterface::getHandlers` method needs to return `HandlerDescriptor` + instances instead of callables. + * [BC BREAK] The `HandledStamp` stamp has changed: `handlerAlias` has been renamed to `handlerName`, + `getCallableName` has been removed and its constructor only has 2 arguments now. + * [BC BREAK] The `ReceivedStamp` needs to exposes the name of the transport from which the message + has been received. 4.2.0 ----- diff --git a/src/Symfony/Component/Messenger/Command/DebugCommand.php b/src/Symfony/Component/Messenger/Command/DebugCommand.php index b0aba38ea4b7e..592b4572075f8 100644 --- a/src/Symfony/Component/Messenger/Command/DebugCommand.php +++ b/src/Symfony/Component/Messenger/Command/DebugCommand.php @@ -84,7 +84,9 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($handlersByMessage as $message => $handlers) { $tableRows[] = [sprintf('%s', $message)]; foreach ($handlers as $handler) { - $tableRows[] = [sprintf(' handled by %s', $handler)]; + $tableRows[] = [ + sprintf(' handled by %s', $handler[0]).$this->formatConditions($handler[1]), + ]; } } @@ -97,4 +99,18 @@ protected function execute(InputInterface $input, OutputInterface $output) } } } + + private function formatConditions(array $options): string + { + if (!$options) { + return ''; + } + + $optionsMapping = []; + foreach ($options as $key => $value) { + $optionsMapping[] = ' '.$key.'='.$value; + } + + return ' (when'.implode(', ', $optionsMapping).')'; + } } diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 672b1d2d8fd0c..46550f5121497 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Component\Messenger\TraceableMessageBus; @@ -94,32 +95,33 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) $message = null; $handlerBuses = (array) ($tag['bus'] ?? $busIds); - foreach ($handles as $message => $method) { + foreach ($handles as $message => $options) { $buses = $handlerBuses; + if (\is_int($message)) { - $message = $method; - $method = '__invoke'; + if (\is_string($options)) { + $message = $options; + $options = []; + } else { + throw new RuntimeException(sprintf('The handler configuration needs to return an array of messages or an associated array of message and configuration. Found value of type "%s" at position "%d" for service "%s".', \gettype($options), $message, $serviceId)); + } } - if (\is_array($message)) { - list($message, $priority) = $message; - } else { - $priority = $tag['priority'] ?? 0; + if (\is_string($options)) { + $options = ['method' => $options]; } - if (\is_array($method)) { - if (isset($method['bus'])) { - if (!\in_array($method['bus'], $busIds)) { - $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)); + $priority = $tag['priority'] ?? $options['priority'] ?? 0; + $method = $options['method'] ?? '__invoke'; - throw new RuntimeException(sprintf('Invalid configuration %s for message "%s": bus "%s" does not exist.', $messageLocation, $message, $method['bus'])); - } + if (isset($options['bus'])) { + if (!\in_array($options['bus'], $busIds)) { + $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)); - $buses = [$method['bus']]; + throw new RuntimeException(sprintf('Invalid configuration %s for message "%s": bus "%s" does not exist.', $messageLocation, $message, $options['bus'])); } - $priority = $method['priority'] ?? $priority; - $method = $method['method'] ?? '__invoke'; + $buses = [$options['bus']]; } if ('*' !== $message && !class_exists($message) && !interface_exists($message, false)) { @@ -141,7 +143,7 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) } foreach ($buses as $handlerBus) { - $handlersByBusAndMessage[$handlerBus][$message][$priority][] = $definitionId; + $handlersByBusAndMessage[$handlerBus][$message][$priority][] = [$definitionId, $options]; } } @@ -154,15 +156,20 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) foreach ($handlersByBusAndMessage as $bus => $handlersByMessage) { foreach ($handlersByMessage as $message => $handlersByPriority) { krsort($handlersByPriority); - $handlersByBusAndMessage[$bus][$message] = array_unique(array_merge(...$handlersByPriority)); + $handlersByBusAndMessage[$bus][$message] = array_merge(...$handlersByPriority); } } $handlersLocatorMappingByBus = []; foreach ($handlersByBusAndMessage as $bus => $handlersByMessage) { - foreach ($handlersByMessage as $message => $handlerIds) { - $handlers = array_map(function (string $handlerId) { return new Reference($handlerId); }, $handlerIds); - $handlersLocatorMappingByBus[$bus][$message] = new IteratorArgument($handlers); + foreach ($handlersByMessage as $message => $handlers) { + $handlerDescriptors = []; + foreach ($handlers as $handler) { + $definitions[$definitionId = '.messenger.handler_descriptor.'.ContainerBuilder::hash($bus.':'.$message.':'.$handler[0])] = (new Definition(HandlerDescriptor::class))->setArguments([new Reference($handler[0]), $handler[1]]); + $handlerDescriptors[] = new Reference($definitionId); + } + + $handlersLocatorMappingByBus[$bus][$message] = new IteratorArgument($handlerDescriptors); } } $container->addDefinitions($definitions); diff --git a/src/Symfony/Component/Messenger/HandleTrait.php b/src/Symfony/Component/Messenger/HandleTrait.php index 9224d13fd310b..e452b82e24518 100644 --- a/src/Symfony/Component/Messenger/HandleTrait.php +++ b/src/Symfony/Component/Messenger/HandleTrait.php @@ -52,7 +52,7 @@ private function handle($message) if (\count($handledStamps) > 1) { $handlers = implode(', ', array_map(function (HandledStamp $stamp): string { - return sprintf('"%s"', $stamp->getHandlerAlias() ?? $stamp->getCallableName()); + return sprintf('"%s"', $stamp->getHandlerName()); }, $handledStamps)); throw new LogicException(sprintf('Message of type "%s" was handled multiple times. Only one handler is expected when using "%s::%s()", got %d: %s.', \get_class($envelope->getMessage()), \get_class($this), __FUNCTION__, \count($handledStamps), $handlers)); diff --git a/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php b/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php new file mode 100644 index 0000000000000..e02d95ed0faa7 --- /dev/null +++ b/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Handler; + +/** + * Describes a handler and the possible associated options, such as `from_transport`, `bus`, etc. + * + * @author Samuel Roze + * + * @experimental in 4.3 + */ +final class HandlerDescriptor +{ + private $handler; + private $options; + + public function __construct(callable $handler, array $options = []) + { + $this->handler = $handler; + $this->options = $options; + } + + public function getHandler(): callable + { + return $this->handler; + } + + public function getName(): string + { + $name = $this->callableName($this->handler); + $alias = $this->options['alias'] ?? null; + + if (null !== $alias) { + $name .= '@'.$alias; + } + + return $name; + } + + public function getOption(string $option) + { + return $this->options[$option] ?? null; + } + + private function callableName(callable $handler) + { + if (\is_array($handler)) { + if (\is_object($handler[0])) { + return \get_class($handler[0]).'::'.$handler[1]; + } + + return $handler[0].'::'.$handler[1]; + } + + if (\is_string($handler)) { + return $handler; + } + + if ($handler instanceof \Closure) { + $r = new \ReflectionFunction($handler); + if (false !== strpos($r->name, '{closure}')) { + return 'Closure'; + } + if ($class = $r->getClosureScopeClass()) { + return $class->name.'::'.$r->name; + } + + return $r->name; + } + + return \get_class($handler).'::__invoke'; + } +} diff --git a/src/Symfony/Component/Messenger/Handler/HandlersLocator.php b/src/Symfony/Component/Messenger/Handler/HandlersLocator.php index 9094eb3dbc128..fe83e9288e61f 100644 --- a/src/Symfony/Component/Messenger/Handler/HandlersLocator.php +++ b/src/Symfony/Component/Messenger/Handler/HandlersLocator.php @@ -12,11 +12,13 @@ namespace Symfony\Component\Messenger\Handler; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Stamp\ReceivedStamp; /** * Maps a message to a list of handlers. * * @author Nicolas Grekas + * @author Samuel Roze * * @experimental in 4.2 */ @@ -25,7 +27,7 @@ class HandlersLocator implements HandlersLocatorInterface private $handlers; /** - * @param callable[][] $handlers + * @param HandlerDescriptor[][]|callable[][] $handlers */ public function __construct(array $handlers) { @@ -40,10 +42,23 @@ public function getHandlers(Envelope $envelope): iterable $seen = []; foreach (self::listTypes($envelope) as $type) { - foreach ($this->handlers[$type] ?? [] as $alias => $handler) { - if (!\in_array($handler, $seen, true)) { - yield $alias => $seen[] = $handler; + foreach ($this->handlers[$type] ?? [] as $handlerDescriptor) { + if (\is_callable($handlerDescriptor)) { + $handlerDescriptor = new HandlerDescriptor($handlerDescriptor); } + + if (!$this->shouldHandle($envelope, $handlerDescriptor)) { + continue; + } + + $name = $handlerDescriptor->getName(); + if (\in_array($name, $seen)) { + continue; + } + + $seen[] = $name; + + yield $handlerDescriptor; } } } @@ -60,4 +75,17 @@ public static function listTypes(Envelope $envelope): array + class_implements($class) + ['*' => '*']; } + + private function shouldHandle(Envelope $envelope, HandlerDescriptor $handlerDescriptor) + { + if (null === $received = $envelope->last(ReceivedStamp::class)) { + return true; + } + + if (null === $expectedTransport = $handlerDescriptor->getOption('from_transport')) { + return true; + } + + return $received->getTransportName() === $expectedTransport; + } } diff --git a/src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php b/src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php index 48673569a6457..80d7786aee555 100644 --- a/src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php +++ b/src/Symfony/Component/Messenger/Handler/HandlersLocatorInterface.php @@ -25,7 +25,7 @@ interface HandlersLocatorInterface /** * Returns the handlers for the given message name. * - * @return iterable|callable[] Indexed by handler alias if available + * @return iterable|HandlerDescriptor[] Indexed by handler alias if available */ public function getHandlers(Envelope $envelope): iterable; } diff --git a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php index 4fc8f6dfcfdd0..25db71c6f0976 100644 --- a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php @@ -16,6 +16,7 @@ use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\Exception\NoHandlerForMessageException; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Handler\HandlersLocatorInterface; use Symfony\Component\Messenger\Stamp\HandledStamp; @@ -54,17 +55,16 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope ]; $exceptions = []; - foreach ($this->handlersLocator->getHandlers($envelope) as $alias => $handler) { - $alias = \is_string($alias) ? $alias : null; - - if ($this->messageHasAlreadyBeenHandled($envelope, $handler, $alias)) { + foreach ($this->handlersLocator->getHandlers($envelope) as $handlerDescriptor) { + if ($this->messageHasAlreadyBeenHandled($envelope, $handlerDescriptor)) { continue; } try { - $handledStamp = HandledStamp::fromCallable($handler, $handler($message), $alias); + $handler = $handlerDescriptor->getHandler(); + $handledStamp = HandledStamp::fromDescriptor($handlerDescriptor, $handler($message)); $envelope = $envelope->with($handledStamp); - $this->logger->info('Message "{class}" handled by "{handler}"', $context + ['handler' => $handledStamp->getCallableName()]); + $this->logger->info('Message "{class}" handled by "{handler}"', $context + ['handler' => $handledStamp->getHandlerName()]); } catch (\Throwable $e) { $exceptions[] = $e; } @@ -85,12 +85,11 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope return $stack->next()->handle($envelope, $stack); } - private function messageHasAlreadyBeenHandled(Envelope $envelope, callable $handler, ?string $alias): bool + private function messageHasAlreadyBeenHandled(Envelope $envelope, HandlerDescriptor $handlerDescriptor): bool { $some = array_filter($envelope - ->all(HandledStamp::class), function (HandledStamp $stamp) use ($handler, $alias) { - return $stamp->getCallableName() === HandledStamp::getNameFromCallable($handler) && - $stamp->getHandlerAlias() === $alias; + ->all(HandledStamp::class), function (HandledStamp $stamp) use ($handlerDescriptor) { + return $stamp->getHandlerName() === $handlerDescriptor->getName(); }); return \count($some) > 0; diff --git a/src/Symfony/Component/Messenger/Stamp/HandledStamp.php b/src/Symfony/Component/Messenger/Stamp/HandledStamp.php index 491aa64472ebd..c7987866f5458 100644 --- a/src/Symfony/Component/Messenger/Stamp/HandledStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/HandledStamp.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Messenger\Stamp; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; + /** * Stamp identifying a message handled by the `HandleMessageMiddleware` middleware * and storing the handler returned value. @@ -24,54 +26,23 @@ final class HandledStamp implements StampInterface { private $result; - private $callableName; - private $handlerAlias; + private $handlerName; /** * @param mixed $result The returned value of the message handler */ - public function __construct($result, string $callableName, string $handlerAlias = null) + public function __construct($result, string $handlerName) { $this->result = $result; - $this->callableName = $callableName; - $this->handlerAlias = $handlerAlias; + $this->handlerName = $handlerName; } /** * @param mixed $result The returned value of the message handler */ - public static function fromCallable(callable $handler, $result, ?string $handlerAlias = null): self - { - return new self($result, self::getNameFromCallable($handler), $handlerAlias); - } - - public static function getNameFromCallable(callable $handler): string + public static function fromDescriptor(HandlerDescriptor $handler, $result): self { - if (\is_array($handler)) { - if (\is_object($handler[0])) { - return \get_class($handler[0]).'::'.$handler[1]; - } - - return $handler[0].'::'.$handler[1]; - } - - if (\is_string($handler)) { - return $handler; - } - - if ($handler instanceof \Closure) { - $r = new \ReflectionFunction($handler); - if (false !== strpos($r->name, '{closure}')) { - return 'Closure'; - } - if ($class = $r->getClosureScopeClass()) { - return $class->name.'::'.$r->name; - } - - return $r->name; - } - - return \get_class($handler).'::__invoke'; + return new self($result, $handler->getName()); } /** @@ -82,13 +53,8 @@ public function getResult() return $this->result; } - public function getCallableName(): string - { - return $this->callableName; - } - - public function getHandlerAlias(): ?string + public function getHandlerName(): string { - return $this->handlerAlias; + return $this->handlerName; } } diff --git a/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php b/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php index 38eedd3625b2c..07e998f720397 100644 --- a/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php +++ b/src/Symfony/Component/Messenger/Stamp/ReceivedStamp.php @@ -27,4 +27,15 @@ */ final class ReceivedStamp implements StampInterface { + private $transportName; + + public function __construct(string $transportName) + { + $this->transportName = $transportName; + } + + public function getTransportName(): string + { + return $this->transportName; + } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php index 97f2b3e71859a..ed867aa9757e4 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/DebugCommandTest.php @@ -40,12 +40,12 @@ public function testOutput() { $command = new DebugCommand([ 'command_bus' => [ - DummyCommand::class => [DummyCommandHandler::class], - MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], + DummyCommand::class => [[DummyCommandHandler::class, []]], + MultipleBusesMessage::class => [[MultipleBusesMessageHandler::class, []]], ], 'query_bus' => [ - DummyQuery::class => [DummyQueryHandler::class], - MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], + DummyQuery::class => [[DummyQueryHandler::class, []]], + MultipleBusesMessage::class => [[MultipleBusesMessageHandler::class, []]], ], ]); diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index bebb0d9f2a117..77bba7b9be842 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Messenger\Tests\DependencyInjection; use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -66,13 +65,12 @@ public function testProcess() $handlersLocatorDefinition = $container->getDefinition($busId.'.messenger.handlers_locator'); $this->assertSame(HandlersLocator::class, $handlersLocatorDefinition->getClass()); - $this->assertEquals( - [ - DummyMessage::class => new IteratorArgument([new Reference(DummyHandler::class)]), - SecondMessage::class => new IteratorArgument([new Reference(MissingArgumentTypeHandler::class)]), - ], - $handlersLocatorDefinition->getArgument(0) - ); + + $handlerDescriptionMapping = $handlersLocatorDefinition->getArgument(0); + $this->assertCount(2, $handlerDescriptionMapping); + + $this->assertHandlerDescriptor($container, $handlerDescriptionMapping, DummyMessage::class, [DummyHandler::class]); + $this->assertHandlerDescriptor($container, $handlerDescriptionMapping, SecondMessage::class, [MissingArgumentTypeHandler::class]); $this->assertEquals( [DummyReceiver::class => new Reference(DummyReceiver::class)], @@ -106,22 +104,33 @@ public function testProcessHandlersByBus() $commandBusHandlersLocatorDefinition = $container->getDefinition($commandBusId.'.messenger.handlers_locator'); $this->assertSame(HandlersLocator::class, $commandBusHandlersLocatorDefinition->getClass()); - $this->assertEquals( - [ - MultipleBusesMessage::class => new IteratorArgument([new Reference(MultipleBusesMessageHandler::class)]), - DummyCommand::class => new IteratorArgument([new Reference(DummyCommandHandler::class)]), - ], - $commandBusHandlersLocatorDefinition->getArgument(0) + + $this->assertHandlerDescriptor( + $container, + $commandBusHandlersLocatorDefinition->getArgument(0), + MultipleBusesMessage::class, + [MultipleBusesMessageHandler::class] + ); + $this->assertHandlerDescriptor( + $container, + $commandBusHandlersLocatorDefinition->getArgument(0), + DummyCommand::class, + [DummyCommandHandler::class] ); $queryBusHandlersLocatorDefinition = $container->getDefinition($queryBusId.'.messenger.handlers_locator'); $this->assertSame(HandlersLocator::class, $queryBusHandlersLocatorDefinition->getClass()); - $this->assertEquals( - [ - DummyQuery::class => new IteratorArgument([new Reference(DummyQueryHandler::class)]), - MultipleBusesMessage::class => new IteratorArgument([new Reference(MultipleBusesMessageHandler::class)]), - ], - $queryBusHandlersLocatorDefinition->getArgument(0) + $this->assertHandlerDescriptor( + $container, + $queryBusHandlersLocatorDefinition->getArgument(0), + DummyQuery::class, + [DummyQueryHandler::class] + ); + $this->assertHandlerDescriptor( + $container, + $queryBusHandlersLocatorDefinition->getArgument(0), + MultipleBusesMessage::class, + [MultipleBusesMessageHandler::class] ); } @@ -156,10 +165,10 @@ public function testGetClassesFromTheHandlerSubscriberInterface() $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); - $this->assertEquals(new IteratorArgument([new Reference(HandlerWithMultipleMessages::class)]), $handlersMapping[DummyMessage::class]); + $this->assertHandlerDescriptor($container, $handlersMapping, DummyMessage::class, [HandlerWithMultipleMessages::class]); $this->assertArrayHasKey(SecondMessage::class, $handlersMapping); - $this->assertEquals(new IteratorArgument([new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class)]), $handlersMapping[SecondMessage::class]); + $this->assertHandlerDescriptor($container, $handlersMapping, SecondMessage::class, [PrioritizedHandler::class, HandlerWithMultipleMessages::class], [['priority' => 10]]); } public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() @@ -181,13 +190,20 @@ public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); $this->assertArrayHasKey(SecondMessage::class, $handlersMapping); - $dummyHandlerReference = $handlersMapping[DummyMessage::class]->getValues()[0]; + $dummyHandlerDescriptorReference = $handlersMapping[DummyMessage::class]->getValues()[0]; + $dummyHandlerDescriptorDefinition = $container->getDefinition($dummyHandlerDescriptorReference); + + $dummyHandlerReference = $dummyHandlerDescriptorDefinition->getArgument(0); $dummyHandlerDefinition = $container->getDefinition($dummyHandlerReference); + $this->assertSame('callable', $dummyHandlerDefinition->getClass()); $this->assertEquals([new Reference(HandlerMappingMethods::class), 'dummyMethod'], $dummyHandlerDefinition->getArgument(0)); $this->assertSame(['Closure', 'fromCallable'], $dummyHandlerDefinition->getFactory()); - $secondHandlerReference = $handlersMapping[SecondMessage::class]->getValues()[1]; + $secondHandlerDescriptorReference = $handlersMapping[SecondMessage::class]->getValues()[1]; + $secondHandlerDescriptorDefinition = $container->getDefinition($secondHandlerDescriptorReference); + + $secondHandlerReference = $secondHandlerDescriptorDefinition->getArgument(0); $secondHandlerDefinition = $container->getDefinition($secondHandlerReference); $this->assertSame(PrioritizedHandler::class, $secondHandlerDefinition->getClass()); } @@ -294,13 +310,19 @@ public function testItShouldNotThrowIfGeneratorIsReturnedInsteadOfArray() $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); - $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); - $firstReference = $handlersMapping[DummyMessage::class]->getValues()[0]; - $this->assertEquals([new Reference(HandlerWithGenerators::class), 'dummyMethod'], $container->getDefinition($firstReference)->getArgument(0)); + $this->assertHandlerDescriptor( + $container, + $handlersMapping, + DummyMessage::class, + [[HandlerWithGenerators::class, 'dummyMethod']] + ); - $this->assertArrayHasKey(SecondMessage::class, $handlersMapping); - $secondReference = $handlersMapping[SecondMessage::class]->getValues()[0]; - $this->assertEquals([new Reference(HandlerWithGenerators::class), 'secondMessage'], $container->getDefinition($secondReference)->getArgument(0)); + $this->assertHandlerDescriptor( + $container, + $handlersMapping, + SecondMessage::class, + [[HandlerWithGenerators::class, 'secondMessage']] + ); } public function testItRegistersHandlersOnDifferentBuses() @@ -310,22 +332,29 @@ public function testItRegistersHandlersOnDifferentBuses() $container ->register(HandlerOnSpecificBuses::class, HandlerOnSpecificBuses::class) - ->addTag('messenger.message_handler') - ; + ->addTag('messenger.message_handler'); (new MessengerPass())->process($container); $eventsHandlerMapping = $container->getDefinition($eventsBusId.'.messenger.handlers_locator')->getArgument(0); - $this->assertEquals([DummyMessage::class], array_keys($eventsHandlerMapping)); - $firstReference = $eventsHandlerMapping[DummyMessage::class]->getValues()[0]; - $this->assertEquals([new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForEvents'], $container->getDefinition($firstReference)->getArgument(0)); + $this->assertHandlerDescriptor( + $container, + $eventsHandlerMapping, + DummyMessage::class, + [[HandlerOnSpecificBuses::class, 'dummyMethodForEvents']], + [['bus' => 'event_bus']] + ); $commandsHandlerMapping = $container->getDefinition($commandsBusId.'.messenger.handlers_locator')->getArgument(0); - $this->assertEquals([DummyMessage::class], array_keys($commandsHandlerMapping)); - $firstReference = $commandsHandlerMapping[DummyMessage::class]->getValues()[0]; - $this->assertEquals([new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForCommands'], $container->getDefinition($firstReference)->getArgument(0)); + $this->assertHandlerDescriptor( + $container, + $commandsHandlerMapping, + DummyMessage::class, + [[HandlerOnSpecificBuses::class, 'dummyMethodForCommands']], + [['bus' => 'command_bus']] + ); } /** @@ -574,12 +603,12 @@ public function testItRegistersTheDebugCommand() $this->assertEquals([ $commandBusId => [ - DummyCommand::class => [DummyCommandHandler::class], - MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], + DummyCommand::class => [[DummyCommandHandler::class, []]], + MultipleBusesMessage::class => [[MultipleBusesMessageHandler::class, []]], ], $queryBusId => [ - DummyQuery::class => [DummyQueryHandler::class], - MultipleBusesMessage::class => [MultipleBusesMessageHandler::class], + DummyQuery::class => [[DummyQueryHandler::class, []]], + MultipleBusesMessage::class => [[MultipleBusesMessageHandler::class, []]], ], $emptyBus => [], ], $container->getDefinition('console.command.messenger_debug')->getArgument(0)); @@ -601,6 +630,36 @@ private function getContainerBuilder(string $busId = 'message_bus'): ContainerBu return $container; } + + private function assertHandlerDescriptor(ContainerBuilder $container, array $mapping, string $message, array $handlerClasses, array $options = []) + { + $this->assertArrayHasKey($message, $mapping); + $this->assertCount(\count($handlerClasses), $mapping[$message]->getValues()); + + foreach ($handlerClasses as $index => $class) { + $handlerReference = $mapping[$message]->getValues()[$index]; + + if (\is_array($class)) { + $reference = [new Reference($class[0]), $class[1]]; + $options[$index] = array_merge(['method' => $class[1]], $options[$index] ?? []); + } else { + $reference = new Reference($class); + } + + $definitionArguments = $container->getDefinition($handlerReference)->getArguments(); + + if (\is_array($class)) { + $methodDefinition = $container->getDefinition($definitionArguments[0]); + + $this->assertEquals(['Closure', 'fromCallable'], $methodDefinition->getFactory()); + $this->assertEquals([$reference], $methodDefinition->getArguments()); + } else { + $this->assertEquals($reference, $definitionArguments[0]); + } + + $this->assertEquals($options[$index] ?? [], $definitionArguments[1]); + } + } } class DummyHandler diff --git a/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php b/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php index 3fb24ce2e8130..417d3dedc0f39 100644 --- a/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php +++ b/src/Symfony/Component/Messenger/Tests/EnvelopeTest.php @@ -25,7 +25,7 @@ class EnvelopeTest extends TestCase { public function testConstruct() { - $receivedStamp = new ReceivedStamp(); + $receivedStamp = new ReceivedStamp('transport'); $envelope = new Envelope($dummy = new DummyMessage('dummy'), [$receivedStamp]); $this->assertSame($dummy, $envelope->getMessage()); @@ -37,12 +37,12 @@ public function testWithReturnsNewInstance() { $envelope = new Envelope(new DummyMessage('dummy')); - $this->assertNotSame($envelope, $envelope->with(new ReceivedStamp())); + $this->assertNotSame($envelope, $envelope->with(new ReceivedStamp('transport'))); } public function testWithoutAll() { - $envelope = new Envelope(new DummyMessage('dummy'), [new ReceivedStamp(), new ReceivedStamp(), new DelayStamp(5000)]); + $envelope = new Envelope(new DummyMessage('dummy'), [new ReceivedStamp('transport1'), new ReceivedStamp('transport2'), new DelayStamp(5000)]); $envelope = $envelope->withoutAll(ReceivedStamp::class); @@ -52,7 +52,7 @@ public function testWithoutAll() public function testLast() { - $receivedStamp = new ReceivedStamp(); + $receivedStamp = new ReceivedStamp('transport'); $envelope = new Envelope($dummy = new DummyMessage('dummy'), [$receivedStamp]); $this->assertSame($receivedStamp, $envelope->last(ReceivedStamp::class)); @@ -62,7 +62,7 @@ public function testLast() public function testAll() { $envelope = (new Envelope($dummy = new DummyMessage('dummy'))) - ->with($receivedStamp = new ReceivedStamp()) + ->with($receivedStamp = new ReceivedStamp('transport')) ->with($validationStamp = new ValidationStamp(['foo'])) ; @@ -76,7 +76,7 @@ public function testAll() public function testWrapWithMessage() { $message = new \stdClass(); - $stamp = new ReceivedStamp(); + $stamp = new ReceivedStamp('transport'); $envelope = Envelope::wrap($message, [$stamp]); $this->assertSame($message, $envelope->getMessage()); @@ -86,7 +86,7 @@ public function testWrapWithMessage() public function testWrapWithEnvelope() { $envelope = new Envelope(new \stdClass(), [new DelayStamp(5)]); - $envelope = Envelope::wrap($envelope, [new ReceivedStamp()]); + $envelope = Envelope::wrap($envelope, [new ReceivedStamp('transport')]); $this->assertCount(1, $envelope->all(DelayStamp::class)); $this->assertCount(1, $envelope->all(ReceivedStamp::class)); diff --git a/src/Symfony/Component/Messenger/Tests/HandleTraitTest.php b/src/Symfony/Component/Messenger/Tests/HandleTraitTest.php index 30d8b0a9766f6..09442f2a2cb56 100644 --- a/src/Symfony/Component/Messenger/Tests/HandleTraitTest.php +++ b/src/Symfony/Component/Messenger/Tests/HandleTraitTest.php @@ -65,7 +65,7 @@ public function testHandleThrowsOnNoHandledStamp() /** * @expectedException \Symfony\Component\Messenger\Exception\LogicException - * @expectedExceptionMessage Message of type "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage" was handled multiple times. Only one handler is expected when using "Symfony\Component\Messenger\Tests\TestQueryBus::handle()", got 2: "FirstDummyHandler::__invoke", "dummy_2". + * @expectedExceptionMessage Message of type "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage" was handled multiple times. Only one handler is expected when using "Symfony\Component\Messenger\Tests\TestQueryBus::handle()", got 2: "FirstDummyHandler::__invoke", "SecondDummyHandler::__invoke". */ public function testHandleThrowsOnMultipleHandledStamps() { @@ -74,7 +74,7 @@ public function testHandleThrowsOnMultipleHandledStamps() $query = new DummyMessage('Hello'); $bus->expects($this->once())->method('dispatch')->willReturn( - new Envelope($query, [new HandledStamp('first_result', 'FirstDummyHandler::__invoke'), new HandledStamp('second_result', 'SecondDummyHandler::__invoke', 'dummy_2')]) + new Envelope($query, [new HandledStamp('first_result', 'FirstDummyHandler::__invoke'), new HandledStamp('second_result', 'SecondDummyHandler::__invoke')]) ); $queryBus->query($query); diff --git a/src/Symfony/Component/Messenger/Tests/Handler/HandleDescriptorTest.php b/src/Symfony/Component/Messenger/Tests/Handler/HandleDescriptorTest.php new file mode 100644 index 0000000000000..61e080b034ae9 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Handler/HandleDescriptorTest.php @@ -0,0 +1,46 @@ +assertStringMatchesFormat($expectedHandlerString, $descriptor->getName()); + } + + public function provideHandlers() + { + yield [function () {}, 'Closure']; + yield ['var_dump', 'var_dump']; + yield [new DummyCommandHandler(), DummyCommandHandler::class.'::__invoke']; + yield [ + [new DummyCommandHandlerWithSpecificMethod(), 'handle'], + DummyCommandHandlerWithSpecificMethod::class.'::handle', + ]; + yield [\Closure::fromCallable(function () {}), 'Closure']; + yield [\Closure::fromCallable(new DummyCommandHandler()), DummyCommandHandler::class.'::__invoke']; + yield [\Closure::bind(\Closure::fromCallable(function () {}), new \stdClass()), 'Closure']; + yield [new class() { + public function __invoke() + { + } + }, 'class@anonymous%sHandleDescriptorTest.php%s::__invoke']; + } +} + +class DummyCommandHandlerWithSpecificMethod +{ + public function handle(): void + { + } +} diff --git a/src/Symfony/Component/Messenger/Tests/Handler/HandlersLocatorTest.php b/src/Symfony/Component/Messenger/Tests/Handler/HandlersLocatorTest.php index 0c258f395b4c6..48e546afa1a8f 100644 --- a/src/Symfony/Component/Messenger/Tests/Handler/HandlersLocatorTest.php +++ b/src/Symfony/Component/Messenger/Tests/Handler/HandlersLocatorTest.php @@ -13,18 +13,41 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Handler\HandlersLocator; +use Symfony\Component\Messenger\Stamp\ReceivedStamp; use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage; class HandlersLocatorTest extends TestCase { - public function testItYieldsProvidedAliasAsKey() + public function testItYieldsHandlerDescriptors() { $handler = $this->createPartialMock(\stdClass::class, ['__invoke']); $locator = new HandlersLocator([ - DummyMessage::class => ['dummy' => $handler], + DummyMessage::class => [$handler], ]); - $this->assertSame(['dummy' => $handler], iterator_to_array($locator->getHandlers(new Envelope(new DummyMessage('a'))))); + $this->assertEquals([new HandlerDescriptor($handler)], iterator_to_array($locator->getHandlers(new Envelope(new DummyMessage('a'))))); + } + + public function testItReturnsOnlyHandlersMatchingTransport() + { + $firstHandler = $this->createPartialMock(\stdClass::class, ['__invoke']); + $secondHandler = $this->createPartialMock(\stdClass::class, ['__invoke']); + + $locator = new HandlersLocator([ + DummyMessage::class => [ + $first = new HandlerDescriptor($firstHandler, ['alias' => 'one']), + new HandlerDescriptor($this->createPartialMock(\stdClass::class, ['__invoke']), ['from_transport' => 'ignored', 'alias' => 'two']), + $second = new HandlerDescriptor($secondHandler, ['from_transport' => 'transportName', 'alias' => 'three']), + ], + ]); + + $this->assertEquals([ + $first, + $second, + ], iterator_to_array($locator->getHandlers( + new Envelope(new DummyMessage('Body'), [new ReceivedStamp('transportName')]) + ))); } } diff --git a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php index b00e2a693ad5c..8235024bb5246 100644 --- a/src/Symfony/Component/Messenger/Tests/MessageBusTest.php +++ b/src/Symfony/Component/Messenger/Tests/MessageBusTest.php @@ -71,7 +71,7 @@ public function testItCallsMiddleware() public function testThatAMiddlewareCanAddSomeStampsToTheEnvelope() { $message = new DummyMessage('Hello'); - $envelope = new Envelope($message, [new ReceivedStamp()]); + $envelope = new Envelope($message, [new ReceivedStamp('transport')]); $envelopeWithAnotherStamp = $envelope->with(new AnEnvelopeStamp()); $firstMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock(); @@ -109,7 +109,7 @@ public function testThatAMiddlewareCanAddSomeStampsToTheEnvelope() public function testThatAMiddlewareCanUpdateTheMessageWhileKeepingTheEnvelopeStamps() { $message = new DummyMessage('Hello'); - $envelope = new Envelope($message, $stamps = [new ReceivedStamp()]); + $envelope = new Envelope($message, $stamps = [new ReceivedStamp('transport')]); $changedMessage = new DummyMessage('Changed'); $expectedEnvelope = new Envelope($changedMessage, $stamps); diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php index cd15957f1b0f2..3bc263c74bdfc 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php @@ -13,6 +13,7 @@ use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Exception\HandlerFailedException; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Middleware\StackMiddleware; @@ -79,28 +80,26 @@ public function itAddsHandledStampsProvider() ]; yield 'A stamp is added per handler' => [ - ['first' => $first, 'second' => $second], [ - new HandledStamp('first result', $firstClass.'::__invoke', 'first'), - new HandledStamp(null, $secondClass.'::__invoke', 'second'), + new HandlerDescriptor($first, ['alias' => 'first']), + new HandlerDescriptor($second, ['alias' => 'second']), ], - true, - ]; - - yield 'Yielded locator alias is used' => [ - ['first_alias' => $first, $second], [ - new HandledStamp('first result', $firstClass.'::__invoke', 'first_alias'), - new HandledStamp(null, $secondClass.'::__invoke'), + new HandledStamp('first result', $firstClass.'::__invoke@first'), + new HandledStamp(null, $secondClass.'::__invoke@second'), ], true, ]; yield 'It tries all handlers' => [ - ['first' => $first, 'failing' => $failing, 'second' => $second], [ - new HandledStamp('first result', $firstClass.'::__invoke', 'first'), - new HandledStamp(null, $secondClass.'::__invoke', 'second'), + new HandlerDescriptor($first, ['alias' => 'first']), + new HandlerDescriptor($failing, ['alias' => 'failing']), + new HandlerDescriptor($second, ['alias' => 'second']), + ], + [ + new HandledStamp('first result', $firstClass.'::__invoke@first'), + new HandledStamp(null, $secondClass.'::__invoke@second'), ], false, ]; diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php index 7e84f18f8fcf3..a88f93b6a3d25 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/SendMessageMiddlewareTest.php @@ -195,7 +195,7 @@ public function testItCallsTheNextMiddlewareWhenNoSenderForThisMessage() public function testItSkipsReceivedMessages() { - $envelope = (new Envelope(new DummyMessage('Hey')))->with(new ReceivedStamp()); + $envelope = (new Envelope(new DummyMessage('Hey')))->with(new ReceivedStamp('transport')); $sender = $this->getMockBuilder(SenderInterface::class)->getMock(); diff --git a/src/Symfony/Component/Messenger/Tests/RetryIntegrationTest.php b/src/Symfony/Component/Messenger/Tests/RetryIntegrationTest.php index 00346bbc703c2..74eae64afa493 100644 --- a/src/Symfony/Component/Messenger/Tests/RetryIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Tests/RetryIntegrationTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; @@ -45,8 +46,8 @@ public function testRetryMechanism() $throwingHandler = new DummyMessageHandlerFailingFirstTimes(1); $handlerLocator = new HandlersLocator([ DummyMessage::class => [ - 'handler' => $handler, - 'throwing' => $throwingHandler, + new HandlerDescriptor($handler, ['alias' => 'first']), + new HandlerDescriptor($throwingHandler, ['alias' => 'throwing']), ], ]); diff --git a/src/Symfony/Component/Messenger/Tests/Stamp/HandledStampTest.php b/src/Symfony/Component/Messenger/Tests/Stamp/HandledStampTest.php index 2bdc7f4050937..eefa293e64899 100644 --- a/src/Symfony/Component/Messenger/Tests/Stamp/HandledStampTest.php +++ b/src/Symfony/Component/Messenger/Tests/Stamp/HandledStampTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger\Tests\Stamp; use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Stamp\HandledStamp; use Symfony\Component\Messenger\Tests\Fixtures\DummyCommandHandler; @@ -19,54 +20,22 @@ class HandledStampTest extends TestCase { public function testConstruct() { - $stamp = new HandledStamp('some result', 'FooHandler::__invoke()', 'foo'); + $stamp = new HandledStamp('some result', 'FooHandler::__invoke()'); $this->assertSame('some result', $stamp->getResult()); - $this->assertSame('FooHandler::__invoke()', $stamp->getCallableName()); - $this->assertSame('foo', $stamp->getHandlerAlias()); + $this->assertSame('FooHandler::__invoke()', $stamp->getHandlerName()); $stamp = new HandledStamp('some result', 'FooHandler::__invoke()'); $this->assertSame('some result', $stamp->getResult()); - $this->assertSame('FooHandler::__invoke()', $stamp->getCallableName()); - $this->assertNull($stamp->getHandlerAlias()); - } - - /** - * @dataProvider provideCallables - */ - public function testFromCallable(callable $handler, ?string $expectedHandlerString) - { - /** @var HandledStamp $stamp */ - $stamp = HandledStamp::fromCallable($handler, 'some_result', 'alias'); - $this->assertStringMatchesFormat($expectedHandlerString, $stamp->getCallableName()); - $this->assertSame('alias', $stamp->getHandlerAlias(), 'alias is forwarded to construct'); - $this->assertSame('some_result', $stamp->getResult(), 'result is forwarded to construct'); + $this->assertSame('FooHandler::__invoke()', $stamp->getHandlerName()); } - public function provideCallables() + public function testFromDescriptor() { - yield [function () {}, 'Closure']; - yield ['var_dump', 'var_dump']; - yield [new DummyCommandHandler(), DummyCommandHandler::class.'::__invoke']; - yield [ - [new DummyCommandHandlerWithSpecificMethod(), 'handle'], - DummyCommandHandlerWithSpecificMethod::class.'::handle', - ]; - yield [\Closure::fromCallable(function () {}), 'Closure']; - yield [\Closure::fromCallable(new DummyCommandHandler()), DummyCommandHandler::class.'::__invoke']; - yield [\Closure::bind(\Closure::fromCallable(function () {}), new \stdClass()), 'Closure']; - yield [new class() { - public function __invoke() - { - } - }, 'class@anonymous%sHandledStampTest.php%s::__invoke']; - } -} + $stamp = HandledStamp::fromDescriptor(new HandlerDescriptor(new DummyCommandHandler()), 'some_result'); -class DummyCommandHandlerWithSpecificMethod -{ - public function handle(): void - { + $this->assertEquals(DummyCommandHandler::class.'::__invoke', $stamp->getHandlerName()); + $this->assertSame('some_result', $stamp->getResult(), 'result is forwarded to construct'); } } diff --git a/src/Symfony/Component/Messenger/Tests/WorkerTest.php b/src/Symfony/Component/Messenger/Tests/WorkerTest.php index b7eeef5a88346..e899f1cee8b75 100644 --- a/src/Symfony/Component/Messenger/Tests/WorkerTest.php +++ b/src/Symfony/Component/Messenger/Tests/WorkerTest.php @@ -44,10 +44,15 @@ public function testWorkerDispatchTheReceivedMessage() $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); - $bus->expects($this->at(0))->method('dispatch')->with($envelope = new Envelope($apiMessage, [new ReceivedStamp()]))->willReturn($envelope); - $bus->expects($this->at(1))->method('dispatch')->with($envelope = new Envelope($ipaMessage, [new ReceivedStamp()]))->willReturn($envelope); + $bus->expects($this->at(0))->method('dispatch')->with( + $envelope = new Envelope($apiMessage, [new ReceivedStamp('transport')]) + )->willReturn($envelope); - $worker = new Worker([$receiver], $bus); + $bus->expects($this->at(1))->method('dispatch')->with( + $envelope = new Envelope($ipaMessage, [new ReceivedStamp('transport')]) + )->willReturn($envelope); + + $worker = new Worker(['transport' => $receiver], $bus); $worker->run([], function (?Envelope $envelope) use ($worker) { // stop after the messages finish if (null === $envelope) { @@ -62,12 +67,12 @@ public function testWorkerDoesNotWrapMessagesAlreadyWrappedWithReceivedMessage() { $envelope = new Envelope(new DummyMessage('API')); $receiver = new DummyReceiver([[$envelope]]); - $envelope = $envelope->with(new ReceivedStamp()); + $envelope = $envelope->with(new ReceivedStamp('transport')); $bus = $this->getMockBuilder(MessageBusInterface::class)->getMock(); $bus->expects($this->at(0))->method('dispatch')->with($envelope)->willReturn($envelope); - $worker = new Worker([$receiver], $bus, []); + $worker = new Worker(['transport' => $receiver], $bus, []); $worker->run([], function (?Envelope $envelope) use ($worker) { // stop after the messages finish if (null === $envelope) { diff --git a/src/Symfony/Component/Messenger/Worker.php b/src/Symfony/Component/Messenger/Worker.php index 134b98e61aace..cf3a5f68951c4 100644 --- a/src/Symfony/Component/Messenger/Worker.php +++ b/src/Symfony/Component/Messenger/Worker.php @@ -44,7 +44,7 @@ class Worker implements WorkerInterface private $shouldStop = false; /** - * @param ReceiverInterface[] $receivers Where the key will be used as the string "identifier" + * @param ReceiverInterface[] $receivers Where the key is the transport name * @param RetryStrategyInterface[] $retryStrategies Retry strategies for each receiver (array keys must match) */ public function __construct(array $receivers, MessageBusInterface $bus, $retryStrategies = [], EventDispatcherInterface $eventDispatcher = null, LoggerInterface $logger = null) @@ -86,13 +86,13 @@ public function run(array $options = [], callable $onHandledCallback = null): vo while (false === $this->shouldStop) { $envelopeHandled = false; - foreach ($this->receivers as $receiverName => $receiver) { + foreach ($this->receivers as $transportName => $receiver) { $envelopes = $receiver->get(); foreach ($envelopes as $envelope) { $envelopeHandled = true; - $this->handleMessage($envelope, $receiver, $receiverName, $this->retryStrategies[$receiverName] ?? null); + $this->handleMessage($envelope, $receiver, $transportName, $this->retryStrategies[$transportName] ?? null); $onHandled($envelope); } @@ -114,9 +114,9 @@ public function run(array $options = [], callable $onHandledCallback = null): vo $this->dispatchEvent(new WorkerStoppedEvent()); } - private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, string $receiverName, ?RetryStrategyInterface $retryStrategy) + private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, string $transportName, ?RetryStrategyInterface $retryStrategy) { - $this->dispatchEvent(new WorkerMessageReceivedEvent($envelope, $receiverName)); + $this->dispatchEvent(new WorkerMessageReceivedEvent($envelope, $transportName)); $message = $envelope->getMessage(); $context = [ @@ -125,7 +125,7 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, ]; try { - $envelope = $this->bus->dispatch($envelope->with(new ReceivedStamp())); + $envelope = $this->bus->dispatch($envelope->with(new ReceivedStamp($transportName))); } catch (\Throwable $throwable) { if ($throwable instanceof HandlerFailedException) { $envelope = $throwable->getEnvelope(); @@ -133,7 +133,7 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, $shouldRetry = $this->shouldRetry($throwable, $envelope, $retryStrategy); - $this->dispatchEvent(new WorkerMessageFailedEvent($envelope, $receiverName, $throwable, $shouldRetry)); + $this->dispatchEvent(new WorkerMessageFailedEvent($envelope, $transportName, $throwable, $shouldRetry)); if ($shouldRetry) { if (null === $retryStrategy) { @@ -166,7 +166,7 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver, return; } - $this->dispatchEvent(new WorkerMessageHandledEvent($envelope, $receiverName)); + $this->dispatchEvent(new WorkerMessageHandledEvent($envelope, $transportName)); if (null !== $this->logger) { $this->logger->info('{class} was handled successfully (acknowledging to transport).', $context);