EventDispatcher Component – Eventos

El componente EventDispatcher de Symfony2 es uno de los más usados, este componente implementa el patrón observer y nos permite extender y cambiar el comportamiento de las clases sin modificarlas. En este artículo veremos cómo funciona y las distintas partes que lo componen, la semana que viene veremos en otro artículo este componente integrado dentro de Symfony2, cómo registrar listeners, lanzar eventos propios y las ventajas que ofrece usar eventos.

Observer Pattern

Este patrón consiste en un objeto, llamado subject, que tiene una lista de observadores, llamados listeners y cuando se llama a un método del objeto se notifica a los observadores de que se ha producido ese evento.

EventDispatcher Component

Este componente consta de 3 partes, por una parte los eventos que son los que se lanzan, por otra parte están los listeners que escuchan a esos eventos y finalmente está la clase EventDispatcher que se encarga de lanzar los eventos y avisar a los listeners que están suscritos a un determinado evento.

Eventos

Hay que diferenciar dos partes:

Nombre del evento: Es un nombre único y será el nombre que se le pase al EventDispatcher al lanzar el evento y el nombre al que se suscriben los listeners, este nombre debe seguir unas naming conventions. Para mantener el código ordenado se recomienda crear una clase final en la que se definan y documenten los nombres de los eventos que se van a lanzar.

Objetos Event: Cuando se lanza un evento lleva asociado un objeto Event que tiene que extender de la clase Event o usar la propia clase (también se puede lanzar un evento sin objeto, pero internamente lo crea). Este objeto entre otras cosas tiene un método stopPropagation que permite parar la propagación. Lo habitual es extender esta clase para añadirle un atributo en la que se guarda una referencia algún objeto que nos interese pasarlo a los listeners, ya que cuando se les invoque recibirán este evento.

Un ejemplo de Event:

Nuevo en la versión 2.1
Como lo habitual es crearse clases que extienden de Event para añadirle un objeto o argumentos en la versión 2.1 se ha añadido la clase GenericEvent que extiende de la clase Event y le añade un atributo $subject y otro $arguments.

8cf54f95 EventDispatcher Component   Eventos

Listeners

Hay dos tipos de listeners:

Listeners: Un listener suele ser una clase con un método que recibe como parámetro un Event o una subclase de Event.

Un ejemplo de Listener:

Subscribers: Un subscriber es una clase que implementa la interfaz EventSubscriberInterface y por lo tanto tiene que implementar el método estático getSubscribedEvents.

bc61ba3b EventDispatcher Component   Eventos

Un ejemplo de subscriber:

La diferencia está a la hora de registrarlos en el EventDispatcher, cuando es un listener hay que indicar qué metodo hay que llamar cuando se lance un evento. En cambio en el caso del subscriber sólo hay que registrar la clase, ya que en el método getSubscribedEvents indica a qué eventos se suscribe y a qué métodos tiene que llamar cuando se produzca cada evento. Además tanto en listeners como en subscribers se puede indicar una prioridad que afectará en el orden de ejecución de los listeners.

EventDispatcher

El dispatcher es el objeto central de este componente, es el que se encarga de registrar listeners, lanzar eventos y notificar a los listeners suscritos a un evento que se ha lanzado.

42762b6c EventDispatcher Component   Eventos

Además de la clase EventDispatcher también está la clase InmutableEventDispatcher que actúa de proxy y recibe como parámetro un objeto que implemente EventDispatcherInterface impidiendo que se puedas añadir o borrar listeners.

La clase ContainerAwareEventDispatcher extiende de EventDispatcher y recibe en el constructor el contenedor de dependencias, esta clase lo que permite es cargar los listeners bajo demanda del contenedor. De hecho cuando usamos este componente en una aplicación hecha en Symfony2 lo hacemos a través del servicio event_dispatcher que es una instancia de ContainerAwareEventDispatcher.

Para registrar un listener en el dispatcher lo hacemos a través del método addListener:

El segundo parámetro de addListener es un PHP callable, esto significa que puede ser un objeto \Closure, un objeto que implemente el método __invoke o un string que representa una función. También puede ser como en este caso un array con un objeto y el nombre del método que se ejecutará. El callable PHP recibe como parámetro un Event o una subclase de Event.

Para registrar un subscriber usamos el método addSubscriber:

Internamente el método getSubscribedEvents llama a addListener con los parámetros adecuados.

Ya tenemos evento y los listeners registrados sólo queda lanzarlo:

El método dispatch recibe como primer argumento el nombre del evento y como segundo el objeto evento, el método llama a todos los listeners registrados a este evento por orden y les pasa como parámetro el objeto evento.

Detener la propagación

La clase Event tiene un método stopPropagation que permite que una vez llamado no se notifique a los listeners que aún no han sido invocados:

Se puede saber si un evento se ha parado con el método isPropagationStopped:

Además de las interfaces y clases que hemos visto del componente, existe otra más, la interfaz TraceableEventDispatcherInterface que se encuentra dentro del directorio Debug, esta interfaz tiene los métodos getCalledListeners()getNotCalledListeners() y la usa Symfony2 para el entorno de desarrollo.

En el siguiente artículo veremos un ejemplo de uso de eventos.

Nota: Para los dibujos he usado Yuml, mola!