notify($event, $data) method */ abstract class Observer { protected $params = array(); public function __construct($params = null) { if ($params) $this->params = $params; } abstract public function update($observable, $event, &$data); } /** * Abstract observable superclass (observers attach to this) * * 1) Each ObservableModel subclass maintains a list of Observers listening for specific events. * * 2) When an instance of ObservableModel calls it's ->notify($event) method, all Observers * registered to listen for this particular event receive a callback to their ->update() method. * */ class ObservableModel extends Model { /** * An array of Observer objects to notify * * @access private * @var array */ var $_observers = array(); /** * Constructor */ function __construct($id = null, $fields = null, $db = null, $row = null, $cacheable = false) { $this->_observers = array(); // Load observers from observers.conf (e.g. ActivityLogger, Notifier, Messenger) $config = Config::Get('observers/eventmap'); /* Eventmap format * --------------- * * : (use instanceof to check so we can include superclasses here) * - event: * observers: * - class: * params: {} * * * Sample mapping * -------------- * Project: * - event: rate * observers: * - class: ActivityLogger * * - event: comment * observers: * - class: Notifier * params: {subscription: itemcomment} * * - class: ActivityLogger * * # * # This sample config will: * # - Trigger a post on the Project owner's "activity log" (wall) whenever * # someone rates or comments on their project. * # * # - Send a message to the Project owner's mailbox with the comment * # provided they're opted into this type of alert. * # */ // autoload observers $conf = Config::Get('observers'); if ($conf->auto) { foreach ($conf->auto->items as $obs) uses("observer.$obs"); foreach ($config->items as $observable_class => $event_observers) { if ($this instanceof $observable_class) { foreach ($event_observers->items as $event) { foreach ($event->observers->items as $item) { $params = $item->params; $parameters = ($params) ? $params->items : null; $instance = new $item->class($parameters); $this->attach($instance, $event->event); } } } } } parent::__construct($id, $fields, $db, $row, $cacheable); } /** * Update each attached observer object and return an array of their return values * * @access public * @return array Array of return values from the observers */ function notify($event, $data = null) { $return = array(); $data = ($data) ? $data : array(); // Iterate through the _observers array if (isset($this->_observers[$event])) { foreach ($this->_observers[$event] as $observer) { $return[] = $observer->update($this, $event, $data); } } return $return; } /** * Attach an observer object * * @access public * @param object $observer An observer object to attach * @return void */ function attach(&$observer, $event) { trace(get_class($this), 'Attached observer ' . get_class($observer) . ' for event ' . $event); // Make sure we haven't already attached this object as an observer if (is_object($observer)) { $class = get_class($observer); if (isset($this->_observers[$event])) foreach ($this->_observers[$event] as $check) if (is_a($check, $class)) return; if (!isset($this->_observers[$event])) $this->_observers[$event] = array(); $this->_observers[$event][] = $observer; } } /** * Detach an observer object * * @access public * @param object $observer An observer object to detach * @return boolean True if the observer object was detached */ function detach($observer, $event) { // Initialize variables $retval = false; if (isset($this->_observers[$event])) { $key = array_search($observer, $this->_observers[$event]); if ($key !== false) { unset($this->_observers[$event][$key]); $retval = true; } } return $retval; } /* * The following methods are all events raised during the persistance lifecycle of a Model instance. * * They are being overridden here in ObservableModel to expose these lifecycle events to Observers * that want to listen for them. */ protected function pre_create(&$fields) { parent::pre_create($fields); $this->notify(__FUNCTION__); } protected function post_create() { parent::post_create(); $this->notify(__FUNCTION__); } protected function pre_read() { parent::pre_read(); $this->notify(__FUNCTION__); } protected function post_read() { parent::post_read(); $this->notify(__FUNCTION__); } protected function pre_update(&$fields) { parent::pre_update($fields); $this->notify(__FUNCTION__); } protected function post_update($fields) { parent::post_update($fields); $this->notify(__FUNCTION__); } protected function pre_delete() { parent::pre_delete(); $this->notify(__FUNCTION__); } protected function post_delete() { parent::post_delete(); $this->notify(__FUNCTION__); } }