El trastero de José Juan Valid XHTML 1.1 Valid CSS! Estilo de página alternativo
Artículo creado en 2008.
Valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración sobre 14 comentarios.

PHP Eventos

¿Qué son los eventos?

No es el objetivo de este artículo describir detalladamente en qué consiste la programación orientada a eventos. En general, de un programa se espera que tenga un punto de entrada, realice una serie de instrucciones y finalice en un punto de salida o término. Sin embargo, en muchas situaciones los procesos que deben realizarse no tienen un orden preestablecido, o este orden (en el que deben hacerse las cosas) cambia debido a factores que no conocemos o controlamos. Gestionar de forma correcta el orden en el que deben ejecutarse puede ser realmente complicado. La programación asociada a eventos, permite relacionar el conjunto de acciones que deben ejecutarse cuando ocurre una determinada "cosa". La ocurrencia de esta cosa es la que genera, dispara un evento y este evento conoce qué acciones deben realizarse.

Aun cuando sean conocidas las situaciones en que una determinada acción debiera ser ejecutada, la programación orientada a eventos provee de una técnica que simplifica enormente la gestión de estas interrelaciones de "acción - reacción".

Eventos en PHP

PHP no suministra dentro del lenguaje una gestión integral de eventos (que sí suministra, por ejemplo, C#), es por ello que muchos programadores deben implementar sus propias estrategias para dar soporte en sus aplicaciones.

He visto clases realmente feas y engorrosas para controlar eventos en PHP. No es que PHP lo deje fácil para hacer algo muy elegante, pero sí es posible dar soporte sencillo y cómodo.

Implementar eventos en PHP

Desde mi punto de vista, la gestión de eventos debería tener las siguientes características:

Lo más lógico es que el soporte de eventos esté disponible desde nuestra clase más básica, en los lenguajes de programación a dicha clase base se le suele denominar (cuando existe) object. Cualquier otra clase que herede de esta clase base, absorberá la capacidad de generar y gestionar eventos.

Para gestionar los eventos sólo precisamos dos métodos, uno para añadir un controlador de eventos al evento y otro para disparar el evento (y que se ejecuten todos los controladores asociados), ésto se resuelve de la siguiente forma:

abstract class mspwObject {
	public function EventHandler( $event, &$object, $method ) {
		$this->{'__event_' . $event}[] = array( $object, $method );
	}
	protected function EventFire( $event, Array $args ) {
		foreach( $this->{'__event_' . $event} as $w )
			$w[0]->{$w[1]}( $args );
		return;
	}
}

Para añadir un controlador de eventos sólo es preciso llamar al método EventHandler pasándole el nombre del evento, una referencia al objeto al que se llamará y el nombre del método del objeto que será invocado. El método lo único que hace es añadir a un vector la información necesaria: el objeto y el método a invocar.

Por otra parte, el objeto, cuando desea disparar un evento sólo tiene que llamar al método EventFire pasándole el evento que desea disparar y un vector con los parámetros propios del evento (por ejemplo, si se trata de un registro que ha sido actualizado en la base de datos, el vector podría contener los datos actualizados). La información proporcionada por el evento (en el array) depende lógicamente de quien dispara el evento, por eso sólo el "creador" del evento debería poder dispararlo, es por ello que el método es protegido, aunque en PHP ésto no tiene mucho sentido (cualquier objeto que herede mspwObject podrá invocar sin restricción a EventFire).

De forma adicional, sería bueno controlar que realmente el evento está disponible para el objeto, tanto si otro objeto pretende manejarlo como si el propio objeto quiere dispararlo. Para ésto, elevaremos sendas excepciones si el evento en cuestión no ha sido definido explíctamente en la clase:

abstract class mspwObject {
	public function EventHandler( $event, &$object, $method ) {
		$en = '__event_' . $event;
		if( ! is_array( $this->$en ) )
			throw new Exception('El evento ' . $event . ' no está definido en el objeto.');
		$this->{$en}[] = array( $object, $method );
	}
	protected function EventFire( $event, Array $args ) {
		$en = '__event_' . $event;
		if( ! is_array( $this->$en ) )
			throw new Exception('El evento ' . $event . ' no está definido en el objeto.');
		foreach( $this->{$en} as $w )
			$w[0]->{$w[1]}( $args );
		return;
	}
}

Y bueno, con ésto sería más que suficiente.

¿Cómo se usa esta gestión de eventos?

Pues dentro de lo que ofrece PHP, es bastante similar a lenguajes como C# o VB.NET. Primero la clase debe definir explícitamente el tipo concreto de evento que se desea gestionar, para ello lo mejor es un miembro protegido (no puede ser privado, pues la clase base mspwObject debe poder acceder a él).

Supongamos a modo de ejemplo que tenemos 3 máquinas: A, B y C. Inicialmente cada máquina posee un número determinado de recursos, el recurso usado por una máquina no puede ser usado por las otras, sin embargo, cuando una máquina consume un número prefijado de sus recursos, entonces genera unos pocos recursos para las otras dos máquinas. Este proceso se repite hasta que ninguna máquina tiene suficientes recursos para generar recursos para las otras dos máquinas.

El programa completo sería:

abstract class mspwObject {
	public function EventHandler( $event, &$object, $method ) {
		$en = '__event_' . $event;
		if( ! is_array( $this->$en ) )
			throw new Exception('El evento ' . $event . ' no está definido en el objeto.');
		$this->{$en}[] = array( $object, $method );
	}
	protected function EventFire( $event, Array $args ) {
		$en = '__event_' . $event;
		if( ! is_array( $this->$en ) )
			throw new Exception('El evento ' . $event . ' no está definido en el objeto.');
		foreach( $this->{$en} as $w )
			$w[0]->{$w[1]}( $args );
		return;
	}
}

class mspwMaquina extends mspwObject {

	protected $__event_entrega = array(); // evento que genera recursos para las otras máquinas.

	private $n;
	private $rn;
	private $r;
	private $rg;

	public function __construct(
		$nmaquina,
		$recursos_necesarios_para_generar,
		Array $recursos_generados
	) {
		echo "Maquina $nmaquina iniciada.<br/>";
		$this->n = $nmaquina;
		$this->rn = $recursos_necesarios_para_generar;
		$this->rg = $recursos_generados;
		$this->r = 0; // disponemos de 0 recursos.
	}

	public function CargarRecursos( Array $k ) { // el vector contiene los recursos entregados a cada máquina
		echo "La maquina $this->n recibe " . $k[$this->n] . " recursos.<br/>";
		if( ( $this->r += $k[$this->n] ) >= $this->rn ) {
			$generados = $this->r / $this->rn;
			$this->r %= $this->rn;
			$v = array();
			$i = 0;
			foreach( $this->rg as $x ) {
				$w = $generados * $x;
				$v[] = $w;
				echo "&nbsp;&nbsp;&nbsp;La maquina $this->n genera " . $w . " recursos para la maquina " . $i++ . ".<br/>";
			}
			echo "&nbsp;&nbsp;&nbsp;Le quedan " . $this->r . " recursos.<br/>";
			$this->EventFire( 'entrega', $v );
		}

	}

	public function __destruct() {
	}

}

$maquinas = array();
$maquinas[] = new mspwMaquina( 0, 6, array( 0, 2, 3 ) );
$maquinas[] = new mspwMaquina( 1, 9, array( 3, 0, 4 ) );
$maquinas[] = new mspwMaquina( 2, 4, array( 1, 2, 0 ) );

// asociamos eventos a todas las máquinas
for( $j = 0; $j < 3; $j++ ) {
	$maquinas[$j]->EventHandler( 'entrega', $maquinas[($j+1)%3], 'CargarRecursos' );
	$maquinas[$j]->EventHandler( 'entrega', $maquinas[($j+2)%3], 'CargarRecursos' );
}

// damos recursos iniciales a una única máquina
$maquinas[0]->CargarRecursos( array( 18, 0, 0 ) );

Y la salida generada por el programa sería.

Maquina 0 iniciada.
Maquina 1 iniciada.
Maquina 2 iniciada.
La maquina 0 recibe 18 recursos y ahora tiene 18.
   La maquina 0 genera 0 recursos para la maquina 0.
   La maquina 0 genera 6 recursos para la maquina 1.
   La maquina 0 genera 9 recursos para la maquina 2.
   Le quedan 0 recursos.
La maquina 1 recibe 6 recursos y ahora tiene 6.
La maquina 2 recibe 9 recursos y ahora tiene 9.
   La maquina 2 genera 2.25 recursos para la maquina 0.
   La maquina 2 genera 4.5 recursos para la maquina 1.
   La maquina 2 genera 0 recursos para la maquina 2.
   Le quedan 0 recursos.
La maquina 0 recibe 2.25 recursos y ahora tiene 2.25.
La maquina 1 recibe 4.5 recursos y ahora tiene 10.5.
   La maquina 1 genera 3.5 recursos para la maquina 0.
   La maquina 1 genera 0 recursos para la maquina 1.
   La maquina 1 genera 4.6666666666667 recursos para la maquina 2.
   Le quedan 0 recursos.
La maquina 2 recibe 4.6666666666667 recursos y ahora tiene 4.6666666666667.
   La maquina 2 genera 1.1666666666667 recursos para la maquina 0.
   La maquina 2 genera 2.3333333333333 recursos para la maquina 1.
   La maquina 2 genera 0 recursos para la maquina 2.
   Le quedan 0 recursos.
La maquina 0 recibe 1.1666666666667 recursos y ahora tiene 3.4166666666667.
La maquina 1 recibe 2.3333333333333 recursos y ahora tiene 2.3333333333333.
La maquina 0 recibe 3.5 recursos y ahora tiene 6.9166666666667.
   La maquina 0 genera 0 recursos para la maquina 0.
   La maquina 0 genera 2.3055555555556 recursos para la maquina 1.
   La maquina 0 genera 3.4583333333333 recursos para la maquina 2.
   Le quedan -8.8817841970013E-16 recursos.
La maquina 1 recibe 2.3055555555556 recursos y ahora tiene 4.6388888888889.
La maquina 2 recibe 3.4583333333333 recursos y ahora tiene 3.4583333333333.

Opinado el 22/12/09 16:53, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    muy interesante, pero falta explicacion me quede en las mismas, porfavor si pueden apliar informacion de php orientado a evetos. gracias
Opinado el 21/04/10 01:57, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    
Opinado el 16/09/10 05:26, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    Interesante. El codigo for( $j = 0; $j < 3; $j++ ) deberia reescribirse mejor porque estas usando un arreglo[] no definido, asi que abria que usar un count para saber cuantos elementos hay en el arreglo $maquinas e iterarlo
Opinado el 16/09/10 10:36, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    No, por supuesto que está definido y tiene 3 elementos, cuyos índices van desde el 0 hasta el 2.
Opinado el 25/11/10 17:09, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    deberias de explicar un poco mas el fucionamiento y hacer un ejemplo mas practico y sencillo
Opinado el 05/01/11 18:34, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    muy interesante
Opinado el 22/01/11 02:54, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    Excelente! Justo buscaba algo parecido. Aunque el ejemplo es un poco espagueti :)
Opinado el 07/03/11 19:30, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    
Opinado el 27/12/11 17:12, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    uffa
Opinado el 16/01/12 22:55, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    Se entiende el ejemplo, aunque su aplicación practica en un ejemplo de programación se presenta algo complejo. Ya que si bien manejas eventos estos ocurren en el servidor al finalizar el script php de modo que no es un evento real ya que no persiste en el cliente (salvo que se haga algo) Eso es el ejemplo de aplicacion que estoy buscando.
Opinado el 14/11/12 17:32, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    deven de poner los tipos de eventos y sus funcione
Opinado el 05/09/14 17:19, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    no se, solo quería aparecer aqui :)
Opinado el 15/03/16 17:52, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    si
Opinado el 07/10/16 11:05, valoración ValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoraciónValoración
    
¿Te ha gustado? ¡aporta tu opinión!
Valoración:
 0    1    2    3    4    5    6    7    8    9    10

Comentario:
NOTA: si es una petición... ¡pon el e-mail al que responderte o no sabré a dónde escribir!

Código de verificación captcha