From 0f8f9fdf0d6203dac18555fd1805fe2eff00fa40 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 16:03:14 +0200 Subject: [PATCH 01/13] Documented the useAttributeAsKey() method --- components/config/definition.rst | 70 +++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index b79ad3853a2..c6a26f35469 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -200,25 +200,58 @@ Array Node Options Before defining the children of an array node, you can provide options like: -``useAttributeAsKey()`` - Provide the name of a child node, whose value should be used as the key in the resulting array. -``requiresAtLeastOneElement()`` - There should be at least one element in the array (works only when ``isRequired()`` is also - called). ``addDefaultsIfNotSet()`` - If any child nodes have default values, use them if explicit values haven't been provided. + If any child nodes have default values, use them if explicit values haven't + been provided. +``requiresAtLeastOneElement()`` + There should be at least one element in the array (works only when + ``isRequired()`` is also called). +``useAttributeAsKey()`` + Provide the name of a child node, whose value should be used as the key in + the resulting array. This method also defines the way config array keys are + treated, as explained in the following example. + +When the ``useAttributeAsKey()`` method is not used, the names of the array +elements (i.e. the array keys) are ignored when parsing the configuration. +Consider this example:: + + $rootNode + ->children() + ->arrayNode('parameters') + ->prototype('array') + ->children() + ->scalarNode('parameter1')->end() + ->scalarNode('parameter2')->end() + ->end() + ->end() + ->end() + ->end() + ; + +In YAML, the configuration might look like this: + +.. code-block:: yaml + + database: + parameters: [ 'value1', 'value2' ] -An example of this:: +In XML, the configuration might look like this: + +.. code-block:: xml + + ... + +However, if the ``useAttributeAsKey()`` method is set, the parsed configuration +will be completely different:: $rootNode ->children() ->arrayNode('parameters') - ->isRequired() - ->requiresAtLeastOneElement() - ->useAttributeAsKey('name') + ->useAttributeAsKey('value') ->prototype('array') ->children() - ->scalarNode('value')->isRequired()->end() + ->scalarNode('parameter1')->end() + ->scalarNode('parameter2')->end() ->end() ->end() ->end() @@ -231,12 +264,19 @@ In YAML, the configuration might look like this: database: parameters: - param1: { value: param1val } + parameter1: { value: 'value1' } + parameter2: { value: 'value2' } + +In XML, the configuration might look like this: + +.. code-block:: xml + + ... -In XML, each ``parameters`` node would have a ``name`` attribute (along with +In XML, each ``parameters`` node has a ``value`` attribute (along with ``value``), which would be removed and used as the key for that element in -the final array. The ``useAttributeAsKey`` is useful for normalizing how -arrays are specified between different formats like XML and YAML. +the final array. The ``useAttributeAsKey()`` method is useful for normalizing +how arrays are specified between different formats like XML and YAML. Default and required Values --------------------------- From e77c3b28e8c78863f97e202f2d4709a50dc347b1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 17:04:28 +0200 Subject: [PATCH 02/13] Rewritten the explanation about the useAttributeAsKey() method --- components/config/definition.rst | 143 +++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 34 deletions(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index c6a26f35469..f77855870f9 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -211,72 +211,147 @@ Before defining the children of an array node, you can provide options like: the resulting array. This method also defines the way config array keys are treated, as explained in the following example. -When the ``useAttributeAsKey()`` method is not used, the names of the array -elements (i.e. the array keys) are ignored when parsing the configuration. -Consider this example:: +A basic prototyped array configuration can be defined as follows:: - $rootNode + $node + ->fixXmlConfig('driver') ->children() - ->arrayNode('parameters') - ->prototype('array') - ->children() - ->scalarNode('parameter1')->end() - ->scalarNode('parameter2')->end() - ->end() - ->end() + ->arrayNode('drivers') + ->prototype('scalar')->end() ->end() ->end() ; -In YAML, the configuration might look like this: +When using the following YAML configuration: .. code-block:: yaml - database: - parameters: [ 'value1', 'value2' ] + drivers: ['mysql', 'sqlite'] -In XML, the configuration might look like this: +Or the following XML configuration: .. code-block:: xml - ... + msyql + sqlite -However, if the ``useAttributeAsKey()`` method is set, the parsed configuration -will be completely different:: +The processed configuration is:: - $rootNode + Array( + [0] => 'mysql' + [1] => 'sqlite' + ) + +A more complex example would be to define a prototyped array with children: + + $node + ->fixXmlConfig('connection') ->children() - ->arrayNode('parameters') - ->useAttributeAsKey('value') + ->arrayNode('connections') ->prototype('array') ->children() - ->scalarNode('parameter1')->end() - ->scalarNode('parameter2')->end() + ->scalarNode('table')->end() + ->scalarNode('user')->end() + ->scalarNode('password')->end() ->end() ->end() ->end() ->end() ; -In YAML, the configuration might look like this: +When using the following YAML configuration: .. code-block:: yaml - database: - parameters: - parameter1: { value: 'value1' } - parameter2: { value: 'value2' } + connections: + - { table: symfony, user: root, password: ~ } + - { table: foo, user: root, password: pa$$ } -In XML, the configuration might look like this: +Or the following XML configuration: .. code-block:: xml - ... + + + +The processed configuration is:: + + Array( + [0] => Array( + [table] => 'symfony' + [user] => 'root' + [password] => null + ) + [1] => Array( + [table] => 'foo' + [user] => 'root' + [password] => 'pa$$' + ) + ) + +The previous output matches the expected result. However, given the configuration +tree, when using the following YAML configuration: + +.. code-block:: yaml + + connections: + sf_connection: + table: symfony + user: root + password: ~ + default: + table: foo + user: root + password: pa$$ + +The output configuration will be exactly the same as before. In other words, the +``sf_connection`` and ``default`` configuration keys are lost. The reason is that +the Symfony Config component treats arrays as lists by default. + +In order to maintain the array keys use the ``useAttributeAsKey()`` method:: + + $node + ->fixXmlConfig('connection') + ->children() + ->arrayNode('connections') + ->prototype('array') + ->useAttributeAsKey('name') + ->children() + ->scalarNode('table')->end() + ->scalarNode('user')->end() + ->scalarNode('password')->end() + ->end() + ->end() + ->end() + ->end() + ; + +The argument of this method (``name`` in the example above) defines the name of +the attribute added to each XML node to differentiate them. Now you can use the +same YAML configuration showed before or the following XML configuration: + +.. code-block:: xml -In XML, each ``parameters`` node has a ``value`` attribute (along with -``value``), which would be removed and used as the key for that element in -the final array. The ``useAttributeAsKey()`` method is useful for normalizing -how arrays are specified between different formats like XML and YAML. + + + +In both cases, the processed configuration maintains the ``sf_connection`` and +``default`` keys:: + + Array( + [sf_connection] => Array( + [table] => 'symfony' + [user] => 'root' + [password] => null + ) + [default] => Array( + [table] => 'foo' + [user] => 'root' + [password] => 'pa$$' + ) + ) Default and required Values --------------------------- From 9fe902060cf1a957998bba52c551f23c36fe6ef1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 26 May 2015 12:48:00 +0200 Subject: [PATCH 03/13] Fixed a minor syntax issue --- components/config/definition.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index f77855870f9..abce20b6a2c 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -242,7 +242,7 @@ The processed configuration is:: [1] => 'sqlite' ) -A more complex example would be to define a prototyped array with children: +A more complex example would be to define a prototyped array with children:: $node ->fixXmlConfig('connection') From 0184e0f0ece5a1e9841440325e8cade181390582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Miguel=20Benito=20Calzada?= Date: Mon, 1 Dec 2014 20:24:37 +0100 Subject: [PATCH 04/13] Added a note about the priority meaning in event subscribers Conflicts: cookbook/service_container/event_listener.rst --- cookbook/event_dispatcher/event_listener.rst | 160 ++++++++++++++++--- 1 file changed, 139 insertions(+), 21 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 3dd9987266c..41d2578b1c7 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -1,27 +1,34 @@ .. 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. 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:: +a service that listens to that event. You can do that in two different ways, +creating an event listener or an event subscriber instead. In this entry, +you will see the two ways of creating 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. - // src/AppBundle/EventListener/AcmeExceptionListener.php +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) { @@ -43,7 +50,7 @@ event is just one of the core kernel events:: $response->setStatusCode($exception->getStatusCode()); $response->headers->replace($exception->getHeaders()); } else { - $response->setStatusCode(500); + $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR); } // Send the modified response object to the event @@ -51,18 +58,15 @@ event is just one of the core kernel events:: } } +.. versionadded:: 2.4 + Support for HTTP status code constants was introduced in Symfony 2.4. + .. tip:: Each event receives a slightly different type of ``$event`` object. For 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": @@ -71,7 +75,7 @@ using a special "tag": .. code-block:: yaml - # app/config/services.yml + # app/config/config.yml services: kernel.listener.your_listener_name: class: AppBundle\EventListener\AcmeExceptionListener @@ -80,14 +84,14 @@ using a special "tag": .. code-block:: xml - + .. code-block:: php - // app/config/services.php + // app/config/config.php $container ->register('kernel.listener.your_listener_name', 'AppBundle\EventListener\AcmeExceptionListener') ->addTag('kernel.event_listener', array('event' => 'kernel.exception', 'method' => 'onKernelException')) @@ -96,12 +100,124 @@ using a special "tag": .. 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. + to 0. This value can be from -255 to 255, and 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. + +Creating an Event Subscriber +---------------------------- + +Another way to listen to events is via an event subscriber. An event subscriber +can define one or various methods that listen to one or various events, +and can set a priority for each method. The higher the priority, the earlier +the method is called. To learn more about event subscribers, see :doc:`/components/event_dispatcher/introduction`. +The following example shows a subscriber that subscribes various methods +to the ``kernel.exception`` event:: + + // src/AppBundle/EventListener/ExceptionSubscriber.php + namespace AppBundle\EventSubscriber; + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; + + class ExceptionSubscriber implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + // Return the events it is subscribed to, the methods that listen each event and the + // priority of each method + return array( + 'kernel.exception' => array( + array('onKernelExceptionPre', 10), + array('onKernelExceptionMid', 5), + array('onKernelExceptionPost', 0), + ) + ); + } + + public function onKernelExceptionPre(GetResponseForExceptionEvent $event) + { + $exception = $event->getException(); + $message = sprintf( + 'My Error says: %s with code: %s', + $exception->getMessage(), + $exception->getCode() + ); + + $response = new Response(); + $response->setContent($message); + + if ($exception instanceof HttpExceptionInterface) { + $response->setStatusCode($exception->getStatusCode()); + $response->headers->replace($exception->getHeaders()); + } else { + $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $event->setResponse($response); + } + + public function onKernerlExceptionMid(GetResponseForExceptionEvent $event) + { + // ... + } + + public function onKernerlExceptionPost(GetResponseForExceptionEvent $event) + { + // ... + } + } + +Now, you just need to register the class as a service and notify Symfony that it +is an event subscriber: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + kernel.listener.your_subscriber_name: + class: AppBundle\EventSubscriber\AcmeExceptionSubscriber + tags: + - { name: kernel.event_subscriber } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container + ->register( + 'acme_exception_subscriber', + 'AppBundle\EventSubscriber\AcmeExceptionSubscriber' + ) + ->addTag('kernel.event_subscriber') + ; Request Events, Checking Types ------------------------------ +.. versionadded:: 2.4 + The ``isMasterRequest()`` method was introduced in Symfony 2.4. + Prior, the ``getRequestType()`` method must be used. + A single page can make several requests (one master request, and then multiple 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 @@ -117,7 +233,7 @@ done as follow:: { public function onKernelRequest(GetResponseEvent $event) { - if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) { + if (!$event->isMasterRequest()) { // don't do anything if it's not the master request return; } @@ -131,3 +247,5 @@ done as follow:: Two types of request are available in the :class:`Symfony\\Component\\HttpKernel\\HttpKernelInterface` interface: ``HttpKernelInterface::MASTER_REQUEST`` and ``HttpKernelInterface::SUB_REQUEST``. + +.. _`The EventDispatcher component`: http://symfony.com/doc/current/components/event_dispatcher/index.html From 9a6dab70bc297650b4874ee33d1fe8f7cd007297 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 10 Jun 2015 20:04:41 +0200 Subject: [PATCH 05/13] Completed the cookbook about the event subscriber --- cookbook/event_dispatcher/event_listener.rst | 112 +++++++++---------- 1 file changed, 51 insertions(+), 61 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 41d2578b1c7..43089685cd6 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -6,23 +6,25 @@ 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. +actions in your application. Those events are thrown by the HttpKernel +component and they are defined in the :class:`Symfony\\Component\\HttpKernel\\KernelEvents` +class. -To hook into an event and add your own custom logic, you have to create -a service that listens to that event. You can do that in two different ways, -creating an event listener or an event subscriber instead. In this entry, -you will see the two ways of creating 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. +To hook into an event and execute your own custom logic, you have to create +a service that listens to that event. As explained in this article, you can do +that in two different ways: creating an event listener or an event subscriber. + +The examples of this article only use the ``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:: +The most common way to listen to an event is to register an **event listener**:: - // src/AppBundle/EventListener/ExceptionListener.php - namespace AppBundle\EventListener; + // src/AppBundle/Listener/ExceptionListener.php + namespace AppBundle\Listener; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Response; @@ -78,44 +80,50 @@ using a special "tag": # app/config/config.yml services: kernel.listener.your_listener_name: - class: AppBundle\EventListener\AcmeExceptionListener + class: AppBundle\Listener\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/config.php $container - ->register('kernel.listener.your_listener_name', 'AppBundle\EventListener\AcmeExceptionListener') - ->addTag('kernel.event_listener', array('event' => 'kernel.exception', 'method' => 'onKernelException')) + ->register('kernel.listener.your_listener_name', 'AppBundle\Listener\ExceptionListener') + ->addTag('kernel.event_listener', array('event' => 'kernel.exception')) ; .. note:: - There is an additional tag option ``priority`` that is optional and defaults - to 0. This value can be from -255 to 255, and 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 option 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 option is called ``priority`` and it defaults to ``0``. + This value ranges from ``-255`` to ``255`` 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. Creating an Event Subscriber ---------------------------- -Another way to listen to events is via an event subscriber. An event subscriber -can define one or various methods that listen to one or various events, -and can set a priority for each method. The higher the priority, the earlier -the method is called. To learn more about event subscribers, see :doc:`/components/event_dispatcher/introduction`. -The following example shows a subscriber that subscribes various methods -to the ``kernel.exception`` event:: +Another way to listen to events is via an **event subscriber**, which is a class +that can define one or more methods that listen to one or various events. The +event priority can be defined for 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/EventListener/ExceptionSubscriber.php - namespace AppBundle\EventSubscriber; + // src/AppBundle/Subscriber/ExceptionSubscriber.php + namespace AppBundle\Subscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -126,45 +134,27 @@ to the ``kernel.exception`` event:: { public static function getSubscribedEvents() { - // Return the events it is subscribed to, the methods that listen each event and the - // priority of each method + // return the subscribed events, their methods and priorities return array( 'kernel.exception' => array( - array('onKernelExceptionPre', 10), - array('onKernelExceptionMid', 5), - array('onKernelExceptionPost', 0), + array('processException', 10), + array('logException', 0), + array('notifyException', -10), ) ); } - public function onKernelExceptionPre(GetResponseForExceptionEvent $event) + public function processException(GetResponseForExceptionEvent $event) { - $exception = $event->getException(); - $message = sprintf( - 'My Error says: %s with code: %s', - $exception->getMessage(), - $exception->getCode() - ); - - $response = new Response(); - $response->setContent($message); - - if ($exception instanceof HttpExceptionInterface) { - $response->setStatusCode($exception->getStatusCode()); - $response->headers->replace($exception->getHeaders()); - } else { - $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $event->setResponse($response); + // ... } - public function onKernerlExceptionMid(GetResponseForExceptionEvent $event) + public function logException(GetResponseForExceptionEvent $event) { // ... } - public function onKernerlExceptionPost(GetResponseForExceptionEvent $event) + public function notifyException(GetResponseForExceptionEvent $event) { // ... } @@ -180,7 +170,7 @@ is an event subscriber: # app/config/config.yml services: kernel.listener.your_subscriber_name: - class: AppBundle\EventSubscriber\AcmeExceptionSubscriber + class: AppBundle\Subscriber\ExceptionSubscriber tags: - { name: kernel.event_subscriber } @@ -192,7 +182,7 @@ is an event subscriber: + class="AppBundle\Subscriber\ExceptionSubscriber"> @@ -206,7 +196,7 @@ is an event subscriber: $container ->register( 'acme_exception_subscriber', - 'AppBundle\EventSubscriber\AcmeExceptionSubscriber' + 'AppBundle\Subscriber\ExceptionSubscriber' ) ->addTag('kernel.event_subscriber') ; @@ -223,13 +213,13 @@ 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 - namespace AppBundle\EventListener; + // src/AppBundle/Listener/RequestListener.php + namespace AppBundle\Listener; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\HttpKernel; - class AcmeRequestListener + class RequestListener { public function onKernelRequest(GetResponseEvent $event) { From a44495115428ac959f4acc948266199a26762d5d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 12 Jun 2015 22:11:01 +0200 Subject: [PATCH 06/13] Implemented the suggestions made by @xabbuh --- cookbook/event_dispatcher/event_listener.rst | 38 ++++++++------------ 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 43089685cd6..19c1a4c3ae7 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -5,8 +5,8 @@ How to Create Event Listeners and Subscribers ============================================= -Symfony has various events and hooks that can be used to trigger custom -actions in your application. Those events are thrown by the HttpKernel +Symfony has various events and hooks that can be used to perform custom +actions in your application. Those events are triggered by the HttpKernel component and they are defined in the :class:`Symfony\\Component\\HttpKernel\\KernelEvents` class. @@ -15,7 +15,7 @@ a service that listens to that event. As explained in this article, you can do that in two different ways: creating an event listener or an event subscriber. The examples of this article only use the ``KernelEvents::EXCEPTION`` event for -consistency purposes. In your own application you can use any event and even mix +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 @@ -52,7 +52,7 @@ The most common way to listen to an event is to register an **event listener**:: $response->setStatusCode($exception->getStatusCode()); $response->headers->replace($exception->getHeaders()); } else { - $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR); + $response->setStatusCode(500); } // Send the modified response object to the event @@ -60,9 +60,6 @@ The most common way to listen to an event is to register an **event listener**:: } } -.. versionadded:: 2.4 - Support for HTTP status code constants was introduced in Symfony 2.4. - .. tip:: Each event receives a slightly different type of ``$event`` object. For @@ -77,7 +74,7 @@ using a special "tag": .. code-block:: yaml - # app/config/config.yml + # app/config/services.yml services: kernel.listener.your_listener_name: class: AppBundle\Listener\ExceptionListener @@ -86,14 +83,14 @@ using a special "tag": .. code-block:: xml - + .. code-block:: php - // app/config/config.php + // app/config/services.php $container ->register('kernel.listener.your_listener_name', 'AppBundle\Listener\ExceptionListener') ->addTag('kernel.event_listener', array('event' => 'kernel.exception')) @@ -101,12 +98,12 @@ using a special "tag": .. note:: - There is an optional tag option called ``method`` which defines which method + 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 option is called ``priority`` and it defaults to ``0``. + The other optional tag attribute is called ``priority`` which defaults to ``0``. This value ranges from ``-255`` to ``255`` 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 @@ -126,8 +123,8 @@ listen to the same ``kernel.exception`` event:: namespace AppBundle\Subscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; class ExceptionSubscriber implements EventSubscriberInterface @@ -169,7 +166,7 @@ is an event subscriber: # app/config/config.yml services: - kernel.listener.your_subscriber_name: + app.exception_subscriber: class: AppBundle\Subscriber\ExceptionSubscriber tags: - { name: kernel.event_subscriber } @@ -181,7 +178,7 @@ is an event subscriber: - @@ -195,7 +192,7 @@ is an event subscriber: // app/config/config.php $container ->register( - 'acme_exception_subscriber', + 'app.exception_subscriber', 'AppBundle\Subscriber\ExceptionSubscriber' ) ->addTag('kernel.event_subscriber') @@ -204,10 +201,6 @@ is an event subscriber: Request Events, Checking Types ------------------------------ -.. versionadded:: 2.4 - The ``isMasterRequest()`` method was introduced in Symfony 2.4. - Prior, the ``getRequestType()`` method must be used. - A single page can make several requests (one master request, and then multiple 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 @@ -218,12 +211,13 @@ done as follow:: 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()) { + if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) { // don't do anything if it's not the master request return; } @@ -237,5 +231,3 @@ done as follow:: Two types of request are available in the :class:`Symfony\\Component\\HttpKernel\\HttpKernelInterface` interface: ``HttpKernelInterface::MASTER_REQUEST`` and ``HttpKernelInterface::SUB_REQUEST``. - -.. _`The EventDispatcher component`: http://symfony.com/doc/current/components/event_dispatcher/index.html From 36b1d106a83ca94c56420335768d148269a34322 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Jun 2015 15:01:58 +0200 Subject: [PATCH 07/13] Fixed the name of the services file --- cookbook/event_dispatcher/event_listener.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 19c1a4c3ae7..6ef9f399026 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -164,7 +164,7 @@ is an event subscriber: .. code-block:: yaml - # app/config/config.yml + # app/config/services.yml services: app.exception_subscriber: class: AppBundle\Subscriber\ExceptionSubscriber @@ -173,7 +173,7 @@ is an event subscriber: .. code-block:: xml - + @@ -189,7 +189,7 @@ is an event subscriber: .. code-block:: php - // app/config/config.php + // app/config/services.php $container ->register( 'app.exception_subscriber', From c8c8bf8497333a4414646a1787ea85e80c76d6a0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Jun 2015 15:06:10 +0200 Subject: [PATCH 08/13] Reworded the subscriber introduction --- cookbook/event_dispatcher/event_listener.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 6ef9f399026..f736b9208a6 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -113,9 +113,15 @@ Creating an Event Subscriber ---------------------------- Another way to listen to events is via an **event subscriber**, which is a class -that can define one or more methods that listen to one or various events. The -event priority can be defined for each method (the higher the priority, the earlier -the method is called). To learn more about event subscribers, read :doc:`/components/event_dispatcher/introduction`. +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:: From e56fed8cd4f11b3a711209aa5df64fc5ecd9bee8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 08:56:08 +0200 Subject: [PATCH 09/13] Fixed minor issues --- cookbook/event_dispatcher/event_listener.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index f736b9208a6..a52732b8c75 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -103,11 +103,12 @@ using a special "tag": ``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``. - This value ranges from ``-255`` to ``255`` 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 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 events range from ``-255`` to ``255`` but your own + events can use any positive or negative integer. Creating an Event Subscriber ---------------------------- @@ -119,7 +120,7 @@ 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 +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 From 483f029433ce9bc8b9b6a8bab849de5069e58f67 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 7 Jul 2015 16:46:03 +0200 Subject: [PATCH 10/13] Added a note about the advantages/drawbacks of listeners/subscribers --- cookbook/event_dispatcher/event_listener.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index a52732b8c75..8c7ba323791 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -238,3 +238,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. From ba9ec6b1acc5aa3a55d79fc24b8001375fba57ba Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 2 Sep 2015 16:37:53 +0200 Subject: [PATCH 11/13] Reworded the introduction and other minor fixes --- cookbook/event_dispatcher/event_listener.rst | 29 ++++++++++---------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 8c7ba323791..681862986e4 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -5,18 +5,18 @@ How to Create Event Listeners and Subscribers ============================================= -Symfony has various events and hooks that can be used to perform custom -actions in your application. Those events are triggered by the HttpKernel -component and they are defined 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 execute your own custom logic, you have to create -a service that listens to that event. As explained in this article, you can do -that in two different ways: creating an event listener or an event subscriber. +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 `. -The examples of this article only use the ``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. +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 -------------------------- @@ -107,8 +107,8 @@ using a special "tag": ``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 events range from ``-255`` to ``255`` but your own - events can use any positive or negative integer. + 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 ---------------------------- @@ -182,14 +182,15 @@ is an event subscriber: - + - From b5a82ca4eab8bf2982f7b39dc5346e321efac3ce Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 5 Sep 2015 15:54:14 +0200 Subject: [PATCH 12/13] Final changes --- cookbook/event_dispatcher/event_listener.rst | 44 ++++++++++++-------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 681862986e4..939646339a9 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -23,8 +23,8 @@ Creating an Event Listener The most common way to listen to an event is to register an **event listener**:: - // src/AppBundle/Listener/ExceptionListener.php - namespace AppBundle\Listener; + // src/AppBundle/EventListener/ExceptionListener.php + namespace AppBundle\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Response; @@ -76,23 +76,33 @@ using a special "tag": # app/config/services.yml services: - kernel.listener.your_listener_name: - class: AppBundle\Listener\ExceptionListener + app.exception_listener: + class: AppBundle\EventListener\ExceptionListener tags: - { 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\Listener\ExceptionListener') + ->register('app.exception_listener', 'AppBundle\EventListener\ExceptionListener') ->addTag('kernel.event_listener', array('event' => 'kernel.exception')) ; @@ -126,8 +136,8 @@ 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/Subscriber/ExceptionSubscriber.php - namespace AppBundle\Subscriber; + // src/AppBundle/EventSubscriber/ExceptionSubscriber.php + namespace AppBundle\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Response; @@ -164,8 +174,8 @@ listen to the same ``kernel.exception`` event:: } } -Now, you just need to register the class as a service and notify Symfony that it -is an event subscriber: +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:: @@ -174,7 +184,7 @@ is an event subscriber: # app/config/services.yml services: app.exception_subscriber: - class: AppBundle\Subscriber\ExceptionSubscriber + class: AppBundle\EventSubscriber\ExceptionSubscriber tags: - { name: kernel.event_subscriber } @@ -188,7 +198,7 @@ is an event subscriber: + class="AppBundle\EventSubscriber\ExceptionSubscriber"> @@ -201,7 +211,7 @@ is an event subscriber: $container ->register( 'app.exception_subscriber', - 'AppBundle\Subscriber\ExceptionSubscriber' + 'AppBundle\EventSubscriber\ExceptionSubscriber' ) ->addTag('kernel.event_subscriber') ; @@ -214,8 +224,8 @@ 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/Listener/RequestListener.php - namespace AppBundle\Listener; + // src/AppBundle/EventListener/RequestListener.php + namespace AppBundle\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\HttpKernel; From dd46bcd5e6e9afc74f0531110711ef2d18c08ac2 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 13 Oct 2015 21:01:22 -0400 Subject: [PATCH 13/13] Minor tweaks --- cookbook/event_dispatcher/event_listener.rst | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 939646339a9..3d50183110d 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -64,7 +64,8 @@ The most common way to listen to an event is to register an **event listener**:: Each event receives a slightly different type of ``$event`` object. For 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`. + To see what type of object each event listener receives, see :class:`Symfony\\Component\\HttpKernel\\KernelEvents` + or the documentation about the specific even you're listening to. 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 @@ -220,9 +221,9 @@ Request Events, Checking Types ------------------------------ A single page can make several requests (one master request, and then multiple -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:: +sub-requests - typically by :ref:`templating-embedding-controller`). For the core +Symfony events, you might need to check to see if the event is for a "master" request +or a "sub request": // src/AppBundle/EventListener/RequestListener.php namespace AppBundle\EventListener; @@ -244,11 +245,8 @@ done as follow:: } } -.. tip:: - - Two types of request are available in the :class:`Symfony\\Component\\HttpKernel\\HttpKernelInterface` - interface: ``HttpKernelInterface::MASTER_REQUEST`` and - ``HttpKernelInterface::SUB_REQUEST``. +Certain things, like checking information on the *real* request, may not need to +be done on the sub-request listeners. Events or Subscribers ---------------------