10000 [Messenger] Resolve handled classes when only method in tag is provided by angelov · Pull Request #44971 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Messenger] Resolve handled classes when only method in tag is provided #44971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Component/Messenger/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Symfony\Component\Messenger\Tests\Fixtures;

use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
class TaggedDummyHandlerWithCustomMethods
{
public function handleDummyMessage(DummyMessage $message)
{
}

public function handleSecondMessage(SecondMessage $message)
{
}
}
0