Completed
Push — master ( 724970...8458d4 )
by Andreas
12:51 queued 08:20
created

Doku_Event_Handler::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 0
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * DokuWiki Events
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Christopher Smith <[email protected]>
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) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
35
36
        $this->name = $name;
37
        $this->data =& $data;
38
39
    }
40
41
    /**
42
     * @return string
43
     */
44
    function __toString() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
175
176
        // load action plugins
177
        /** @var DokuWiki_Action_Plugin $plugin */
178
        $plugin = null;
0 ignored issues
show
Unused Code introduced by
$plugin is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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='') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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
}
276