Events and event listening


During the execution of the symfony program, a large number of event notifications will be triggered. Your program can listen for these notifications and execute arbitrary code in response.

The internal events provided by Symfony itself are defined in the KernelEvents class. Third-party Bundles and class libraries will also trigger a large number of events, and your own program can trigger custom events.

All examples shown in this article, taking into account consistency, use the same KernelEvents::EXCEPTION event. In your own program, you can use any event and even mix several events in the same subscriber.

Create an event listener

The most common way to monitor an event is to register an event listener

// src/AppBundle/EventListener/ExceptionListener.phpnamespace AppBundle\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; class ExceptionListener{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        // You get the exception object from the received event
        // 你可以从接收到的事件中,取得异常对象
        $exception = $event->getException();
        $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
        // HttpExceptionInterface是一个特殊类型的异常,持有状态码和头信息的细节
        if ($exception instanceof HttpExceptionInterface) {
            $response->setStatusCode($exception->getStatusCode());
            $response->headers->replace($exception->getHeaders());
        } else {
            $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
        }         // Send the modified response object to the event
        // 发送修改后的响应对象到事件中
        $event->setResponse($response);
    }}


Every event must receive $event objects of "slightly different types". For the kernel.exception event, this object is GetResponseForExceptionEvent. To understand the type of "event object" received by each "event listener", refer to KernelEvents, or the documentation for the specific event you want to listen for.


Now that the class is created, you just need to register it as a service and tell Symfony this by using a special "tag" It is a "listener" for the kernel.exception event:

YAML:# app/config/services.ymlservices:
    app.exception_listener:
        class: AppBundle\EventListener\ExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception }
xml:<!-- app/config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">     <services>
        <service id="app.exception_listener"            class="AppBundle\EventListener\ExceptionListener">             <tag name="kernel.event_listener" event="kernel.exception" />
        </service>
    </services></container>
php:// app/config/services.php$container
    ->register('app.exception_listener', 'AppBundle\EventListener\ExceptionListener')
    ->addTag('kernel.event_listener', array('event' => 'kernel.exception'));

has an optional tag attribute which is method, which defines "Which method should be executed when the event is triggered". By default, the method name is on "CamelCase event name". If the event is kernel.exception, the default execution method is onKernelException().

Another optional tag attribute is priority, its default value is 0, which is used to control the order in which the monitor is executed (a monitor The higher the priority of the server, the earlier it will be executed). This is useful when you want to make sure a listener is executed before other listeners. Symfony's internal listeners have a priority range of -255 to 255, but your own listeners can use any positive or negative integer.

Create an event subscription

Another way to listen to events is event subscriberEvent subscription, which is a class that defines one or more Method for listening to one or more events. The main difference with event listening is that subscribers always know which event they are listening for.

In a given subscriber, different methods can listen to the same event. The order in which methods are executed is defined by the priority parameter in each method (the higher the priority, the earlier the method is called). To learn more about subscribers, refer to EventDispatcher Component.

The following example shows an event subscription, which defines several methods and listens to the same kernel.exception event:

// src/AppBundle/EventSubscriber/ExceptionSubscriber.phpnamespace AppBundle\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface;use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;use Symfony\Component\HttpKernel\KernelEvents; class ExceptionSubscriber implements EventSubscriberInterface{
    public static function getSubscribedEvents()
    {
        // return the subscribed events, their methods and priorities
        // 返回被订阅的事件,以及它们的方法和属性
        return array(
           KernelEvents::EXCEPTION => array(
               array('processException', 10),
               array('logException', 0),
               array('notifyException', -10),
           )
        );
    }     public function processException(GetResponseForExceptionEvent $event)
    {
        // ...
    }     public function logException(GetResponseForExceptionEvent $event)
    {
        // ...
    }     public function notifyException(GetResponseForExceptionEvent $event)
    {
        // ...
    }}

Now, you only need to add this class Register as a service and tag kernel.event_subscriber to tell Symofny that this is an event subscriber:

PHP:// app/config/services.php$container
    ->register(
        'app.exception_subscriber',
        'AppBundle\EventSubscriber\ExceptionSubscriber'
    )
    ->addTag('kernel.event_subscriber');
XML:<!-- app/config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">     <services>
        <service id="app.exception_subscriber"            class="AppBundle\EventSubscriber\ExceptionSubscriber">             <tag name="kernel.event_subscriber"/>
        </service>
    </services></container>
YAML:# app/config/services.ymlservices:
    app.exception_subscriber:
        class: AppBundle\EventSubscriber\ExceptionSubscriber
        tags:
            - { name: kernel.event_subscriber }

Request event, check Type

A single page can generate several requests (a master request, then multiple sub-requests), typically like How to embed a controller in a template). For Symfony core events, you may want to check to see if the event is a "main" request or a "sub" request:

// src/AppBundle/EventListener/RequestListener.phpnamespace AppBundle\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseEvent;use Symfony\Component\HttpKernel\HttpKernel;use Symfony\Component\HttpKernel\HttpKernelInterface; class RequestListener{
    public function onKernelRequest(GetResponseEvent $event)
    {
        if (!$event->isMasterRequest()) {
            // don't do anything if it's not the master request
            // 如果不是主请求,就什么也不做
            return;
        }         // ...
    }}

Specific behavior, like real request Checking this may not need to be done in the subrequest listener.

Listen or subscribe

The boundaries between listeners and subscribers may be blurred when used in the same program. Deciding which one to use often comes down to personal taste. However, each has its own advantages:

  • Subscribers are easy to reuse because the content related to the event exists in the class, not in the service Defining. This results in Symfony using subscribers internally;

  • #Listeners are more flexible, since bundles can conditionally turn them on or off, based on certain " option value".

Debug event listeners

Using the command line, you can find "which listeners are registered to the event dispatcher". To display all events and their listeners, run:

By specifying the event name, you can get the listeners registered for this specific event:

##
1
$  php bin/console debug:event-dispatcher
##
1
$  php bin/console debug:event-dispatcher kernel.exception