[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> events.php (source)

   1  <?php
   2  /**
   3   * DokuWiki Events
   4   *
   5   * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
   6   * @author     Christopher Smith <chris@jalakai.co.uk>
   7   */
   8  
   9  if(!defined('DOKU_INC')) die('meh.');
  10  
  11  /**
  12   * The event
  13   */
  14  class Doku_Event {
  15  
  16      // public properties
  17      public $name = '';                // READONLY  event name, objects must register against this name to see the event
  18      public $data = null;              // READWRITE data relevant to the event, no standardised format (YET!)
  19      public $result = null;            // READWRITE the results of the event action, only relevant in "_AFTER" advise
  20      //    event handlers may modify this if they are preventing the default action
  21      //    to provide the after event handlers with event results
  22      public $canPreventDefault = true; // READONLY  if true, event handlers can prevent the events default action
  23  
  24      // private properties, event handlers can effect these through the provided methods
  25      protected $_default = true;     // whether or not to carry out the default action associated with the event
  26      protected $_continue = true;    // whether or not to continue propagating the event to other handlers
  27  
  28      /**
  29       * event constructor
  30       *
  31       * @param string $name
  32       * @param mixed $data
  33       */
  34      function __construct($name, &$data) {
  35  
  36          $this->name = $name;
  37          $this->data =& $data;
  38  
  39      }
  40  
  41      /**
  42       * @return string
  43       */
  44      function __toString() {
  45          return $this->name;
  46      }
  47  
  48      /**
  49       * advise functions
  50       *
  51       * advise all registered handlers of this event
  52       *
  53       * if these methods are used by functions outside of this object, they must
  54       * properly handle correct processing of any default action and issue an
  55       * advise_after() signal. e.g.
  56       *    $evt = new Doku_Event(name, data);
  57       *    if ($evt->advise_before(canPreventDefault) {
  58       *      // default action code block
  59       *    }
  60       *    $evt->advise_after();
  61       *    unset($evt);
  62       *
  63       * @param bool $enablePreventDefault
  64       * @return bool results of processing the event, usually $this->_default
  65       */
  66      function advise_before($enablePreventDefault=true) {
  67          global $EVENT_HANDLER;
  68  
  69          $this->canPreventDefault = $enablePreventDefault;
  70          $EVENT_HANDLER->process_event($this,'BEFORE');
  71  
  72          return (!$enablePreventDefault || $this->_default);
  73      }
  74  
  75      function advise_after() {
  76          global $EVENT_HANDLER;
  77  
  78          $this->_continue = true;
  79          $EVENT_HANDLER->process_event($this,'AFTER');
  80      }
  81  
  82      /**
  83       * trigger
  84       *
  85       * - advise all registered (<event>_BEFORE) handlers that this event is about to take place
  86       * - carry out the default action using $this->data based on $enablePrevent and
  87       *   $this->_default, all of which may have been modified by the event handlers.
  88       * - advise all registered (<event>_AFTER) handlers that the event has taken place
  89       *
  90       * @param null|callable $action
  91       * @param bool $enablePrevent
  92       * @return  mixed $event->results
  93       *          the value set by any <event>_before or <event> handlers if the default action is prevented
  94       *          or the results of the default action (as modified by <event>_after handlers)
  95       *          or NULL no action took place and no handler modified the value
  96       */
  97      function trigger($action=null, $enablePrevent=true) {
  98  
  99          if (!is_callable($action)) {
 100              $enablePrevent = false;
 101              if (!is_null($action)) {
 102                  trigger_error('The default action of '.$this.' is not null but also not callable. Maybe the method is not public?', E_USER_WARNING);
 103              }
 104          }
 105  
 106          if ($this->advise_before($enablePrevent) && is_callable($action)) {
 107              if (is_array($action)) {
 108                  list($obj,$method) = $action;
 109                  $this->result = $obj->$method($this->data);
 110              } else {
 111                  $this->result = $action($this->data);
 112              }
 113          }
 114  
 115          $this->advise_after();
 116  
 117          return $this->result;
 118      }
 119  
 120      /**
 121       * stopPropagation
 122       *
 123       * stop any further processing of the event by event handlers
 124       * this function does not prevent the default action taking place
 125       */
 126      public function stopPropagation() {
 127          $this->_continue = false;
 128      }
 129  
 130      /**
 131       * may the event propagate to the next handler?
 132       *
 133       * @return bool
 134       */
 135      public function mayPropagate() {
 136          return $this->_continue;
 137      }
 138  
 139      /**
 140       * preventDefault
 141       *
 142       * prevent the default action taking place
 143       */
 144      public function preventDefault() {
 145          $this->_default = false;
 146      }
 147  
 148      /**
 149       * should the default action be executed?
 150       *
 151       * @return bool
 152       */
 153      public function mayRunDefault() {
 154          return $this->_default;
 155      }
 156  }
 157  
 158  /**
 159   * Controls the registration and execution of all events,
 160   */
 161  class Doku_Event_Handler {
 162  
 163      // public properties:  none
 164  
 165      // private properties
 166      protected $_hooks = array();          // array of events and their registered handlers
 167  
 168      /**
 169       * event_handler
 170       *
 171       * constructor, loads all action plugins and calls their register() method giving them
 172       * an opportunity to register any hooks they require
 173       */
 174      function __construct() {
 175  
 176          // load action plugins
 177          /** @var DokuWiki_Action_Plugin $plugin */
 178          $plugin = null;
 179          $pluginlist = plugin_list('action');
 180  
 181          foreach ($pluginlist as $plugin_name) {
 182              $plugin = plugin_load('action',$plugin_name);
 183  
 184              if ($plugin !== null) $plugin->register($this);
 185          }
 186      }
 187  
 188      /**
 189       * register_hook
 190       *
 191       * register a hook for an event
 192       *
 193       * @param  string   $event   string   name used by the event, (incl '_before' or '_after' for triggers)
 194       * @param  string   $advise
 195       * @param  object   $obj     object in whose scope method is to be executed,
 196       *                             if NULL, method is assumed to be a globally available function
 197       * @param  string   $method  event handler function
 198       * @param  mixed    $param   data passed to the event handler
 199       * @param  int      $seq     sequence number for ordering hook execution (ascending)
 200       */
 201      function register_hook($event, $advise, $obj, $method, $param=null, $seq=0) {
 202          $seq = (int)$seq;
 203          $doSort = !isset($this->_hooks[$event.'_'.$advise][$seq]);
 204          $this->_hooks[$event.'_'.$advise][$seq][] = array($obj, $method, $param);
 205  
 206          if ($doSort) {
 207              ksort($this->_hooks[$event.'_'.$advise]);
 208          }
 209      }
 210  
 211      /**
 212       * process the before/after event
 213       *
 214       * @param Doku_Event $event
 215       * @param string     $advise BEFORE or AFTER
 216       */
 217      function process_event($event,$advise='') {
 218  
 219          $evt_name = $event->name . ($advise ? '_'.$advise : '_BEFORE');
 220  
 221          if (!empty($this->_hooks[$evt_name])) {
 222              foreach ($this->_hooks[$evt_name] as $sequenced_hooks) {
 223                  foreach ($sequenced_hooks as $hook) {
 224                      list($obj, $method, $param) = $hook;
 225  
 226                      if (is_null($obj)) {
 227                          $method($event, $param);
 228                      } else {
 229                          $obj->$method($event, $param);
 230                      }
 231  
 232                      if (!$event->mayPropagate()) return;
 233                  }
 234              }
 235          }
 236      }
 237  
 238      /**
 239       * Check if an event has any registered handlers
 240       *
 241       * When $advise is empty, both BEFORE and AFTER events will be considered,
 242       * otherwise only the given advisory is checked
 243       *
 244       * @param string $name Name of the event
 245       * @param string $advise BEFORE, AFTER or empty
 246       * @return bool
 247       */
 248      public function hasHandlerForEvent($name, $advise = '') {
 249          if($advise) {
 250              return isset($this->_hooks[$name . '_' . $advise]);
 251          } else {
 252              return isset($this->_hooks[$name . '_BEFORE']) || isset($this->_hooks[$name . '_AFTER']);
 253          }
 254      }
 255  }
 256  
 257  /**
 258   * trigger_event
 259   *
 260   * function wrapper to process (create, trigger and destroy) an event
 261   *
 262   * @param  string   $name               name for the event
 263   * @param  mixed    $data               event data
 264   * @param  callback $action             (optional, default=NULL) default action, a php callback function
 265   * @param  bool     $canPreventDefault  (optional, default=true) can hooks prevent the default action
 266   *
 267   * @return mixed                        the event results value after all event processing is complete
 268   *                                      by default this is the return value of the default action however
 269   *                                      it can be set or modified by event handler hooks
 270   */
 271  function trigger_event($name, &$data, $action=null, $canPreventDefault=true) {
 272  
 273      $evt = new Doku_Event($name, $data);
 274      return $evt->trigger($action, $canPreventDefault);
 275  }