Skip to content

Commit a6e5b75

Browse files
minor #50775 [EventDispatcher] Throw exception when listener method cannot be resolved (nikophil)
This PR was merged into the 5.4 branch. Discussion ---------- [EventDispatcher] Throw exception when listener method cannot be resolved | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | License | MIT | Doc PR | not needed while working on #50687 I've discovered bugs on how the method for listener is resolved: Given this code: ```php #[AsEventListener(event: KernelEvents::REQUEST, priority: 5)] class MyListenerNotInvokable { public function someMethod(RequestEvent $event): void {} } ``` A listener on `MyListenerNotInvokable::onKernelRequest()` is registered, and then an error is dispatched at runtime when the event is dispatched: `Error: Call to undefined method App\EventListener\MyListenerNotInvokable::onKernelRequest()` The problem comes from [this code](https://github.com/symfony/symfony/blob/6.4/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php#L85-L95): since the `method` is omitted in tag definition, we "camelize" the event's name. We then try to fallback on `__invoke` but because it does not exist, the wrong method name is kept. this PR fixes this behavior by throwing an exception at compile time if neither the camlized method exist, not the `__invoke` method. ping `@GromNaN` Commits ------- 2b3497f [EventDispatcher] [EventDispatcher] Throw exception when listener method cannot be resolved
2 parents 6d52175 + 2b3497f commit a6e5b75

File tree

2 files changed

+31
-3
lines changed

2 files changed

+31
-3
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ public function process(ContainerBuilder $container)
115115
], function ($matches) { return strtoupper($matches[0]); }, $event['event']);
116116
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
117117

118-
if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) {
118+
if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method'])) {
119+
if (!$r->hasMethod('__invoke')) {
120+
throw new InvalidArgumentException(sprintf('None of the "%s" or "__invoke" methods exist for the service "foo". Please define the "method" attribute on "kernel.event_listener" tags.', $event['method'], $id, $this->listenerTag));
121+
}
122+
119123
$event['method'] = '__invoke';
120124
}
121125
}

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,20 @@ public function testEventSubscriberUnresolvableClassName()
200200
public function testInvokableEventListener()
201201
{
202202
$container = new ContainerBuilder();
203-
$container->register('foo', \stdClass::class)->addTag('kernel.event_listener', ['event' => 'foo.bar']);
203+
$container->setParameter('event_dispatcher.event_aliases', [AliasedEvent::class => 'aliased_event']);
204+
205+
$container->register('foo', \get_class(new class() {
206+
public function onFooBar()
207+
{
208+
}
209+
}))->addTag('kernel.event_listener', ['event' => 'foo.bar']);
204210
$container->register('bar', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => 'foo.bar']);
205211
$container->register('baz', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => 'event']);
206-
$container->register('zar', \stdClass::class)->addTag('kernel.event_listener', ['event' => 'foo.bar_zar']);
212+
$container->register('zar', \get_class(new class() {
213+
public function onFooBarZar()
214+
{
215+
}
216+
}))->addTag('kernel.event_listener', ['event' => 'foo.bar_zar']);
207217
$container->register('event_dispatcher', \stdClass::class);
208218

209219
$registerListenersPass = new RegisterListenersPass();
@@ -247,6 +257,20 @@ public function testInvokableEventListener()
247257
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
248258
}
249259

260+
public function testItThrowsAnExceptionIfTagIsMissingMethodAndClassHasNoValidMethod()
261+
{
262+
$this->expectException(InvalidArgumentException::class);
263+
$this->expectExceptionMessage('None of the "onFooBar" or "__invoke" methods exist for the service "foo". Please define the "method" attribute on "kernel.event_listener" tags.');
264+
265+
$container = new ContainerBuilder();
266+
267+
$container->register('foo', \stdClass::class)->addTag('kernel.event_listener', ['event' => 'foo.bar']);
268+
$container->register('event_dispatcher', \stdClass::class);
269+
270+
$registerListenersPass = new RegisterListenersPass();
271+
$registerListenersPass->process($container);
272+
}
273+
250274
/**
251275
* @requires PHP 8
252276
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy