This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @author Todd Burry <[email protected]> |
||
4 | * @copyright 2009-2014 Vanilla Forums Inc. |
||
5 | * @license MIT |
||
6 | */ |
||
7 | |||
8 | namespace Garden; |
||
9 | |||
10 | /** |
||
11 | * Contains methods for binding and firing to events. |
||
12 | * |
||
13 | * Addons can create callbacks that bind to events which are calle througout the code to allow |
||
14 | * extension of the application and framework. |
||
15 | */ |
||
16 | class Event { |
||
17 | /// Constants /// |
||
18 | |||
19 | const PRIORITY_LOW = 1000; |
||
20 | const PRIORITY_NORMAL = 100; |
||
21 | const PRIORITY_HIGH = 10; |
||
22 | |||
23 | /// Properties /// |
||
24 | |||
25 | /** |
||
26 | * All of the event handlers that have been registered. |
||
27 | * @var array An array of event handlers. |
||
28 | */ |
||
29 | protected static $handlers = array(); |
||
30 | |||
31 | /** |
||
32 | * All of the event handlers that still need to be sorted by priority. |
||
33 | * @var array An array of event handler names that need to be sorted. |
||
34 | */ |
||
35 | protected static $toSort = array(); |
||
36 | |||
37 | /// Methods /// |
||
38 | |||
39 | /** |
||
40 | * Call a callback with an array of parameters, checking for events to be called with the callback. |
||
41 | * |
||
42 | * This method is similar to {@link call_user_func_array()} but it will fire events that can happen |
||
43 | * before and/or after the callback and can call an event instead of the callback itself to override it. |
||
44 | * |
||
45 | * In order to use events with this method they must be bound with a particular naming convention. |
||
46 | * |
||
47 | * **Modify a function** |
||
48 | * |
||
49 | * - functionname_before |
||
50 | * - functionname |
||
51 | * - functionname_after |
||
52 | * |
||
53 | * **Modify a method call** |
||
54 | * |
||
55 | * - classname_methodname_before |
||
56 | * - classname_methodname |
||
57 | * - classname_methodname_after |
||
58 | * |
||
59 | * @param callable $callback The {@link callable} to be called. |
||
60 | * @param array $args The arguments to be passed to the callback, as an indexed array. |
||
61 | * @return mixed Returns the return value of the callback. |
||
62 | */ |
||
63 | 18 | public static function callUserFuncArray($callback, $args = []) { |
|
64 | // Figure out the event name from the callback. |
||
65 | 18 | $event_name = static::getEventname($callback); |
|
66 | 18 | if (!$event_name) { |
|
67 | 1 | return call_user_func_array($callback, $args); |
|
68 | } |
||
69 | |||
70 | // The events could have different args because the event handler can take the object as the first parameter. |
||
71 | 17 | $event_args = $args; |
|
72 | // If the callback is an object then it gets passed as the first argument. |
||
73 | 17 | if (is_array($callback) && is_object($callback[0])) { |
|
74 | 16 | array_unshift($event_args, $callback[0]); |
|
75 | 16 | } |
|
76 | |||
77 | // Fire before events. |
||
78 | 17 | self::fireArray($event_name.'_before', $event_args); |
|
79 | |||
80 | // Call the function. |
||
81 | 17 | if (static::hasHandler($event_name)) { |
|
82 | // The callback was overridden so fire it. |
||
83 | 1 | $result = static::fireArray($event_name, $event_args); |
|
84 | 1 | } else { |
|
85 | // The callback was not overridden so just call the passed callback. |
||
86 | 16 | $result = call_user_func_array($callback, $args); |
|
87 | } |
||
88 | |||
89 | // Fire after events. |
||
90 | 17 | self::fireArray($event_name.'_after', $event_args); |
|
91 | |||
92 | 17 | return $result; |
|
93 | } |
||
94 | |||
95 | /** |
||
96 | * Bind an event handler to an event. |
||
97 | * |
||
98 | * @param string $event The naame of the event to bind to. |
||
99 | * @param callback $callback The callback of the event. |
||
100 | * @param int $priority The priority of the event. |
||
101 | */ |
||
102 | 41 | public static function bind($event, $callback, $priority = Event::PRIORITY_NORMAL) { |
|
103 | 41 | $event = strtolower($event); |
|
104 | 41 | self::$handlers[$event][$priority][] = $callback; |
|
105 | 41 | self::$toSort[$event] = true; |
|
106 | 41 | } |
|
107 | |||
108 | /** |
||
109 | * Bind a class' declared event handlers. |
||
110 | * |
||
111 | * Plugin classes declare event handlers in the following way: |
||
112 | * |
||
113 | * ``` |
||
114 | * // Bind to a normal event. |
||
115 | * public function eventname_handler($arg1, $arg2, ...) { ... } |
||
116 | * |
||
117 | * // Add/override a method called with Event::callUserFuncArray(). |
||
118 | * public function ClassName_methodName($sender, $arg1, $arg2) { ... } |
||
119 | * public function ClassName_methodName_create($sender, $arg1, $arg2) { ... } // deprecated |
||
120 | * |
||
121 | * // Call the handler before or after a method called with Event::callUserFuncArray(). |
||
122 | * public function ClassName_methodName_before($sender, $arg1, $arg2) { ... } |
||
123 | * public function ClassName_methodName_after($sender, $arg1, $arg2) { ... } |
||
124 | * ``` |
||
125 | * |
||
126 | * @param mixed $class The class name or an object instance. |
||
127 | * @param int $priority The priority of the event. |
||
128 | * @throws \InvalidArgumentException Throws an exception when binding to a class name with no `instance()` method. |
||
129 | */ |
||
130 | 2 | public static function bindClass($class, $priority = Event::PRIORITY_NORMAL) { |
|
131 | 2 | $method_names = get_class_methods($class); |
|
132 | |||
133 | // Grab an instance of the class so there is something to bind to. |
||
134 | 2 | if (is_string($class)) { |
|
135 | 1 | if (method_exists($class, 'instance')) { |
|
136 | // TODO: Make the instance lazy load. |
||
137 | 1 | $instance = call_user_func(array($class, 'instance')); |
|
138 | 1 | } else { |
|
139 | throw new \InvalidArgumentException('Event::bindClass(): The class for argument #1 must have an instance() method or be passed as an object.', 422); |
||
140 | } |
||
141 | 1 | } else { |
|
142 | 1 | $instance = $class; |
|
143 | } |
||
144 | |||
145 | 2 | foreach ($method_names as $method_name) { |
|
146 | 2 | if (strpos($method_name, '_') === false) { |
|
147 | 2 | continue; |
|
148 | } |
||
149 | |||
150 | 2 | $parts = explode('_', strtolower($method_name)); |
|
151 | 2 | switch (end($parts)) { |
|
152 | 2 | case 'handler': |
|
153 | 2 | case 'create': |
|
154 | 2 | case 'override': |
|
155 | 2 | array_pop($parts); |
|
156 | 2 | $event_name = implode('_', $parts); |
|
157 | 2 | break; |
|
158 | 2 | case 'before': |
|
159 | 2 | case 'after': |
|
160 | 2 | default: |
|
161 | 2 | $event_name = implode('_', $parts); |
|
162 | 2 | break; |
|
163 | 2 | } |
|
164 | // Bind the event if we have one. |
||
165 | 2 | if ($event_name) { |
|
166 | 2 | static::bind($event_name, array($instance, $method_name), $priority); |
|
167 | 2 | } |
|
168 | 2 | } |
|
169 | 2 | } |
|
170 | |||
171 | /** |
||
172 | * Dumps all of the bound handlers. |
||
173 | * |
||
174 | * This method is meant for debugging. |
||
175 | * |
||
176 | * @return array Returns an array of all handlers indexed by event name. |
||
177 | */ |
||
178 | 1 | public static function dumpHandlers() { |
|
179 | 1 | $result = []; |
|
180 | |||
181 | 1 | foreach (self::$handlers as $event_name => $nested) { |
|
182 | 1 | $handlers = call_user_func_array('array_merge', static::getHandlers($event_name)); |
|
183 | 1 | $result[$event_name] = array_map('format_callback', $handlers); |
|
184 | 1 | } |
|
185 | |||
186 | 1 | return $result; |
|
187 | } |
||
188 | |||
189 | /** |
||
190 | * Fire an event. |
||
191 | * |
||
192 | * @param string $event The name of the event. |
||
193 | * @return mixed Returns the result of the last event handler. |
||
194 | */ |
||
195 | 38 | public static function fire($event) { |
|
196 | 38 | $handlers = self::getHandlers($event); |
|
197 | 38 | if (!$handlers) { |
|
0 ignored issues
–
show
|
|||
198 | 1 | return null; |
|
199 | } |
||
200 | |||
201 | // Grab the handlers and call them. |
||
202 | 37 | $args = array_slice(func_get_args(), 1); |
|
203 | 37 | $result = null; |
|
204 | 37 | foreach ($handlers as $callbacks) { |
|
205 | 37 | foreach ($callbacks as $callback) { |
|
206 | 37 | $result = call_user_func_array($callback, $args); |
|
207 | 37 | } |
|
208 | 37 | } |
|
209 | 37 | return $result; |
|
210 | } |
||
211 | |||
212 | /** |
||
213 | * Fire an event with an array of arguments. |
||
214 | * |
||
215 | * This method is to {@link Event::fire()} as {@link call_user_func_array()} is to {@link call_user_funct()}. |
||
216 | * The main purpose though is to allow you to have event handlers that can take references. |
||
217 | * |
||
218 | * @param string $event The name of the event. |
||
219 | * @param array $args The arguments for the event handlers. |
||
220 | * @return mixed Returns the result of the last event handler. |
||
221 | */ |
||
222 | 18 | public static function fireArray($event, $args = []) { |
|
223 | 18 | $handlers = self::getHandlers($event); |
|
224 | 18 | if (!$handlers) { |
|
0 ignored issues
–
show
The expression
$handlers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
225 | 18 | return null; |
|
226 | } |
||
227 | |||
228 | // Grab the handlers and call them. |
||
229 | 2 | $result = null; |
|
230 | 2 | foreach ($handlers as $callbacks) { |
|
231 | 2 | foreach ($callbacks as $callback) { |
|
232 | 2 | $result = call_user_func_array($callback, $args); |
|
233 | 2 | } |
|
234 | 2 | } |
|
235 | 2 | return $result; |
|
236 | } |
||
237 | |||
238 | /** |
||
239 | * Chain several event handlers together. |
||
240 | * |
||
241 | * This method will fire the first handler and pass its result as the first argument |
||
242 | * to the next event handler and so on. A chained event handler can have more than one parameter, |
||
243 | * but must have at least one parameter. |
||
244 | * |
||
245 | * @param string $event The name of the event to fire. |
||
246 | * @param mixed $value The value to pass into the filter. |
||
247 | * @return mixed The result of the chained event or `$value` if there were no handlers. |
||
248 | */ |
||
249 | 2 | public static function fireFilter($event, $value) { |
|
250 | 2 | $handlers = self::getHandlers($event); |
|
251 | 2 | if (!$handlers) { |
|
0 ignored issues
–
show
The expression
$handlers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
252 | 2 | return $value; |
|
253 | } |
||
254 | |||
255 | 1 | $args = array_slice(func_get_args(), 1); |
|
256 | 1 | foreach ($handlers as $callbacks) { |
|
257 | 1 | foreach ($callbacks as $callback) { |
|
258 | 1 | $value = call_user_func_array($callback, $args); |
|
259 | 1 | $args[0] = $value; |
|
260 | 1 | } |
|
261 | 1 | } |
|
262 | 1 | return $value; |
|
263 | } |
||
264 | |||
265 | /** |
||
266 | * Fire an event meant to override another function or method. |
||
267 | * The event handler should return true if it wants to say it is overridding the method. |
||
268 | * |
||
269 | * @param string $event The name of the event to fire. |
||
270 | * @return bool Whether or not the event has been overridden. |
||
271 | */ |
||
272 | // public static function fireOverride($event) { |
||
273 | // $handlers = self::getHandlers($event); |
||
274 | // if ($handlers === false) |
||
275 | // return $value; |
||
276 | // |
||
277 | // $args = array_slice(func_get_args(), 1); |
||
278 | // foreach ($handlers as $callbacks) { |
||
279 | // foreach ($callbacks as $callback) { |
||
280 | // $overridden = call_user_func_array($callback, $args); |
||
281 | // if ($overridden) |
||
282 | // return true; |
||
283 | // } |
||
284 | // } |
||
285 | // return false; |
||
286 | // } |
||
287 | |||
288 | /** |
||
289 | * Checks if a function exists or there is a replacement event for it. |
||
290 | * |
||
291 | * @param string $function_name The function name. |
||
292 | * @param bool $only_events Whether or not to only check events. |
||
293 | * @return boolean Returns `true` if the function given by `function_name` has been defined, `false` otherwise. |
||
294 | * @see http://ca1.php.net/manual/en/function.function-exists.php |
||
295 | */ |
||
296 | 2 | public static function functionExists($function_name, $only_events = false) { |
|
297 | 2 | if (!$only_events && function_exists($function_name)) { |
|
298 | 1 | return true; |
|
299 | } else { |
||
300 | 1 | return static::hasHandler($function_name); |
|
301 | } |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Get the event name for a callback. |
||
306 | * |
||
307 | * @param string|array|callable $callback The callback or an array in the form of a callback. |
||
308 | * @return string The name of the callback. |
||
309 | */ |
||
310 | 29 | protected static function getEventname($callback) { |
|
311 | 29 | if (is_string($callback)) { |
|
312 | 1 | return strtolower($callback); |
|
313 | 29 | } elseif (is_array($callback)) { |
|
314 | 28 | if (is_string($callback[0])) { |
|
315 | 1 | $classname = $callback[0]; |
|
316 | 1 | } else { |
|
317 | 27 | $classname = get_class($callback[0]); |
|
318 | } |
||
319 | 28 | $eventclass = trim(strrchr($classname, '\\'), '\\'); |
|
320 | 28 | if (!$eventclass) { |
|
321 | 26 | $eventclass = $classname; |
|
322 | 26 | } |
|
323 | 28 | return strtolower($eventclass.'_'.$callback[1]); |
|
324 | } |
||
325 | 1 | return ''; |
|
326 | } |
||
327 | |||
328 | /** |
||
329 | * Get all of the handlers bound to an event. |
||
330 | * |
||
331 | * @param string $name The name of the event. |
||
332 | * @return array Returns the handlers that are watching {@link $name}. |
||
333 | */ |
||
334 | 42 | public static function getHandlers($name) { |
|
335 | 42 | $name = strtolower($name); |
|
336 | |||
337 | 42 | if (!isset(self::$handlers[$name])) { |
|
338 | 19 | return []; |
|
339 | } |
||
340 | |||
341 | // See if the handlers need to be sorted. |
||
342 | 41 | if (isset(self::$toSort[$name])) { |
|
343 | 41 | ksort(self::$handlers[$name]); |
|
344 | 41 | unset(self::$toSort[$name]); |
|
345 | 41 | } |
|
346 | |||
347 | 41 | return self::$handlers[$name]; |
|
348 | } |
||
349 | |||
350 | /** |
||
351 | * Checks if an event has a handler. |
||
352 | * |
||
353 | * @param string $event The name of the event. |
||
354 | * @return bool Returns `true` if the event has at least one handler, `false` otherwise. |
||
355 | */ |
||
356 | 29 | public static function hasHandler($event) { |
|
357 | 29 | $event = strtolower($event); |
|
358 | 29 | return array_key_exists($event, self::$handlers) && !empty(self::$handlers[$event]); |
|
359 | } |
||
360 | |||
361 | /** |
||
362 | * Checks if a class method exists or there is a replacement event for it. |
||
363 | * |
||
364 | * @param mixed $object An object instance or a class name. |
||
365 | * @param string $method_name The method name. |
||
366 | * @param bool $only_events Whether or not to only check events. |
||
367 | * @return boolean Returns `true` if the method given by method_name has been defined for the given object, |
||
368 | * `false` otherwise. |
||
369 | * @see http://ca1.php.net/manual/en/function.method-exists.php |
||
370 | */ |
||
371 | 29 | public static function methodExists($object, $method_name, $only_events = false) { |
|
372 | 29 | if (!$only_events && method_exists($object, $method_name)) { |
|
373 | 27 | return true; |
|
374 | } else { |
||
375 | // Check to see if there is an event bound to the method. |
||
376 | 13 | $event_name = self::getEventname([$object, $method_name]); |
|
377 | 13 | return static::hasHandler($event_name); |
|
378 | } |
||
379 | } |
||
380 | |||
381 | /** |
||
382 | * Clear all of the event handlers. |
||
383 | * |
||
384 | * This method resets the event object to its original state. |
||
385 | */ |
||
386 | 10 | public static function reset() { |
|
387 | 10 | self::$handlers = []; |
|
388 | 10 | self::$toSort = []; |
|
389 | 10 | } |
|
390 | } |
||
391 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.