Cómo crear un escucha de evento

Symfony dispone de varios eventos y ganchos que puedes utilizar para desencadenar un comportamiento personalizado en tu aplicación. Estos eventos los desencadena el componente HttpKernel y se pueden ver en la clase Symfony\Component\HttpKernel\KernelEvents.

Para enganchar a un evento y agregar tu propia lógica personalizada, tienes que crear un servicio que actúe como un escucha de eventos en ese evento. En este artículo, vas a crear un servicio que actúe como un Escucha de excepción, permitiéndote modificar la forma en que tu aplicación exhibe las excepciones. El evento KernelEvents::EXCEPTION es sólo uno de los eventos principales del núcleo:

// src/Acme/DemoBundle/EventListener/AcmeExceptionListener.php
namespace Acme\DemoBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

class AcmeExceptionListener
{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        // consigues el objeto excepción del evento recibido
        $exception = $event->getException();
        $message = sprintf(
            'My Error says: %s with code: %s',
            $exception->getMessage(),
            $exception->getCode()
        );

        // Personaliza tu objeto respuesta para mostrar los detalles de la excepción
        $response = new Response();
        $response->setContent($message);

        // HttpExceptionInterface es un tipo de excepción especial que
        // contiene el código de estado y detalles de la cabecera
        if ($exception instanceof HttpExceptionInterface) {
            $response->setStatusCode($exception->getStatusCode());
            $response->headers->replace($exception->getHeaders());
        } else {
            $response->setStatusCode(500);
        }

        // envía el objeto respuesta modificado al evento
        $event->setResponse($response);
    }
}

Truco

Cada evento recibe un tipo de objeto $event ligeramente diferente. El evento kernel.exception, es de la clase Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent. Para ver qué tipo de objeto recibe cada escucha de eventos, consulta la clase Symfony\Component\HttpKernel\KernelEvents.

Ahora que has creado la clase, sólo la tienes que registrar como un servicio y notificar a Symfony que es un «escucha» para el evento kernel.exception usando una «etiqueta» especial:

  • YAML
    # app/config/config.yml
    services:
        kernel.listener.your_listener_name:
            class: Acme\DemoBundle\EventListener\AcmeExceptionListener
            tags:
                    - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
    
  • XML
    <!-- app/config/config.xml -->
    <service id="kernel.listener.your_listener_name" class="Acme\DemoBundle\EventListener\AcmeExceptionListener">
        <tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" />
    </service>
    
  • PHP
    // app/config/config.php
    $container
        ->register('kernel.listener.your_listener_name', 'Acme\DemoBundle\EventListener\AcmeExceptionListener')
        ->addTag('kernel.event_listener', array('event' => 'kernel.exception', 'method' => 'onKernelException'))
    ;
    

Nota

Existe una opción adicional para la etiqueta priority, esta no es obligatoria y por omisión es 0. Este valor puede ser desde -255 hasta 255, y los escuchas se ejecutan en ese orden de prioridad. Esto es útil cuando necesitas garantizar que un escucha se ejecuta antes que otro.

Eventos de petición, comprobación de tipo

Una sola página puede hacer varias peticiones (una petición maestra, y luego múltiples subpeticiones), por lo tanto cuando trabajas con la instancia del KernelEvents::REQUEST, podrías necesitar verificar el tipo de la petición. Esto se puede hacer fácilmente de la siguiente manera:

// src/Acme/DemoBundle/EventListener/AcmeRequestListener.php
namespace Acme\DemoBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;

class AcmeRequestListener
{
    public function onKernelRequest(GetResponseEvent $event)
    {
        if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) {
            // no hace nada si no es una petición mástil
            return;
        }

        // ...
    }
}

Truco

Dos tipos de peticiones están disponibles en la interfaz Symfony\Component\HttpKernel\HttpKernelInterface: HttpKernelInterface::MASTER_REQUEST y HttpKernelInterface::SUB_REQUEST.

Bifúrcame en GitHub