|
1 |
| -Event Dispatcher |
2 |
| -================ |
| 1 | +.. index:: |
| 2 | + single: Events; Create listener |
| 3 | + single: Create subscriber |
| 4 | + |
| 5 | +Events and Event Listeners |
| 6 | +========================== |
| 7 | + |
| 8 | +During the execution of a Symfony application, lots of event notifications are |
| 9 | +triggered. Your application can listen to these notifications and respond to |
| 10 | +them by executing any piece of code. |
| 11 | + |
| 12 | +Internal events provided by Symfony itself are defined in the |
| 13 | +:class:`Symfony\\Component\\HttpKernel\\KernelEvents` class. Third-party bundles |
| 14 | +and libraries also trigger lots of events and your own application can trigger |
| 15 | +:doc:`custom events </components/event_dispatcher>`. |
| 16 | + |
| 17 | +All the examples shown in this article use the same ``KernelEvents::EXCEPTION`` |
| 18 | +event for consistency purposes. In your own application, you can use any event |
| 19 | +and even mix several of them in the same subscriber. |
| 20 | + |
| 21 | +Creating an Event Listener |
| 22 | +-------------------------- |
| 23 | + |
| 24 | +The most common way to listen to an event is to register an **event listener**:: |
| 25 | + |
| 26 | + // src/AppBundle/EventListener/ExceptionListener.php |
| 27 | + namespace AppBundle\EventListener; |
| 28 | + |
| 29 | + use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; |
| 30 | + use Symfony\Component\HttpFoundation\Response; |
| 31 | + use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; |
| 32 | + |
| 33 | + class ExceptionListener |
| 34 | + { |
| 35 | + public function onKernelException(GetResponseForExceptionEvent $event) |
| 36 | + { |
| 37 | + // You get the exception object from the received event |
| 38 | + $exception = $event->getException(); |
| 39 | + $message = sprintf( |
| 40 | + 'My Error says: %s with code: %s', |
| 41 | + $exception->getMessage(), |
| 42 | + $exception->getCode() |
| 43 | + ); |
| 44 | + |
| 45 | + // Customize your response object to display the exception details |
| 46 | + $response = new Response(); |
| 47 | + $response->setContent($message); |
| 48 | + |
| 49 | + // HttpExceptionInterface is a special type of exception that |
| 50 | + // holds status code and header details |
| 51 | + if ($exception instanceof HttpExceptionInterface) { |
| 52 | + $response->setStatusCode($exception->getStatusCode()); |
| 53 | + $response->headers->replace($exception->getHeaders()); |
| 54 | + } else { |
| 55 | + $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR); |
| 56 | + } |
| 57 | + |
| 58 | + // Send the modified response object to the event |
| 59 | + $event->setResponse($response); |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | +.. tip:: |
| 64 | + |
| 65 | + Each event receives a slightly different type of ``$event`` object. For |
| 66 | + the ``kernel.exception`` event, it is :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent`. |
| 67 | + To see what type of object each event listener receives, see :class:`Symfony\\Component\\HttpKernel\\KernelEvents` |
| 68 | + or the documentation about the specific event you're listening to. |
| 69 | + |
| 70 | +Now that the class is created, you just need to register it as a service and |
| 71 | +notify Symfony that it is a "listener" on the ``kernel.exception`` event by |
| 72 | +using a special "tag": |
| 73 | + |
| 74 | +.. configuration-block:: |
| 75 | + |
| 76 | + .. code-block:: yaml |
| 77 | +
|
| 78 | + # app/config/services.yml |
| 79 | + services: |
| 80 | + app.exception_listener: |
| 81 | + class: AppBundle\EventListener\ExceptionListener |
| 82 | + tags: |
| 83 | + - { name: kernel.event_listener, event: kernel.exception } |
| 84 | +
|
| 85 | + .. code-block:: xml |
| 86 | +
|
| 87 | + <!-- app/config/services.xml --> |
| 88 | + <?xml version="1.0" encoding="UTF-8" ?> |
| 89 | + <container xmlns="http://symfony.com/schema/dic/services" |
| 90 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 91 | + xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> |
| 92 | +
|
| 93 | + <services> |
| 94 | + <service id="app.exception_listener" |
| 95 | + class="AppBundle\EventListener\ExceptionListener"> |
| 96 | +
|
| 97 | + <tag name="kernel.event_listener" event="kernel.exception" /> |
| 98 | + </service> |
| 99 | + </services> |
| 100 | + </container> |
| 101 | +
|
| 102 | + .. code-block:: php |
| 103 | +
|
| 104 | + // app/config/services.php |
| 105 | + $container |
| 106 | + ->register('app.exception_listener', 'AppBundle\EventListener\ExceptionListener') |
| 107 | + ->addTag('kernel.event_listener', array('event' => 'kernel.exception')) |
| 108 | + ; |
| 109 | +
|
| 110 | +.. note:: |
| 111 | + |
| 112 | + There is an optional tag attribute called ``method`` which defines which method |
| 113 | + to execute when the event is triggered. By default the name of the method is |
| 114 | + ``on`` + "camel-cased event name". If the event is ``kernel.exception`` the |
| 115 | + method executed by default is ``onKernelException()``. |
| 116 | + |
| 117 | + The other optional tag attribute is called ``priority``, which defaults to |
| 118 | + ``0`` and it controls the order in which listeners are executed (the highest |
| 119 | + the priority, the earlier a listener is executed). This is useful when you |
| 120 | + need to guarantee that one listener is executed before another. The priorities |
| 121 | + of the internal Symfony listeners usually range from ``-255`` to ``255`` but |
| 122 | + your own listeners can use any positive or negative integer. |
| 123 | + |
| 124 | +Creating an Event Subscriber |
| 125 | +---------------------------- |
| 126 | + |
| 127 | +Another way to listen to events is via an **event subscriber**, which is a class |
| 128 | +that defines one or more methods that listen to one or various events. The main |
| 129 | +difference with the event listeners is that subscribers always know which events |
| 130 | +they are listening to. |
| 131 | + |
| 132 | +In a given subscriber, different methods can listen to the same event. The order |
| 133 | +in which methods are executed is defined by the ``priority`` parameter of each |
| 134 | +method (the higher the priority the earlier the method is called). To learn more |
| 135 | +about event subscribers, read :doc:`/components/event_dispatcher`. |
| 136 | + |
| 137 | +The following example shows an event subscriber that defines several methods which |
| 138 | +listen to the same ``kernel.exception`` event:: |
| 139 | + |
| 140 | + // src/AppBundle/EventSubscriber/ExceptionSubscriber.php |
| 141 | + namespace AppBundle\EventSubscriber; |
| 142 | + |
| 143 | + use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
| 144 | + use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; |
| 145 | + use Symfony\Component\HttpKernel\KernelEvents; |
| 146 | + |
| 147 | + class ExceptionSubscriber implements EventSubscriberInterface |
| 148 | + { |
| 149 | + public static function getSubscribedEvents() |
| 150 | + { |
| 151 | + // return the subscribed events, their methods and priorities |
| 152 | + return array( |
| 153 | + KernelEvents::EXCEPTION => array( |
| 154 | + array('processException', 10), |
| 155 | + array('logException', 0), |
| 156 | + array('notifyException', -10), |
| 157 | + ) |
| 158 | + ); |
| 159 | + } |
| 160 | + |
| 161 | + public function processException(GetResponseForExceptionEvent $event) |
| 162 | + { |
| 163 | + // ... |
| 164 | + } |
| 165 | + |
| 166 | + public function logException(GetResponseForExceptionEvent $event) |
| 167 | + { |
| 168 | + // ... |
| 169 | + } |
| 170 | + |
| 171 | + public function notifyException(GetResponseForExceptionEvent $event) |
| 172 | + { |
| 173 | + // ... |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | +Now, you just need to register the class as a service and add the |
| 178 | +``kernel.event_subscriber`` tag to tell Symfony that this is an event subscriber: |
| 179 | + |
| 180 | +.. configuration-block:: |
| 181 | + |
| 182 | + .. code-block:: yaml |
| 183 | +
|
| 184 | + # app/config/services.yml |
| 185 | + services: |
| 186 | + app.exception_subscriber: |
| 187 | + class: AppBundle\EventSubscriber\ExceptionSubscriber |
| 188 | + tags: |
| 189 | + - { name: kernel.event_subscriber } |
| 190 | +
|
| 191 | + .. code-block:: xml |
| 192 | +
|
| 193 | + <!-- app/config/services.xml --> |
| 194 | + <?xml version="1.0" encoding="UTF-8" ?> |
| 195 | + <container xmlns="http://symfony.com/schema/dic/services" |
| 196 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 197 | + xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> |
| 198 | +
|
| 199 | + <services> |
| 200 | + <service id="app.exception_subscriber" |
| 201 | + class="AppBundle\EventSubscriber\ExceptionSubscriber"> |
| 202 | +
|
| 203 | + <tag name="kernel.event_subscriber"/> |
| 204 | + </service> |
| 205 | + </services> |
| 206 | + </container> |
| 207 | +
|
| 208 | + .. code-block:: php |
| 209 | +
|
| 210 | + // app/config/services.php |
| 211 | + $container |
| 212 | + ->register( |
| 213 | + 'app.exception_subscriber', |
| 214 | + 'AppBundle\EventSubscriber\ExceptionSubscriber' |
| 215 | + ) |
| 216 | + ->addTag('kernel.event_subscriber') |
| 217 | + ; |
| 218 | +
|
| 219 | +Request Events, Checking Types |
| 220 | +------------------------------ |
| 221 | + |
| 222 | +A single page can make several requests (one master request, and then multiple |
| 223 | +sub-requests - typically by :doc:`/templating/embedding_controllers`). For the core |
| 224 | +Symfony events, you might need to check to see if the event is for a "master" request |
| 225 | +or a "sub request":: |
| 226 | + |
| 227 | + // src/AppBundle/EventListener/RequestListener.php |
| 228 | + namespace AppBundle\EventListener; |
| 229 | + |
| 230 | + use Symfony\Component\HttpKernel\Event\GetResponseEvent; |
| 231 | + use Symfony\Component\HttpKernel\HttpKernel; |
| 232 | + use Symfony\Component\HttpKernel\HttpKernelInterface; |
| 233 | + |
| 234 | + class RequestListener |
| 235 | + { |
| 236 | + public function onKernelRequest(GetResponseEvent $event) |
| 237 | + { |
| 238 | + if (!$event->isMasterRequest()) { |
| 239 | + // don't do anything if it's not the master request |
| 240 | + return; |
| 241 | + } |
| 242 | + |
| 243 | + // ... |
| 244 | + } |
| 245 | + } |
| 246 | + |
| 247 | +Certain things, like checking information on the *real* request, may not need to |
| 248 | +be done on the sub-request listeners. |
| 249 | + |
| 250 | +.. _events-or-subscribers: |
| 251 | + |
| 252 | +Listeners or Subscribers |
| 253 | +------------------------ |
| 254 | + |
| 255 | +Listeners and subscribers can be used in the same application indistinctly. The |
| 256 | +decision to use either of them is usually a matter of personal taste. However, |
| 257 | +there are some minor advantages for each of them: |
| 258 | + |
| 259 | +* **Subscribers are easier to reuse** because the knowledge of the events is kept |
| 260 | + in the class rather than in the service definition. This is the reason why |
| 261 | + Symfony uses subscribers internally; |
| 262 | +* **Listeners are more flexible** because bundles can enable or disable each of |
| 263 | + them conditionally depending on some configuration value. |
| 264 | + |
| 265 | +Debugging Event Listeners |
| 266 | +------------------------- |
| 267 | + |
| 268 | +.. versionadded:: 2.6 |
| 269 | + The ``debug:event-dispatcher`` command was introduced in Symfony 2.6. |
| 270 | + |
| 271 | +You can find out what listeners are registered in the event dispatcher |
| 272 | +using the console. To show all events and their listeners, run: |
| 273 | + |
| 274 | +.. code-block:: bash |
| 275 | +
|
| 276 | + $ php app/console debug:event-dispatcher |
| 277 | +
|
| 278 | +You can get registered listeners for a particular event by specifying |
| 279 | +its name: |
| 280 | + |
| 281 | +.. code-block:: bash |
| 282 | +
|
| 283 | + $ php app/console debug:event-dispatcher kernel.exception |
3 | 284 |
|
4 | 285 | .. toctree::
|
5 | 286 | :maxdepth: 1
|
|
0 commit comments