Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2461e51

Browse files
committed
[Messenger][DX] Uses custom method names for handlers
1 parent 6988551 commit 2461e51

File tree

3 files changed

+132
-12
lines changed

3 files changed

+132
-12
lines changed

src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,27 +74,55 @@ public function process(ContainerBuilder $container)
7474

7575
private function registerHandlers(ContainerBuilder $container)
7676
{
77+
$definitions = array();
7778
$handlersByMessage = array();
7879

7980
foreach ($container->findTaggedServiceIds($this->handlerTag, true) as $serviceId => $tags) {
8081
foreach ($tags as $tag) {
81-
$handles = isset($tag['handles']) ? array($tag['handles']) : $this->guessHandledClasses($r = $container->getReflectionClass($container->getDefinition($serviceId)->getClass()), $serviceId);
82+
$r = $container->getReflectionClass($container->getDefinition($serviceId)->getClass());
83+
84+
if (isset($tag['handles'])) {
85+
$handles = isset($tag['method']) ? array($tag['handles'] => $tag['method']) : array($tag['handles']);
86+
} else {
87+
$handles = $this->guessHandledClasses($r, $serviceId);
88+
}
89+
8290
$priority = $tag['priority'] ?? 0;
8391

84-
foreach ($handles as $messageClass) {
92+
foreach ($handles as $messageClass => $method) {
93+
if (\is_int($messageClass)) {
94+
$messageClass = $method;
95+
$method = '__invoke';
96+
}
97+
8598
if (\is_array($messageClass)) {
8699
$messagePriority = $messageClass[1];
87100
$messageClass = $messageClass[0];
88101
} else {
89102
$messagePriority = $priority;
90103
}
91104

92-
if (!class_exists($messageClass)) {
93-
$messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : sprintf($r->implementsInterface(MessageHandlerInterface::class) ? 'returned by method "%s::getHandledMessages()"' : 'used as argument type in method "%s::__invoke()"', $r->getName());
105+
if (\is_array($method)) {
106+
$messagePriority = $method[1];
107+
$method = $method[0];
108+
}
109+
110+
if (!\class_exists($messageClass)) {
111+
$messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : $r->implementsInterface(MessageHandlerInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method);
94112

95113
throw new RuntimeException(sprintf('Invalid handler service "%s": message class "%s" %s does not exist.', $serviceId, $messageClass, $messageClassLocation));
96114
}
97115

116+
if (!$r->hasMethod($method)) {
117+
throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::%s()" does not exist.', $serviceId, $r->getName(), $method));
118+
}
119+
120+
if ('__invoke' !== $method) {
121+
$wrapperDefinition = (new Definition('callable'))->addArgument(array(new Reference($serviceId), $method))->setFactory('Closure::fromCallable');
122+
123+
$definitions[$serviceId = '.messenger.method_on_object_wrapper.'.ContainerBuilder::hash($messageClass.':'.$messagePriority.':'.$serviceId.':'.$method)] = $wrapperDefinition;
124+
}
125+
98126
$handlersByMessage[$messageClass][$messagePriority][] = new Reference($serviceId);
99127
}
100128
}
@@ -105,7 +133,6 @@ private function registerHandlers(ContainerBuilder $container)
105133
$handlersByMessage[$message] = array_merge(...$handlersByMessage[$message]);
106134
}
107135

108-
$definitions = array();
109136
$handlersLocatorMapping = array();
110137
foreach ($handlersByMessage as $message => $handlers) {
111138
if (1 === \count($handlers)) {

src/Symfony/Component/Messenger/Handler/MessageSubscriberInterface.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ interface MessageSubscriberInterface extends MessageHandlerInterface
3434
* [SecondMessage::class, -10],
3535
* ];
3636
*
37-
* The `__invoke` method of the handler will be called as usual with the message to handle.
37+
* It can also specify a method and/or a priority per message:
3838
*
39-
* @return array
39+
* return [
40+
* FirstMessage::class => 'firstMessageMethod',
41+
* SecondMessage::class => ['secondMessageMethod', 20],
42+
* ];
43+
*
44+
* The `__invoke` method of the handler will be called as usual with the message to handle.
4045
*/
41-
public static function getHandledMessages(): array;
46+
public static function getHandledMessages(): iterable;
4247
}

src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,53 @@ public function testGetClassesFromTheHandlerSubscriberInterface()
9898
$this->assertEquals(array(new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class)), $definition->getArgument(0));
9999
}
100100

101+
public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber()
102+
{
103+
$container = $this->getContainerBuilder();
104+
$container
105+
->register(HandlerMappingMethods::class, HandlerMappingMethods::class)
106+
->addTag('messenger.message_handler')
107+
;
108+
$container
109+
->register(PrioritizedHandler::class, PrioritizedHandler::class)
110+
->addTag('messenger.message_handler')
111+
;
112+
113+
(new MessengerPass())->process($container);
114+
115+
$handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0));
116+
$handlerMapping = $handlerLocatorDefinition->getArgument(0);
117+
118+
$this->assertArrayHasKey('handler.'.DummyMessage::class, $handlerMapping);
119+
$this->assertArrayHasKey('handler.'.SecondMessage::class, $handlerMapping);
120+
121+
$dummyHandlerReference = (string) $handlerMapping['handler.'.DummyMessage::class]->getValues()[0];
122+
$dummyHandlerDefinition = $container->getDefinition($dummyHandlerReference);
123+
$this->assertSame('callable', $dummyHandlerDefinition->getClass());
124+
$this->assertEquals(array(new Reference(HandlerMappingMethods::class), 'dummyMethod'), $dummyHandlerDefinition->getArgument(0));
125+
$this->assertSame(array('Closure', 'fromCallable'), $dummyHandlerDefinition->getFactory());
126+
127+
$secondHandlerReference = (string) $handlerMapping['handler.'.SecondMessage::class]->getValues()[0];
128+
$secondHandlerDefinition = $container->getDefinition($secondHandlerReference);
129+
$this->assertSame(ChainHandler::class, $secondHandlerDefinition->getClass());
130+
$this->assertEquals(new Reference(PrioritizedHandler::class), $secondHandlerDefinition->getArgument(0)[1]);
131+
}
132+
133+
/**
134+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
135+
* @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\HandlerMappingWithNonExistentMethod": method "Symfony\Component\Messenger\Tests\DependencyInjection\HandlerMappingWithNonExistentMethod::dummyMethod()" does not exist.
136+
*/
137+
public function testThrowsExceptionIfTheHandlerMethodDoesNotExist()
138+
{
139+
$container = $this->getContainerBuilder();
140+
$container
141+
->register(HandlerMappingWithNonExistentMethod::class, HandlerMappingWithNonExistentMethod::class)
142+
->addTag('messenger.message_handler')
143+
;
144+
145+
(new MessengerPass())->process($container);
146+
}
147+
101148
public function testItRegistersReceivers()
102149
{
103150
$container = $this->getContainerBuilder();
@@ -397,7 +444,7 @@ public function __invoke(UndefinedMessage $message)
397444

398445
class UndefinedMessageHandlerViaInterface implements MessageSubscriberInterface
399446
{
400-
public static function getHandledMessages(): array
447+
public static function getHandledMessages(): iterable
401448
{
402449
return array(UndefinedMessage::class);
403450
}
@@ -434,31 +481,72 @@ public function __invoke(string $message)
434481

435482
class HandlerWithMultipleMessages implements MessageSubscriberInterface
436483
{
437-
public static function getHandledMessages(): array
484+
public static function getHandledMessages(): iterable
438485
{
439486
return array(
440487
DummyMessage::class,
441488
SecondMessage::class,
442489
);
443490
}
491+
492+
public function __invoke()
493+
{
494+
}
444495
}
445496

446497
class PrioritizedHandler implements MessageSubscriberInterface
447498
{
448-
public static function getHandledMessages(): array
499+
public static function getHandledMessages(): iterable
449500
{
450501
return array(
451502
array(SecondMessage::class, 10),
452503
);
453504
}
505+
506+
public function __invoke()
507+
{
508+
}
509+
}
510+
511+
class HandlerMappingMethods implements MessageSubscriberInterface
512+
{
513+
public static function getHandledMessages(): iterable
514+
{
515+
return array(
516+
DummyMessage::class => 'dummyMethod',
517+
SecondMessage::class => array('secondMessage', 20),
518+
);
519+
}
520+
521+
public function dummyMethod()
522+
{
523+
}
524+
525+
public function secondMessage()
526+
{
527+
}
528+
}
529+
530+
class HandlerMappingWithNonExistentMethod implements MessageSubscriberInterface
531+
{
532+
public static function getHandledMessages(): iterable
533+
{
534+
return array(
535+
DummyMessage::class => 'dummyMethod',
536+
);
537+
}
454538
}
455539

456540
class HandleNoMessageHandler implements MessageSubscriberInterface
457541
{
458-
public static function getHandledMessages(): array
542+
public static function getHandledMessages(): iterable
459543
{
460544
return array();
461545
}
546+
547+
public function __invoke()
548+
{
549+
}
462550
}
463551

464552
class UselessMiddleware implements MiddlewareInterface

0 commit comments

Comments
 (0)