Events and Event Listeners ¶
During the execution of a Symfony application, lots of event notifications are triggered. Your application can listen to these notifications and respond to them by executing any piece of code.
Symfony triggers several events related to the kernel while processing the HTTP Request. Third-party bundles may also dispatch events, and you can even dispatch custom events from your own code.
All the examples shown in this article use the same KernelEvents::EXCEPTION
event for consistency purposes. In your own application, you can use any event
and even mix several of them in the same subscriber.
Creating an Event Listener ¶
The most common way to listen to an event is to register an event listener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// src/EventListener/ExceptionListener.php
namespace App\EventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class ExceptionListener
{
public function onKernelException(ExceptionEvent $event)
{
// You get the exception object from the received event
$exception = $event->getThrowable();
$message = sprintf(
'My Error says: %s with code: %s',
$exception->getMessage(),
$exception->getCode()
);
// Customize your response object to display the exception details
$response = new Response();
$response->setContent($message);
// HttpExceptionInterface is a special type of exception that
// holds status code and header details
if ($exception instanceof HttpExceptionInterface) {
$response->setStatusCode($exception->getStatusCode());
$response->headers->replace($exception->getHeaders());
} else {
$response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
}
// sends the modified response object to the event
$event->setResponse($response);
}
}
|
Tip
Each event receives a slightly different type of $event
object. For
the kernel.exception
event, it is ExceptionEvent.
Check out the Symfony events reference to see
what type of object each event provides.
Now that the class is created, you need to register it as a service and
notify Symfony that it is a "listener" on the kernel.exception
event by
using a special "tag":
-
YAML
YAML
-
XML
XML
-
PHP
PHP
1 2 3 4 5 |
# config/services.yaml
services:
App\EventListener\ExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.exception }
|
Symfony follows this logic to decide which method to call inside the event listener class:
- If the
kernel.event_listener
tag defines themethod
attribute, that's the name of the method to be called;kernel.event_listener タグが method 属性を定義する場合、それは呼び出されるメソッドの名前です。 - If no
method
attribute is defined, try to call the method whose name ison
+ "PascalCased event name" (e.g.onKernelException()
method for thekernel.exception
event);メソッド属性が定義されていない場合は、名前が on + "PascalCased イベント名" であるメソッドの呼び出しを試みます (たとえば、kernel.exception イベントの onKernelException() メソッド)。 - If that method is not defined either, try to call the
__invoke()
magic method (which makes event listeners invokable);そのメソッドも定義されていない場合は、__invoke() マジックメソッド (イベントリスナーを呼び出し可能にする) を呼び出してみてください。 - If the
__invoke()
method is not defined either, throw an exception.__invoke() メソッドも定義されていない場合は、例外をスローします。
Note
There is an optional attribute for the kernel.event_listener
tag called
priority
, which is a positive or negative integer that defaults to 0
and it controls the order in which listeners are executed (the higher the
number, the earlier a listener is executed). This is useful when you need to
guarantee that one listener is executed before another. The priorities of the
internal Symfony listeners usually range from -256
to 256
but your
own listeners can use any positive or negative integer.
Defining Event Listeners with PHP Attributes ¶
An alternative way to define an event listener is to use the AsEventListener PHP attribute. This allows to configure the listener inside its class, without having to add any configuration in external files:
1 2 3 4 5 6 7 8 9 10 11 12 |
namespace App\EventListener;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener]
final class MyListener
{
public function __invoke(CustomEvent $event): void
{
// ...
}
}
|
You can add multiple #[AsEventListener()]
attributes to configure different methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
namespace App\EventListener;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(event: CustomEvent::class, method: 'onCustomEvent')]
#[AsEventListener(event: 'foo', priority: 42)]
#[AsEventListener(event: 'bar', method: 'onBarEvent')]
final class MyMultiListener
{
public function onCustomEvent(CustomEvent $event): void
{
// ...
}
public function onFoo(): void
{
// ...
}
public function onBarEvent(): void
{
// ...
}
}
|
Creating an Event Subscriber ¶
Another way to listen to events is via an event subscriber, which is a class that defines one or more methods that listen to one or various events. The main difference with the event listeners is that subscribers always know the events to which they are listening.
If different event subscriber methods listen to the same event, their order is
defined by the priority
parameter. This value is a positive or negative
integer which defaults to 0
. The higher the number, the earlier the method
is called. Priority is aggregated for all listeners and subscribers, so your
methods could be called before or after the methods defined in other listeners
and subscribers. To learn more about event subscribers, read The EventDispatcher Component.
The following example shows an event subscriber that defines several methods which
listen to the same kernel.exception
event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// src/EventSubscriber/ExceptionSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class ExceptionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
// return the subscribed events, their methods and priorities
return [
KernelEvents::EXCEPTION => [
['processException', 10],
['logException', 0],
['notifyException', -10],
],
];
}
public function processException(ExceptionEvent $event)
{
// ...
}
public function logException(ExceptionEvent $event)
{
// ...
}
public function notifyException(ExceptionEvent $event)
{
// ...
}
}
|
That's it! Your services.yaml
file should already be setup to load services from
the EventSubscriber
directory. Symfony takes care of the rest.
Tip
If your methods are not called when an exception is thrown, double-check that
you're loading services from
the EventSubscriber
directory and have autoconfigure
enabled. You can also manually add the kernel.event_subscriber
tag.
Request Events, Checking Types ¶
A single page can make several requests (one main request, and then multiple sub-requests - typically when embedding controllers in templates). For the core Symfony events, you might need to check to see if the event is for a "main" request or a "sub request":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// src/EventListener/RequestListener.php
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class RequestListener
{
public function onKernelRequest(RequestEvent $event)
{
if (!$event->isMainRequest()) {
// don't do anything if it's not the main request
return;
}
// ...
}
}
|
Certain things, like checking information on the real request, may not need to be done on the sub-request listeners.
Listeners or Subscribers ¶
Listeners and subscribers can be used in the same application indistinctly. The decision to use either of them is usually a matter of personal taste. However, there are some minor advantages for each of them:
- Subscribers are easier to reuse because the knowledge of the events is kept
in the class rather than in the service definition. This is the reason why
Symfony uses subscribers internally;イベントの知識がサービス定義ではなくクラスに保持されるため、サブスクライバーは再利用しやすくなります。これが、Symfony がサブスクライバーを内部的に使用する理由です。
- Listeners are more flexible because bundles can enable or disable each of
them conditionally depending on some configuration value.バンドルは、構成値に応じて条件付きで各リスナーを有効または無効にできるため、リスナーはより柔軟です。
Event Aliases ¶
When configuring event listeners and subscribers via dependency injection, Symfony's core events can also be referred to by the fully qualified class name (FQCN) of the corresponding event class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// src/EventSubscriber/RequestSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class RequestSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
RequestEvent::class => 'onKernelRequest',
];
}
public function onKernelRequest(RequestEvent $event)
{
// ...
}
}
|
Internally, the event FQCN are treated as aliases for the original event names. Since the mapping already happens when compiling the service container, event listeners and subscribers using FQCN instead of event names will appear under the original event name when inspecting the event dispatcher.
This alias mapping can be extended for custom events by registering the
compiler pass AddEventAliasesPass
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// src/Kernel.php
namespace App;
use App\Event\MyCustomEvent;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\EventDispatcher\DependencyInjection\AddEventAliasesPass;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
protected function build(ContainerBuilder $container)
{
$container->addCompilerPass(new AddEventAliasesPass([
MyCustomEvent::class => 'my_custom_event',
]));
}
}
|
The compiler pass will always extend the existing list of aliases. Because of that, it is safe to register multiple instances of the pass with different configurations.
Debugging Event Listeners ¶
You can find out what listeners are registered in the event dispatcher using the console. To show all events and their listeners, run:
1 |
$ php bin/console debug:event-dispatcher
|
You can get registered listeners for a particular event by specifying its name:
1 |
$ php bin/console debug:event-dispatcher kernel.exception
|
or can get everything which partial matches the event name:
1 2 |
$ php bin/console debug:event-dispatcher kernel // matches "kernel.exception", "kernel.response" etc.
$ php bin/console debug:event-dispatcher Security // matches "Symfony\Component\Security\Http\Event\CheckPassportEvent"
|
The security system uses an event dispatcher per
firewall. Use the --dispatcher
option to get the registered listeners
for a particular event dispatcher:
1 |
$ php bin/console debug:event-dispatcher --dispatcher=security.event_dispatcher.main
|
Learn more ¶
- How to Set Up Before and After Filtersフィルタ前後の設定方法
- How to Customize a Method Behavior without Using Inheritance継承を使用せずにメソッドの動作をカスタマイズする方法