From 780b7dafd5f0ad2f598ddc41a6f7007d45147561 Mon Sep 17 00:00:00 2001 From: Dejan Angelov Date: Wed, 5 Jan 2022 18:14:44 +0100 Subject: [PATCH] [Messenger] Resolve handled classes when only method in tag is provided --- src/Symfony/Component/Messenger/CHANGELOG.md | 1 + .../DependencyInjection/MessengerPass.php | 22 +++++++----- .../DependencyInjection/MessengerPassTest.php | 35 +++++++++++++++++++ .../TaggedDummyHandlerWithCustomMethods.php | 17 +++++++++ 4 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Component/Messenger/Tests/Fixtures/TaggedDummyHandlerWithCustomMethods.php diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index 14b38194898ff..98193ec5c02c4 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add `SerializedMessageStamp` to avoid serializing a message when a retry occurs. + * Automatically resolve handled message type when method different from `__invoke` is used as handler. 6.0 --- diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 99d6975ef702e..3c2f0b6fa4eca 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -78,7 +78,7 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) if (isset($tag['handles'])) { $handles = isset($tag['method']) ? [$tag['handles'] => $tag['method']] : [$tag['handles']]; } else { - $handles = $this->guessHandledClasses($r, $serviceId); + $handles = $this->guessHandledClasses($r, $serviceId, $tag['method'] ?? '__invoke'); } $message = null; @@ -197,25 +197,29 @@ private function registerHandlers(ContainerBuilder $container, array $busIds) } } - private function guessHandledClasses(\ReflectionClass $handlerClass, string $serviceId): iterable + private function guessHandledClasses(\ReflectionClass $handlerClass, string $serviceId, string $methodName): iterable { if ($handlerClass->implementsInterface(MessageSubscriberInterface::class)) { return $handlerClass->getName()::getHandledMessages(); } try { - $method = $handlerClass->getMethod('__invoke'); + $method = $handlerClass->getMethod($methodName); } catch (\ReflectionException $e) { - throw new RuntimeException(sprintf('Invalid handler service "%s": class "%s" must have an "__invoke()" method.', $serviceId, $handlerClass->getName())); + throw new RuntimeException(sprintf('Invalid handler service "%s": class "%s" must have an "%s()" method.', $serviceId, $handlerClass->getName(), $methodName)); } if (0 === $method->getNumberOfRequiredParameters()) { - throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::__invoke()" requires at least one argument, first one being the message it handles.', $serviceId, $handlerClass->getName())); + throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::%s()" requires at least one argument, first one being the message it handles.', $serviceId, $handlerClass->getName(), $methodName)); } $parameters = $method->getParameters(); - if (!$type = $parameters[0]->getType()) { - throw new RuntimeException(sprintf('Invalid handler service "%s": argument "$%s" of method "%s::__invoke()" must have a type-hint corresponding to the message class it handles.', $serviceId, $parameters[0]->getName(), $handlerClass->getName())); + + /** @var \ReflectionNamedType|\ReflectionUnionType|null */ + $type = $parameters[0]->getType(); + + if (!$type) { + throw new RuntimeException(sprintf('Invalid handler service "%s": argument "$%s" of method "%s::%s()" must have a type-hint corresponding to the message class it handles.', $serviceId, $parameters[0]->getName(), $handlerClass->getName(), $methodName)); } if ($type instanceof \ReflectionUnionType) { @@ -232,10 +236,10 @@ private function guessHandledClasses(\ReflectionClass $handlerClass, string $ser } if ($type->isBuiltin()) { - throw new RuntimeException(sprintf('Invalid handler service "%s": type-hint of argument "$%s" in method "%s::__invoke()" must be a class , "%s" given.', $serviceId, $parameters[0]->getName(), $handlerClass->getName(), $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type)); + throw new RuntimeException(sprintf('Invalid handler service "%s": type-hint of argument "$%s" in method "%s::%s()" must be a class , "%s" given.', $serviceId, $parameters[0]->getName(), $handlerClass->getName(), $methodName, $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type)); } - return [$type->getName()]; + return ('__invoke' === $methodName) ? [$type->getName()] : [$type->getName() => $methodName]; } private function registerReceivers(ContainerBuilder $container, array $busIds) diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index 7c11dcce89147..66905402df57d 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -47,6 +47,7 @@ use Symfony\Component\Messenger\Tests\Fixtures\MultipleBusesMessageHandler; use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage; use Symfony\Component\Messenger\Tests\Fixtures\TaggedDummyHandler; +use Symfony\Component\Messenger\Tests\Fixtures\TaggedDummyHandlerWithCustomMethods; use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; class MessengerPassTest extends TestCase @@ -133,6 +134,40 @@ public function testTaggedMessageHandler() $this->assertHandlerDescriptor($container, $handlerDescriptionMapping, DummyMessage::class, [TaggedDummyHandler::class], [[]]); } + public function testTaggedMessageHandlerWithGivenMethodAndNotGivenMessageType() + { + $container = $this->getContainerBuilder($busId = 'message_bus'); + $container + ->register(TaggedDummyHandlerWithCustomMethods::class, TaggedDummyHandlerWithCustomMethods::class) + ->setAutoconfigured(true) + ->addTag('messenger.message_handler', [ + 'method' => 'handleDummyMessage', + ]) + ->addTag('messenger.message_handler', [ + 'method' => 'handleSecondMessage', + ]); + + (new MessengerPass())->process($container); + + $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); + + $this->assertArrayHasKey(DummyMessage::class, $handlersMapping); + $this->assertHandlerDescriptor( + $container, + $handlersMapping, + DummyMessage::class, + [[TaggedDummyHandlerWithCustomMethods::class, 'handleDummyMessage']] + ); + + $this->assertArrayHasKey(SecondMessage::class, $handlersMapping); + $this->assertHandlerDescriptor( + $container, + $handlersMapping, + SecondMessage::class, + [[TaggedDummyHandlerWithCustomMethods::class, 'handleSecondMessage']] + ); + } + public function testProcessHandlersByBus() { $container = $this->getContainerBuilder($commandBusId = 'command_bus'); diff --git a/src/Symfony/Component/Messenger/Tests/Fixtures/TaggedDummyHandlerWithCustomMethods.php b/src/Symfony/Component/Messenger/Tests/Fixtures/TaggedDummyHandlerWithCustomMethods.php new file mode 100644 index 0000000000000..885297c26d7c7 --- /dev/null +++ b/src/Symfony/Component/Messenger/Tests/Fixtures/TaggedDummyHandlerWithCustomMethods.php @@ -0,0 +1,17 @@ +