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

Skip to content

Commit a2f09cb

Browse files
committed
Enable auto-configuration for HandlerInterface
1 parent 2889acf commit a2f09cb

File tree

4 files changed

+126
-9
lines changed

4 files changed

+126
-9
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
use Symfony\Component\Lock\LockInterface;
6060
use Symfony\Component\Lock\Store\StoreFactory;
6161
use Symfony\Component\Lock\StoreInterface;
62+
use Symfony\Component\Messenger\Handler\HandlerInterface;
6263
use Symfony\Component\Messenger\Transport\ReceiverInterface;
6364
use Symfony\Component\Messenger\Transport\SenderInterface;
6465
use Symfony\Component\PropertyAccess\PropertyAccessor;
@@ -346,6 +347,8 @@ public function load(array $configs, ContainerBuilder $container)
346347
->addTag('messenger.receiver');
347348
$container->registerForAutoconfiguration(SenderInterface::class)
348349
->addTag('messenger.sender');
350+
$container->registerForAutoconfiguration(HandlerInterface::class)
351+
->addTag('messenger.message_handler');
349352

350353
if (!$container->getParameter('kernel.debug')) {
351354
// remove tagged iterator argument for resource checkers

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

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
2020
use Symfony\Component\DependencyInjection\Reference;
2121
use Symfony\Component\Messenger\Handler\ChainHandler;
22+
use Symfony\Component\Messenger\Handler\HandlerInterface;
2223

2324
/**
2425
* @author Samuel Roze <[email protected]>
@@ -67,16 +68,29 @@ private function registerHandlers(ContainerBuilder $container)
6768

6869
foreach ($container->findTaggedServiceIds($this->handlerTag, true) as $serviceId => $tags) {
6970
foreach ($tags as $tag) {
70-
$handles = $tag['handles'] ?? $this->guessHandledClass($r = $container->getReflectionClass($container->getParameterBag()->resolveValue($container->getDefinition($serviceId)->getClass())), $serviceId);
71-
72-
if (!class_exists($handles)) {
73-
$messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : sprintf('used as argument type in method "%s::__invoke()"', $r->getName());
74-
75-
throw new RuntimeException(sprintf('Invalid handler service "%s": message class "%s" %s does not exist.', $serviceId, $handles, $messageClassLocation));
71+
$handles = $tag['handles'] ?? $this->guessHandledClasses($r = $container->getReflectionClass($container->getParameterBag()->resolveValue($container->getDefinition($serviceId)->getClass())), $serviceId);
72+
if (empty($handles)) {
73+
throw new RuntimeException(sprintf('The handler service "%s" does not seem to handle any message.', $serviceId));
7674
}
7775

7876
$priority = $tag['priority'] ?? 0;
79-
$handlersByMessage[$handles][$priority][] = new Reference($serviceId);
77+
78+
foreach ($handles as $messageClass) {
79+
if (is_array($messageClass)) {
80+
$messagePriority = $messageClass[1];
81+
$messageClass = $messageClass[0];
82+
} else {
83+
$messagePriority = $priority;
84+
}
85+
86+
if (!class_exists($messageClass)) {
87+
$messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : sprintf('used as argument type in method "%s::__invoke()"', $r->getName());
88+
89+
throw new RuntimeException(sprintf('Invalid handler service "%s": message class "%s" %s does not exist.', $serviceId, $messageClass, $messageClassLocation));
90+
}
91+
92+
$handlersByMessage[$messageClass][$messagePriority][] = new Reference($serviceId);
93+
}
8094
}
8195
}
8296

@@ -108,8 +122,12 @@ private function registerHandlers(ContainerBuilder $container)
108122
$handlerResolver->replaceArgument(0, ServiceLocatorTagPass::register($container, $handlersLocatorMapping));
109123
}
110124

111-
private function guessHandledClass(\ReflectionClass $handlerClass, string $serviceId): string
125+
private function guessHandledClasses(\ReflectionClass $handlerClass, string $serviceId): array
112126
{
127+
if ($handlerClass->implementsInterface(HandlerInterface::class)) {
128+
return $handlerClass->getName()::getHandledMessages();
129+
}
130+
113131
try {
114132
$method = $handlerClass->getMethod('__invoke');
115133
} catch (\ReflectionException $e) {
@@ -129,7 +147,7 @@ private function guessHandledClass(\ReflectionClass $handlerClass, string $servi
129147
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));
130148
}
131149

132-
return $parameters[0]->getType();
150+
return array((string) $parameters[0]->getType());
133151
}
134152

135153
private function registerReceivers(ContainerBuilder $container)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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+
* Handlers can implements this interface. This allows them to be auto-configured and to
16+
* handle multiple requests.
17+
*
18+
* @author Samuel Roze <[email protected]>
19+
*/
20+
interface HandlerInterface
21+
{
22+
/**
23+
* Return a list of messages to be handled.
24+
*
25+
* It returns a list of messages like in the following example:
26+
*
27+
* return [
28+
* FirstMessage::class,
29+
* SecondMessage:class
30+
* ];
31+
*
32+
* It can also change the priority per classes.
33+
*
34+
* return [
35+
* [FirstMessage::class, 0],
36+
* [SecondMessage::class, -10],
37+
* ];
38+
*
39+
* The `__invoke` method of the handler will be called as usual with the message to handle.
40+
*
41+
* @return array
42+
*/
43+
public static function getHandledMessages(): array;
44+
}

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
use Symfony\Component\DependencyInjection\ServiceLocator;
1919
use Symfony\Component\Messenger\ContainerHandlerLocator;
2020
use Symfony\Component\Messenger\DependencyInjection\MessengerPass;
21+
use Symfony\Component\Messenger\Handler\ChainHandler;
22+
use Symfony\Component\Messenger\Handler\HandlerInterface;
2123
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
24+
use Symfony\Component\Messenger\Tests\Fixtures\SecondMessage;
2225
use Symfony\Component\Messenger\Transport\ReceiverInterface;
2326

2427
class MessengerPassTest extends TestCase
@@ -50,6 +53,34 @@ public function testProcess()
5053
);
5154
}
5255

56+
public function testGetClassesFromTheHandlerInterface()
57+
{
58+
$container = $this->getContainerBuilder();
59+
$container
60+
->register(HandlerWithMultipleMessages::class, HandlerWithMultipleMessages::class)
61+
->addTag('messenger.message_handler')
62+
;
63+
$container
64+
->register(PrioritisedHandler::class, PrioritisedHandler::class)
65+
->addTag('messenger.message_handler')
66+
;
67+
68+
(new MessengerPass())->process($container);
69+
70+
$handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0));
71+
$handlerMapping = $handlerLocatorDefinition->getArgument(0);
72+
73+
$this->assertArrayHasKey('handler.'.DummyMessage::class, $handlerMapping);
74+
$this->assertEquals(new ServiceClosureArgument(new Reference(HandlerWithMultipleMessages::class)), $handlerMapping['handler.'.DummyMessage::class]);
75+
76+
$this->assertArrayHasKey('handler.'.SecondMessage::class, $handlerMapping);
77+
$handlerReference = (string) $handlerMapping['handler.'.SecondMessage::class]->getValues()[0];
78+
$definition = $container->getDefinition($handlerReference);
79+
80+
$this->assertEquals(ChainHandler::class, $definition->getClass());
81+
$this->assertEquals(array(new Reference(PrioritisedHandler::class), new Reference(HandlerWithMultipleMessages::class)), $definition->getArgument(0));
82+
}
83+
5384
/**
5485
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
5586
* @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler": message class "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessage" used as argument type in method "Symfony\Component\Messenger\Tests\DependencyInjection\UndefinedMessageHandler::__invoke()" does not exist.
@@ -192,3 +223,24 @@ public function __invoke(string $message)
192223
{
193224
}
194225
}
226+
227+
class HandlerWithMultipleMessages implements HandlerInterface
228+
{
229+
public static function getHandledMessages(): array
230+
{
231+
return array(
232+
DummyMessage::class,
233+
SecondMessage::class,
234+
);
235+
}
236+
}
237+
238+
class PrioritisedHandler implements HandlerInterface
239+
{
240+
public static function getHandledMessages(): array
241+
{
242+
return array(
243+
array(SecondMessage::class, 10),
244+
);
245+
}
246+
}

0 commit comments

Comments
 (0)