8000 [EventDispatcher] Allow to omit the event name when registering liste… · symfony/symfony@f6b6966 · GitHub
[go: up one dir, main page]

Skip to content

Commit f6b6966

Browse files
committed
[EventDispatcher] Allow to omit the event name when registering listeners.
1 parent 8f92594 commit f6b6966

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

src/Symfony/Component/EventDispatcher/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* `AddEventAliasesPass` has been added, allowing applications and bundles to extend the event alias mapping used by `RegisterListenersPass`.
8+
* Made the `event` attribute of the `kernel.event_listener` tag optional for FQCN events.
89

910
4.3.0
1011
-----

src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
1717
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1818
use Symfony\Component\DependencyInjection\Reference;
19+
use Symfony\Component\EventDispatcher\Event as LegacyEvent;
1920
use Symfony\Component\EventDispatcher\EventDispatcher;
2021
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
22+
use Symfony\Contracts\EventDispatcher\Event;
2123

2224
/**
2325
* Compiler pass to register tagged services for an event dispatcher.
@@ -66,6 +68,13 @@ public function process(ContainerBuilder $container)
6668
foreach ($events as $event) {
6769
$priority = isset($event['priority']) ? $event['priority'] : 0;
6870

71+
if (!isset($event['event']) && null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && $r->hasMethod($event['method'] = $event['method'] ?? '__invoke') && 0 < ($m = $r->getMethod($event['method']))->getNumberOfParameters()) {
72+
$type = $m->getParameters()[0]->getType();
73+
if ($type && !$type->isBuiltin() && Event::class !== $type->getName() && LegacyEvent::class !== $type->getName()) {
74+
$event['event'] = $type->getName();
75+
}
76+
}
77+
6978
if (!isset($event['event'])) {
7079
throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
7180
}

src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1718
use Symfony\Component\DependencyInjection\Reference;
1819
use Symfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
1920
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
@@ -244,6 +245,81 @@ public function testAliasedEventListener(): void
244245
];
245246
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
246247
}
248+
249+
public function testOmitEventNameOnTypedListener(): void
250+
{
251+
$container = new ContainerBuilder();
252+
$container->setParameter('event_dispatcher.event_aliases', [AliasedEvent::class => 'aliased_event']);
253+
$container->register('foo', TypedListener::class)->addTag('kernel.event_listener', ['method' => 'onEvent']);
254+
$container->register('bar', TypedListener::class)->addTag('kernel.event_listener');
255+
$container->register('event_dispatcher');
256+
257+
$registerListenersPass = new RegisterListenersPass();
258+
$registerListenersPass->process($container);
259+
260+
$definition = $container->getDefinition('event_dispatcher');
261+
$expectedCalls = [
262+
[
263+
'addListener',
264+
[
265+
CustomEvent::class,
266+
[new ServiceClosureArgument(new Reference('foo')), 'onEvent'],
267+
0,
268+
],
269+
],
270+
[
271+
'addListener',
272+
[
273+
'aliased_event',
274+
[new ServiceClosureArgument(new Reference('bar')), '__invoke'],
275+
0,
276+
],
277+
],
278+
];
279+
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
280+
}
281+
282+
public function testOmitEventNameOnUntypedListener(): void
283+
{
284+
$container = new ContainerBuilder();
285+
$container->register('foo', InvokableListenerService::class)->addTag('kernel.event_listener', ['method' => 'onEvent']);
286+
$container->register('event_dispatcher');
287+
288+
$this->expectException(InvalidArgumentException::class);
289+
$this->expectExceptionMessage('Service "foo" must define the "event" attribute on "kernel.event_listener" tags.');
290+
291+
$registerListenersPass = new RegisterListenersPass();
292+
$registerListenersPass->process($container);
293+
}
294+
295+
public function testOmitEventNameAndMethodOnUntypedListener(): void
296+
{
297+
$container = new ContainerBuilder();
298+
$container->register('foo', InvokableListenerService::class)->addTag('kernel.event_listener');
299+
$container->register('event_dispatcher');
300+
301+
$this->expectException(InvalidArgumentException::class);
302+
$this->expectExceptionMessage('Service "foo" must define the "event" attribute on "kernel.event_listener" tags.');
303+
304+
$registerListenersPass = new RegisterListenersPass();
305+
$registerListenersPass->process($container);
306+
}
307+
308+
/**
309+
* @requires PHP 7.2
310+
*/
311+
public function testOmitEventNameAndMethodOnGenericListener(): void
312+
{
313+
$container = new ContainerBuilder();
314+
$container->register('foo', GenericListener::class)->addTag('kernel.event_listener');
315+
$container->register('event_dispatcher');
316+
317+
$this->expectException(InvalidArgumentException::class);
318+
$this->expectExceptionMessage('Service "foo" must define the "event" attribute on "kernel.event_listener" tags.');
319+
320+
$registerListenersPass = new RegisterListenersPass();
321+
$registerListenersPass->process($container);
322+
}
247323
}
248324

249325
class SubscriberService implements EventSubscriberInterface
@@ -285,3 +361,21 @@ final class AliasedEvent
285361
final class CustomEvent
286362
{
287363
}
364+
365+
final class TypedListener
366+
{
367+
public function __invoke(AliasedEvent $event)
368+
{
369+
}
370+
371+
public function onEvent(CustomEvent $event)
372+
{
373+
}
374+
}
375+
376+
final class GenericListener
377+
{
378+
public function __invoke(object $event): void
379+
{
380+
}
381+
}

0 commit comments

Comments
 (0)
0