Pour les besoins de Nano, notre framework PHP, JP et moi avons réalisé un système de d’abonnement et de gestion d’évènements à des objets.
Nous avons créé deux classes, Observable et Observer. La première sera héritée par les objets que nous souhaitons rendre observables et la seconde contient la définition de l’objet qui appellera le les méthodes déclenchées par les évènements.
Voici le code de ces deux classes, nous verrons après comment l’utilise.
Observer
final class Observer { protected static $_instance; protected $_observed_events=array(); public static function get_instance() { if(is_null(self::$_instance)) { $c=__CLASS__; self::$_instance = new $c; } return self::$_instance; } public function observe($observed_class, $event, $called_class, $called_method, $arguments=array()) { $this->_observed_events[]=array('observed_class' => $observed_class, 'event' => $event, 'called_class' => $called_class, 'called_method' => $called_method,'arguments' => $arguments); } public function call_back($observed_class, $event) { $return_value=true; foreach ($this->_observed_events as $key => $value) { if($value['observed_class']==$observed_class && $value['event']==$event) { $return_value&=call_user_func_array(array($value['called_class'], $value['called_method']), $value['arguments']); } } return $return_value; } }
Observable
interface iObservable { public function __call($method_name, $arguments); public static function __callStatic($method_name, $arguments); } abstract class Observable implements iObservable { public function __call($method_name, $arguments) { $ret = false; if(Observer::get_instance()->call_back(__CLASS__, 'before_'.$method_name)) { $ret = call_user_func_array(array($this, '_'.$method_name), $arguments); } Observer::get_instance()->call_back(__CLASS__, 'on_'.$method_name, $ret); return $ret; } public static function __callStatic($method_name, $arguments) { $ret = false; if(Observer::get_instance()->call_back(__CLASS__, 'before_'.$method_name)) { $ret = call_user_func_array(array(__CLASS__, '_'.$method_name), $arguments); } Observer::get_instance()->call_back(__CLASS__, 'on_'.$method_name, $ret); return $ret; } public function add_observer($event, $called_class, $called_method, $arguments=array()) { Observer::get_instance()->observe(__CLASS__, $event, $called_class, $called_method, $arguments=array()); } }
Utilisation
Encore un peu de code pour illustrer tout ça, on commence par planter le décor avec nos deux classes.
class ClassA extends Observable { public function _my_function_a() { return("Je suis dans ClassA"); } } abstract class CalledClass { public static $value; public static function callback_function_a() { self::$value = "je suis dans CalledClass"; return true; } }
Puis on place notre observer on on vérifie que tout c’est bien passé
ClassA::add_observer('on_my_function_a', 'CalledClass', 'callback_function_a'); $this->a = new ClassA(); echo $this->a->my_function_a(); echo CalledClass::$value;
Je vous laisse découvrir les petites subtilités du code que l’on n’a pas abordées ici, notamment dans les fonction magique.
Un indice : le callback est automatiquement appelé (s’il existe bien sûr) pour les méthodes qui commencent par un underscore, sinon vous pouvez placer des Observer::get_instance()->call_back(__CLASS__, $event); dans votre code aux endroits stratégiques de votre méthode observée.
Développement Web Libre 
2 commentaires ↓
Implémenter les classes (enfin interfaces en l’occurrence) natives : splsubject et SplObserver aurait été un +
http://fr.php.net/manual/en/class.splsubject.php
Merci pour l’info, je connaissais pas cette interface. La SPL est assez riche il faudrait la consulter régulièrement pour faire les bons choix.
Laisser un commentaire