8000 Uses custom method names for handlers · symfony/symfony@0ebaf5b · GitHub
[go: up one dir, main page]

Skip to content

Commit 0ebaf5b

Browse files
committed
Uses custom method names for handlers
Throw an exception when the method do not exist Add an example of the specific method in the interface docblock Add the method in the hash and prefix it
1 parent daf7ac0 commit 0ebaf5b

File tree

4 files changed

+169
-5
lines changed

4 files changed

+169
-5
lines changed

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

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
2424
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
2525
use Symfony\Component\Messenger\TraceableMessageBus;
26+
use Symfony\Component\Messenger\Handler\MethodOnObjectHandler;
2627

2728
/**
2829
* @author Samuel Roze <samuel.roze@gmail.com>
@@ -72,27 +73,55 @@ public function process(ContainerBuilder $container)
7273

7374
private function registerHandlers(ContainerBuilder $container)
7475
{
76+
$definitions = array();
7577
$handlersByMessage = array();
7678

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

82-
foreach ($handles as $messageClass) {
91+
foreach ($handles as $messageClass => $method) {
92+
if (\is_int($messageClass)) {
93+
$messageClass = $method;
94+
$method = '__invoke';
95+
}
96+
8397
if (\is_array($messageClass)) {
8498
$messagePriority = $messageClass[1];
8599
$messageClass = $messageClass[0];
86100
} else {
87101
$messagePriority = $priority;
88102
}
89103

90-
if (!class_exists($messageClass)) {
91-
$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());
104+
if (\is_array($method)) {
105+
$messagePriority = $method[1];
106+
$method = $method[0];
107+
}
108+
109+
if (!\class_exists($messageClass)) {
110+
$messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles&qu 6D40 ot;' : $r->implementsInterface(MessageHandlerInterface::class) ? sprintf('returned by method "%s::getHandledMessages()"', $r->getName()) : sprintf('used as argument type in method "%s::%s()"', $r->getName(), $method);
92111

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

115+
if (!$r->hasMethod($method)) {
116+
throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::%s()" does not exist.', $serviceId, $r->getName(), $method));
117+
}
118+
119+
if ('__invoke' !== $method) {
120+
$wrapperDefinition = (new Definition(MethodOnObjectHandler::class))->setArguments(array(new Reference($serviceId), $method));
121+
122+
$definitions[$serviceId = 'messenger.method_on_object_handler.'.ContainerBuilder::hash($messageClass.':'.$messagePriority.':'.$serviceId.':'.$method)] = $wrapperDefinition;
123+
}
124+
96125
$handlersByMessage[$messageClass][$messagePriority][] = new Reference($serviceId);
97126
}
98127
}
@@ -103,7 +132,6 @@ private function registerHandlers(ContainerBuilder $container)
103132
$handlersByMessage[$message] = \call_user_func_array('array_merge', $handlersByMessage[$message]);
104133
}
105134

106-
$definitions = array();
107135
foreach ($handlersByMessage as $message => $handlers) {
108136
if (1 === \count($handlers)) {
109137
$handlersByMessage[$message] = current($handlers);

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ interface MessageSubscriberInterface extends MessageHandlerInterface
3232
* [SecondMessage::class, -10],
3333
* ];
3434
*
35+
* It can also specify a method and/or a priority per message:
36+
*
37+
* return [
38+
* FirstMessage::class => 'firstMessageMethod',
39+
* SecondMessage::class => ['secondMessageMethod', 20],
40+
* ];
41+
*
3542
* The `__invoke` method of the handler will be called as usual with the message to handle.
3643
*
3744
* @return array
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Handler;
13+
14+
/**
15+
* Used to transform an object and a method to a callable handler.
16+
*
17+
* @author Samuel Roze <samuel.roze@gmail.com>
18+
*
19+
* @internal
20+
*/
21+
final class MethodOnObjectHandler
22+
{
23+
private $object;
24+
private $method;
25+
26+
public function __construct($object, string $method)
27+
{
28+
if (!\is_object($object)) {
29+
throw new \InvalidArgumentException(sprintf('Expected an object as argument but got %s', \gettype($object)));
30+
}
31+
32+
$this->object = $object;
33+
$this->method = $method;
34+
}
35+
36+
public function __invoke($message)
37+
{
38+
return ($this->object)->{$this->method}($message);
39+
}
40+
}

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

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\Component\Messenger\DependencyInjection\MessengerPass;
2424
use Symfony\Component\Messenger\Handler\ChainHandler;
2525
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
26+
use Symfony\Component\Messenger\Handler\MethodOnObjectHandler;
2627
use Symfony\Component\Messenger\MessageBusInterface;
2728
use Symfony\Component\Messenger\Middleware\TolerateNoHandler;
2829
use Symfony\Component\Messenger\MiddlewareInterface;
@@ -96,6 +97,53 @@ public function testGetClassesFromTheHandlerSubscriberInterface()
9697
$this->assertEquals(array(new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class)), $definition->getArgument(0));
9798
}
9899

100+
public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber()
101+
{
102+
$container = $this->getContainerBuilder();
103+
$container
104+
->register(HandlerMappingMethods::class, HandlerMappingMethods::class)
105+
->addTag('messenger.message_handler')
106+
;
107+
$container
108+
->register(PrioritizedHandler::class, PrioritizedHandler::class)
109+
->addTag('messenger.message_handler')
110+
;
111+
112+
(new MessengerPass())->process($container);
113+
114+
$handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0));
115+
$handlerMapping = $handlerLocatorDefinition->getArgument(0);
116+
117+
$this->assertArrayHasKey('handler.'.DummyMessage::class, $handlerMapping);
118+
$this->assertArrayHasKey('handler.'.SecondMessage::class, $handlerMapping);
119+
120+
$dummyHandlerReference = (string) $handlerMapping['handler.'.DummyMessage::class]->getValues()[0];
121+
$dummyHandlerDefinition = $container->getDefinition($dummyHandlerReference);
122+
$this->assertSame(MethodOnObjectHandler::class, $dummyHandlerDefinition->getClass());
123+
$this->assertEquals(new Reference(HandlerMappingMethods::class), $dummyHandlerDefinition->getArgument(0));
124+
$this->assertSame('dummyMethod', $dummyHandlerDefinition->getArgument(1));
125+
126+
$secondHandlerReference = (string) $handlerMapping['handler.'.SecondMessage::class]->getValues()[0];
127+
$secondHandlerDefinition = $container->getDefinition($secondHandlerReference);
128+
$this->assertSame(ChainHandler::class, $secondHandlerDefinition->getClass());
129+
$this->assertEquals(new Reference(PrioritizedHandler::class), $secondHandlerDefinition->getArgument(0)[1]);
130+
}
131+
132+
/**
133+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
134+
* @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\HandlerMappingWithNonExistentMethod": method "Symfony\Component\Messenger\Tests\DependencyInjection\HandlerMappingWithNonExistentMethod::dummyMethod()" does not exist.
135+
*/
136+
public function testThrowsExceptionIfTheHandlerMethodDoesNotExist()
137+
{
138+
$container = $this->getContainerBuilder();
139+
$container
140+
->register(HandlerMappingWithNonExistentMethod::class, HandlerMappingWithNonExistentMethod::class)
141+
->addTag('messenger.message_handle 1241 r')
142+
;
143+
144+
(new MessengerPass())->process($container);
145+
}
146+
99147
public function testItRegistersReceivers()
100148
{
101149
$container = $this->getContainerBuilder();
@@ -386,6 +434,10 @@ public static function getHandledMessages(): array
386434
SecondMessage::class,
387435
);
388436
}
437+
438+
public function __invoke()
439+
{
440+
}
389441
}
390442

391443
class PrioritizedHandler implements MessageSubscriberInterface
@@ -396,6 +448,39 @@ public static function getHandledMessages(): array
396448
array(SecondMessage::class, 10),
397449
);
398450
}
451+
452+
public function __invoke()
453+
{
454+
}
455+
}
456+
457+
class HandlerMappingMethods implements MessageSubscriberInterface
458+
{
459+
public static function getHandledMessages(): array
460+
{
461+
return array(
462+
DummyMessage::class => 'dummyMethod',
463+
SecondMessage::class => array('secondMessage', 20),
464+
);
465+
}
466+
467+
public function dummyMethod()
468+
{
469+
}
470+
471+
public function secondMessage()
472+
{
473+
}
474+
}
475+
476+
class HandlerMappingWithNonExistentMethod implements MessageSubscriberInterface
477+
{
478+
public static function getHandledMessages(): array
479+
{
480+
return array(
481+
DummyMessage::class => 'dummyMethod',
482+
);
483+
}
399484
}
400485

401486
class HandleNoMessageHandler implements MessageSubscriberInterface
@@ -404,6 +489,10 @@ public static function getHandledMessages(): array
404489
{
405490
return array();
406491
}
492+
493+
public function __invoke()
494+
{
495+
}
407496
}
408497

409498
class UselessMiddleware implements MiddlewareInterface

0 commit comments

Comments
 (0)
0