diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 3dd9987266c..939646339a9 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -1,27 +1,36 @@ .. index:: single: Events; Create listener + single: Create subscriber -How to Create an Event Listener -=============================== +How to Create Event Listeners and Subscribers +============================================= -Symfony has various events and hooks that can be used to trigger custom -behavior in your application. Those events are thrown by the HttpKernel -component and can be viewed in the :class:`Symfony\\Component\\HttpKernel\\KernelEvents` class. +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. -To hook into an event and add your own custom logic, you have to create -a service that will act as an event listener on that event. In this entry, -you will create a service that will act as an exception listener, allowing -you to modify how exceptions are shown by your application. The ``KernelEvents::EXCEPTION`` -event is just one of the core kernel events:: +Internal events provided by Symfony itself are defined in the +:class:`Symfony\\Component\\HttpKernel\\KernelEvents` class. Third-party bundles +and libraries also trigger lots of events and your own application can trigger +:doc:`custom events `. - // src/AppBundle/EventListener/AcmeExceptionListener.php +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**:: + + // src/AppBundle/EventListener/ExceptionListener.php namespace AppBundle\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; - class AcmeExceptionListener + class ExceptionListener { public function onKernelException(GetResponseForExceptionEvent $event) { @@ -57,12 +66,6 @@ event is just one of the core kernel events:: the ``kernel.exception`` event, it is :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent`. To see what type of object each event listener receives, see :class:`Symfony\\Component\\HttpKernel\\KernelEvents`. -.. note:: - - When setting a response for the ``kernel.request``, ``kernel.view`` or - ``kernel.exception`` events, the propagation is stopped, so the lower - priority listeners on that event don't get called. - Now that the class is created, you just 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": @@ -73,31 +76,145 @@ using a special "tag": # app/config/services.yml services: - kernel.listener.your_listener_name: - class: AppBundle\EventListener\AcmeExceptionListener + app.exception_listener: + class: AppBundle\EventListener\ExceptionListener tags: - - { name: kernel.event_listener, event: kernel.exception, method: onKernelException } + - { name: kernel.event_listener, event: kernel.exception } .. code-block:: xml - - - + + + + + + + + + + .. code-block:: php // app/config/services.php $container - ->register('kernel.listener.your_listener_name', 'AppBundle\EventListener\AcmeExceptionListener') - ->addTag('kernel.event_listener', array('event' => 'kernel.exception', 'method' => 'onKernelException')) + ->register('app.exception_listener', 'AppBundle\EventListener\ExceptionListener') + ->addTag('kernel.event_listener', array('event' => 'kernel.exception')) ; .. note:: - There is an additional tag option ``priority`` that is optional and defaults - to 0. The listeners will be executed in the order of their priority (highest to lowest). - This is useful when you need to guarantee that one listener is executed before another. + There is an optional tag attribute called ``method`` which defines which method + to execute when the event is triggered. By default the name of the method is + ``on`` + "camel-cased event name". If the event is ``kernel.exception`` the + method executed by default is ``onKernelException()``. + + The other optional tag attribute is called ``priority``, which defaults to + ``0`` and it controls the order in which listeners are executed (the highest + the priority, 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 ``-255`` to ``255`` but + your own listeners can use any positive or negative integer. + +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 which events +they are listening to. + +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 of each +method (the higher the priority the earlier the method is called). To learn more +about event subscribers, read :doc:`/components/event_dispatcher/introduction`. + +The following example shows an event subscriber that defines several methods which +listen to the same ``kernel.exception`` event:: + + // src/AppBundle/EventSubscriber/ExceptionSubscriber.php + namespace AppBundle\EventSubscriber; + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; + use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; + + class ExceptionSubscriber implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + // return the subscribed events, their methods and priorities + return array( + 'kernel.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 just need to register the class as a service and add the +``kernel.event_subscriber`` tag to tell Symfony that this is an event subscriber: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + app.exception_subscriber: + class: AppBundle\EventSubscriber\ExceptionSubscriber + tags: + - { name: kernel.event_subscriber } + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/services.php + $container + ->register( + 'app.exception_subscriber', + 'AppBundle\EventSubscriber\ExceptionSubscriber' + ) + ->addTag('kernel.event_subscriber') + ; Request Events, Checking Types ------------------------------ @@ -107,17 +224,18 @@ sub-requests), which is why when working with the ``KernelEvents::REQUEST`` event, you might need to check the type of the request. This can be easily done as follow:: - // src/AppBundle/EventListener/AcmeRequestListener.php + // src/AppBundle/EventListener/RequestListener.php namespace AppBundle\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\HttpKernel; + use Symfony\Component\HttpKernel\HttpKernelInterface; - class AcmeRequestListener + class RequestListener { public function onKernelRequest(GetResponseEvent $event) { - if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) { + if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) { // don't do anything if it's not the master request return; } @@ -131,3 +249,16 @@ done as follow:: Two types of request are available in the :class:`Symfony\\Component\\HttpKernel\\HttpKernelInterface` interface: ``HttpKernelInterface::MASTER_REQUEST`` and ``HttpKernelInterface::SUB_REQUEST``. + +Events 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; +* **Listeners are more flexible** because bundles can enable or disable each of + them conditionally depending on some configuration value.