<?php
// Exception lancée par notify() quand le callback n'est pas valide
class EventCallbackError extends Exception { }
// Registre des évènements écoutés : ce registre sait quel évènement est écouté par quel objet
// Il permet la distribution globale d'un évènement
// C'est ensuite l'objet lui-même qui se charge de réagir à l'évènement
// Voir principalement register(), unregister(), notifyGlobal(), notify()
class Events
{
// Les évènements, et les objets qui les écoutent
protected static
$registeredEvents = array();
// Prévenir que cet objet écoute cet évènement
public static function register(EventListener $listener, $eventName)
{
if (!isset(Events
::$registeredEvents[$eventName])) {
Events
::$registeredEvents[$eventName] = array();
}
if (!Events::isRegistered($eventName, $listener)) {
Events::$registeredEvents[$eventName][] = $listener;
return true;
}
return false;
}
// L'évènement est-il enregistré pour l'objet donné ?
public static function isRegistered($eventName, EventListener $listener)
{
foreach (Events::getListeners($eventName) as $a_listener) {
if ($listener->getUUID() == $a_listener->getUUID()) {
return true;
}
}
return false;
}
// Récupérer les objets écoutant l'évènement donné
public static function getListeners($eventName)
{
if (isset(Events
::$registeredEvents[$eventName])) {
return Events::$registeredEvents[$eventName];
} else {
}
}
// Effacer l'écoute d'un évènement
public static function unregister(EventListener $listener, $eventName)
{
unset(Events
::$registeredEvents[$eventName][$listener->getUUID()]);
return true;
}
// Notification d'un évènement de manière globale
// Retourne l'évènement (modifié, voir $event->getReturnValue() et $event->getProcessed())
public static
function notifyGlobal
($eventName, $settings = array())
{
$event = new Event($eventName, $settings);
foreach (Events::getListeners($eventName) as $listener) {
$listener->notifyEvent($event);
}
return $event;
}
// Notification d'un évènement à un objet donné
// Même retour que notifyGlobal
public static
function notify
(EventListener
$listener, $eventName, $settings = array())
{
$event = new Event($eventName, $settings);
if (Events::isRegistered($eventName, $listener)) {
$listener->notifyEvent($event);
}
return $event;
}
}
// Classe décrivant un objet pouvant écouter des évènements
// Etendre cette classe pour rendre l'objet "écoutant"
// Appeler register() et unregister() pour écouter (ou arrêter d'écouter) un évènement et associer cet évènement à un callback
// Appeler notify() pour exécuter l'évènement écouté
// Une idée d'amélioration : on pourrait étendre cette classe pour autoriser l'association de plusieurs callbacks à un évènement
class EventListener
{
// Callbacks associés aux évènements
protected
$callbacks = array();
// Universal Unique ID
protected $UUID = null;
// Retourne un identifiant unique pour l'objet
public function getUUID()
{
}
return $this->UUID;
}
// Enregistre l'évènement et lui associe un callback
public function registerEvent($eventName, $callback)
{
if (Events::register($this, $eventName)) {
$this->callbacks[$eventName] = $callback;
return true;
}
return false;
}
// Enregistre l'évènement et lui associe un callback
public function unregisterEvent($eventName, $callback)
{
Events::register($this, $eventName);
unset($this->callbacks[$eventName]);
return true;
}
// Reçoit l'évènement, et exécute le callback associé
// La valeur de retour du callback est stockée dans la "ReturnValue" de l'évènement
public function notifyEvent(Event $event)
{
if (isset($this->callbacks[$event->getName()])) {
$callback = $this->callbacks[$event->getName()];
throw
new EventCallbackError
("Cannot run " . (is_array($callback) ?
$callback[0].'::'.$callback[1
] : $callback));
} else {
$event->setProcessed($this);
$event->setReturnValue($this, $return);
}
}
}
}
// Classe décrivant un évènement
class Event
{
/* string */ protected $name = '';
/* array */ protected
$settings = array();
/* array */ public $returnValues = array();
/* array */ protected
$processors = array();
// Constructeur : nom, paramètres supplémentaires
public function __construct
($name, $settings = array())
{
$this->name = $name;
$this->settings = $settings;
}
// Nom de l'évènement
public function getName()
{
return $this->name;
}
// Récupère un paramètre
// Le nom peut être une chaine, ou une chaine de la forme "tableau.valeur1.valeur2" pour aller récupérer une valeur
// de tableaux imbriqués (dans l'exemple $settings pourrait être array('tableau' => array('valeur1' => array('valeur2' => 'coucou')))
public function get($name, $default = null)
{
$settings = $this->settings;
while (($pos = strpos($name, '.')) !== false) {
$index = substr($name, 0
, $pos);
if (isset($settings[$index])) {
$settings = $settings[$index];
$name = substr($name, $pos+1
);
} else {
return $default;
}
}
if (isset($settings[$name])) {
return $settings[$name];
}
return $default;
}
// Retourne le nombre de fois que l'évènement a été traités (nombre d'objets l'écoutant)
public function getProcessed()
{
return count($this->processors);
}
// Utilisé par l'objet écoutant pour indiquer que l'évènement a été traité
public function setProcessed(EventListener $listener)
{
$this->processors[] = $listener;
}
// Renvoie la liste des écouteurs qui ont traité l'évènement
public function getProcessors()
{
return $this->processors;
}
// Définit la veleur de retour pour un écouteur donné
public function setReturnValue(EventListener $listener, $value)
{
$this->returnValues[$listener->getUUID()] = $value;
}
// Renvoie la valeur de retour du callback pour un écouteur donné
// Si aucun écouteur n'est spécifié, on renvoie la dernière valeur de retour non null
public function getReturnValue(EventListener $listener = null)
{
$lastNotNull = null;
foreach ($this->returnValues as $value) {
$lastNotNull = $value;
}
}
return $lastNotNull;
} else {
return $this->returnValues[$listener->getUUID()];
}
}
}