5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author Christopher Smith <chris@jalakai.co.uk>
9 if(!defined('DOKU_INC')) die('meh.');
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
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
34 function __construct($name, &$data) {
44 function __toString() {
51 * advise all registered handlers of this event
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
60 * $evt->advise_after();
63 * @param bool $enablePreventDefault
64 * @return bool results of processing the event, usually $this->_default
66 function advise_before($enablePreventDefault=true) {
67 global $EVENT_HANDLER;
69 $this->canPreventDefault
= $enablePreventDefault;
70 $EVENT_HANDLER->process_event($this,'BEFORE');
72 return (!$enablePreventDefault ||
$this->_default
);
75 function advise_after() {
76 global $EVENT_HANDLER;
78 $this->_continue
= true;
79 $EVENT_HANDLER->process_event($this,'AFTER');
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
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
97 function trigger($action=null, $enablePrevent=true) {
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
);
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
);
111 $this->result
= $action($this->data
);
115 $this->advise_after();
117 return $this->result
;
123 * stop any further processing of the event by event handlers
124 * this function does not prevent the default action taking place
126 public function stopPropagation() {
127 $this->_continue
= false;
131 * may the event propagate to the next handler?
135 public function mayPropagate() {
136 return $this->_continue
;
142 * prevent the default action taking place
144 public function preventDefault() {
145 $this->_default
= false;
149 * should the default action be executed?
153 public function mayRunDefault() {
154 return $this->_default
;
159 * Controls the registration and execution of all events,
161 class Doku_Event_Handler
{
163 // public properties: none
165 // private properties
166 protected $_hooks = array(); // array of events and their registered handlers
171 * constructor, loads all action plugins and calls their register() method giving them
172 * an opportunity to register any hooks they require
174 function __construct() {
176 // load action plugins
177 /** @var DokuWiki_Action_Plugin $plugin */
179 $pluginlist = plugin_list('action');
181 foreach ($pluginlist as $plugin_name) {
182 $plugin = plugin_load('action',$plugin_name);
184 if ($plugin !== null) $plugin->register($this);
191 * register a hook for an event
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)
201 function register_hook($event, $advise, $obj, $method, $param=null, $seq=0) {
203 $doSort = !isset($this->_hooks
[$event.'_'.$advise][$seq]);
204 $this->_hooks
[$event.'_'.$advise][$seq][] = array($obj, $method, $param);
207 ksort($this->_hooks
[$event.'_'.$advise]);
212 * process the before/after event
214 * @param Doku_Event $event
215 * @param string $advise BEFORE or AFTER
217 function process_event($event,$advise='') {
219 $evt_name = $event->name
. ($advise ?
'_'.$advise : '_BEFORE');
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;
227 $method($event, $param);
229 $obj->$method($event, $param);
232 if (!$event->mayPropagate()) return;
239 * Check if an event has any registered handlers
241 * When $advise is empty, both BEFORE and AFTER events will be considered,
242 * otherwise only the given advisory is checked
244 * @param string $name Name of the event
245 * @param string $advise BEFORE, AFTER or empty
248 public function hasHandlerForEvent($name, $advise = '') {
250 return isset($this->_hooks
[$name . '_' . $advise]);
252 return isset($this->_hooks
[$name . '_BEFORE']) ||
isset($this->_hooks
[$name . '_AFTER']);
260 * function wrapper to process (create, trigger and destroy) an event
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
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
271 function trigger_event($name, &$data, $action=null, $canPreventDefault=true) {
273 $evt = new Doku_Event($name, $data);
274 return $evt->trigger($action, $canPreventDefault);