8000 [Messenger] Compile time errors fixes and tweaks · symfony/symfony@ed9ef9d · GitHub
[go: up one dir, main page]

Skip to content

Commit ed9ef9d

Browse files
committed
[Messenger] Compile time errors fixes and tweaks
1 parent 5b6ff93 commit ed9ef9d

File tree

3 files changed

+209
-14
lines changed

3 files changed

+209
-14
lines changed

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ private function registerHandlers(ContainerBuilder $container)
6767

6868
foreach ($container->findTaggedServiceIds($this->handlerTag, true) as $serviceId => $tags) {
6969
foreach ($tags as $tag) {
70-
$handles = $tag['handles'] ?? $this->guessHandledClass($container, $serviceId);
70+
$handles = $tag['handles'] ?? $this->guessHandledClass($r = $container->getReflectionClass($container->getDefinition($serviceId)->getClass()), $serviceId);
7171

7272
if (!class_exists($handles)) {
73-
$messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : 'declared in `__invoke` function';
73+
$messageClassLocation = isset($tag['handles']) ? 'declared in your tag attribute "handles"' : sprintf('used as argument type in method "%s::__invoke()"', $r->getName());
7474

75-
throw new RuntimeException(sprintf('The message class "%s" %s of service "%s" does not exist.', $messageClassLocation, $handles, $serviceId));
75+
throw new RuntimeException(sprintf('Invalid handler service "%s": message class "%s" %s does not exist.', $serviceId, $handles, $messageClassLocation));
7676
}
7777

7878
$priority = $tag['priority'] ?? 0;
@@ -108,27 +108,28 @@ private function registerHandlers(ContainerBuilder $container)
108108
$handlerResolver->replaceArgument(0, ServiceLocatorTagPass::register($container, $handlersLocatorMapping));
109109
}
110110

111-
private function guessHandledClass(ContainerBuilder $container, string $serviceId): string
111+
private function guessHandledClass(\ReflectionClass $handlerClass, string $serviceId): string
112112
{
113-
$reflection = $container->getReflectionClass($container->getDefinition($serviceId)->getClass());
114-
115113
try {
116-
$method = $reflection->getMethod('__invoke');
114+
$method = $handlerClass->getMethod('__invoke');
117115
} catch (\ReflectionException $e) {
118-
throw new RuntimeException(sprintf('Service "%s" should have an `__invoke` function.', $serviceId));
116+
throw new RuntimeException(sprintf('Invalid handler service "%s": class "%s" must have an "__invoke()" method.', $serviceId, $handlerClass->getName()));
119117
}
120118

121119
$parameters = $method->getParameters();
122120
if (1 !== count($parameters)) {
123-
throw new RuntimeException(sprintf('`__invoke` function of service "%s" must have exactly one parameter.', $serviceId));
121+
throw new RuntimeException(sprintf('Invalid handler service "%s": method "%s::__invoke()" must have exactly one argument corresponding to the message it handles.', $serviceId, $handlerClass->getName()));
122+
}
123+
124+
if (!$type = $parameters[0]->getType()) {
125+
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()));
124126
}
125127

126-
$parameter = $parameters[0];
127-
if (null === $parameter->getClass()) {
128-
throw new RuntimeException(sprintf('The parameter of `__invoke` function of service "%s" must type hint the message class it handles.', $serviceId));
128+
if ($type->isBuiltin()) {
129+
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));
129130
}
130131

131-
return $parameter->getClass()->getName();
132+
return $parameters[0]->getType();
132133
}
133134

134135
private function registerReceivers(ContainerBuilder $container)
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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\Tests\DependencyInjection;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\DependencyInjection\ServiceLocator;
19+
use Symfony\Component\Messenger\ContainerHandlerLocator;
20+
use Symfony\Component\Messenger\DependencyInjection\MessengerPass;
21+
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
22+
use Symfony\Component\Messenger\Transport\ReceiverInterface;
23+
24+
class MessengerPassTest extends TestCase
25+
{
26+
public function testProcess()
27+
{
28+
$container = $this->getContainerBuilder();
29+
$container
30+
->register(DummyHandler::class, DummyHandler::class)
31+
->addTag('messenger.message_handler')
32+
;
33+
$container
34+
->register(DummyReceiver::class, DummyReceiver::class)
35+
->addTag('messenger.receiver')
36+
;
37+
38+
(new MessengerPass())->process($container);
39+
40+
$handlerLocatorDefinition = $container->getDefinition($container->getDefinition('messenger.handler_resolver')->getArgument(0));
41+
$this->assertSame(ServiceLocator::class, $handlerLocatorDefinition->getClass());
42+
$this->assertEquals(
43+
array('handler.'.DummyMessage::class => new ServiceClosureArgument(new Reference(DummyHandler::class))),
44+
$handlerLocatorDefinition->getArgument(0)
45+
);
46+
47+
$this->assertEquals(
48+
array(DummyReceiver::class => new Reference(DummyReceiver::class)),
49+
$container->getDefinition('messenger.receiver_locator')->getArgument(0)
50+
);
51+
}
52+
53+
/**
54+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
55+
* @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.
56+
*/
57+
public function testUndefinedMessageClassForHandler()
58+
{
59+
$container = $this->getContainerBuilder();
60+
$container
61+
->register(UndefinedMessageHandler::class, UndefinedMessageHandler::class)
62+
->addTag('messenger.message_handler')
63+
;
64+
65+
(new MessengerPass())->process($container);
66+
}
67+
68+
/**
69+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
70+
* @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\NotInvokableHandler": class "Symfony\Component\Messenger\Tests\DependencyInjection\NotInvokableHandler" must have an "__invoke()" method.
71+
*/
72+
public function testNotInvokableHandler()
73+
{
74+
$container = $this->getContainerBuilder();
75+
$container
76+
->register(NotInvokableHandler::class, NotInvokableHandler::class)
77+
->addTag('messenger.message_handler')
78+
;
79+
80+
(new MessengerPass())->process($container);
81+
}
82+
83+
/**
84+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
85+
* @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\MissingArgumentHandler": method "Symfony\Component\Messenger\Tests\DependencyInjection\MissingArgumentHandler::__invoke()" must have exactly one argument corresponding to the message it handles.
86+
*/
87+
public function testMissingArgumentHandler()
88+
{
89+
$container = $this->getContainerBuilder();
90+
$container
91+
->register(MissingArgumentHandler::class, MissingArgumentHandler::class)
92+
->addTag('messenger.message_handler')
93+
;
94+
95+
(new MessengerPass())->process($container);
96+
}
97+
98+
/**
99+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
100+
* @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\MissingArgumentTypeHandler": argument "$message" of method "Symfony\Component\Messenger\Tests\DependencyInjection\MissingArgumentTypeHandler::__invoke()" must have a type-hint corresponding to the message class it handles.
101+
*/
102+
public function testMissingArgumentTypeHandler()
103+
{
104+
$container = $this->getContainerBuilder();
105+
$container
106+
->register(MissingArgumentTypeHandler::class, MissingArgumentTypeHandler::class)
107+
->addTag('messenger.message_handler')
108+
;
109+
110+
(new MessengerPass())->process($container);
111+
}
112+
113+
/**
114+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
115+
* @expectedExceptionMessage Invalid handler service "Symfony\Component\Messenger\Tests\DependencyInjection\BuiltinArgumentTypeHandler": type-hint of argument "$message" in method "Symfony\Component\Messenger\Tests\DependencyInjection\BuiltinArgumentTypeHandler::__invoke()" must be a class , "string" given.
116+
*/
117+
public function testBuiltinArgumentTypeHandler()
118+
{
119+
$container = $this->getContainerBuilder();
120+
$container
121+
->register(BuiltinArgumentTypeHandler::class, BuiltinArgumentTypeHandler::class)
122+
->addTag('messenger.message_handler')
123+
;
124+
125+
(new MessengerPass())->process($container);
126+
}
127+
128+
private function getContainerBuilder(): ContainerBuilder
129+
{
130+
$container = new ContainerBuilder();
131+
$container->setParameter('kernel.debug', true);
132+
$container->register('message_bus', ContainerHandlerLocator::class);
133+
134+
$container
135+
->register('messenger.handler_resolver', ContainerHandlerLocator::class)
136+
->addArgument(new Reference('service_container'))
137+
;
138+
139+
$container->register('messenger.receiver_locator', ServiceLocator::class)
140+
->addArgument(new Reference('service_container'))
141+
;
142+
143+
return $container;
144+
}
145+
}
146+
147+
class DummyHandler
148+
{
149+
public function __invoke(DummyMessage $message): void
150+
{
151+
}
152+
}
153+
154+
class DummyReceiver implements ReceiverInterface
155+
{
156+
public function receive(): iterable
157+
{
158+
for ($i = 0; $i < 3; ++$i) {
159+
yield new DummyMessage("Dummy $i");
160+
}
161+
}
162+
}
163+
164+
class UndefinedMessageHandler
165+
{
166+
public function __invoke(UndefinedMessage $message)
167+
{
168+
}
169+
}
170+
171+
class NotInvokableHandler
172+
{
173+
}
174+
175+
class MissingArgumentHandler
176+
{
177+
public function __invoke()
178+
{
179+
}
180+
}
181+
182+
class MissingArgumentTypeHandler
183+
{
184+
public function __invoke($message)
185+
{
186+
}
187+
}
188+
189+
class BuiltinArgumentTypeHandler
190+
{
191+
public function __invoke(string $message)
192+
{
193+
}
194+
}

src/Symfony/Component/Messenger/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
},
2121
"require-dev": {
2222
"symfony/serializer": "~3.4|~4.0",
23-
"symfony/dependency-injection": "~3.4|~4.0",
23+
"symfony/dependency-injection": "~3.4.6|~4.0",
2424
"symfony/property-access": "~3.4|~4.0"
2525
},
2626
"suggest": {

0 commit comments

Comments
 (0)
0