1 <?php
2 
3 // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
4 
5 namespace dokuwiki\Extension;
6 
7 use dokuwiki\Logger;
8 
9 /**
10  * The Action plugin event
11  */
12 class Event
13 {
14     /** @var string READONLY  event name, objects must register against this name to see the event */
15     public $name = '';
16     /** @var mixed|null READWRITE data relevant to the event, no standardised format, refer to event docs */
17     public $data;
18     /**
19      * @var mixed|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      */
23     public $result;
24     /** @var bool READONLY  if true, event handlers can prevent the events default action */
25     public $canPreventDefault = true;
26 
27     /** @var bool whether or not to carry out the default action associated with the event */
28     protected $runDefault = true;
29     /** @var bool whether or not to continue propagating the event to other handlers */
30     protected $mayContinue = true;
31 
32     /**
33      * event constructor
34      *
35      * @param string $name
36      * @param mixed $data
37      */
38     public function __construct($name, &$data)
39     {
40 
41         $this->name = $name;
42         $this->data =& $data;
43     }
44 
45     /**
46      * @return string
47      */
48     public function __toString()
49     {
50         return $this->name;
51     }
52 
53     /**
54      * advise all registered BEFORE handlers of this event
55      *
56      * if these methods are used by functions outside of this object, they must
57      * properly handle correct processing of any default action and issue an
58      * advise_after() signal. e.g.
59      *    $evt = new dokuwiki\Plugin\Doku_Event(name, data);
60      *    if ($evt->advise_before(canPreventDefault) {
61      *      // default action code block
62      *    }
63      *    $evt->advise_after();
64      *    unset($evt);
65      *
66      * @param bool $enablePreventDefault
67      * @return bool results of processing the event, usually $this->runDefault
68      */
69     public function advise_before($enablePreventDefault = true)
70     {
71         global $EVENT_HANDLER;
72 
73         $this->canPreventDefault = $enablePreventDefault;
74         if ($EVENT_HANDLER !== null) {
75             $EVENT_HANDLER->process_event($this, 'BEFORE');
76         } else {
77             Logger::getInstance(Logger::LOG_DEBUG)
78                   ->log($this->name . ':BEFORE event triggered before event system was initialized');
79         }
80 
81         return (!$enablePreventDefault || $this->runDefault);
82     }
83 
84     /**
85      * advise all registered AFTER handlers of this event
86      *
87      * @param bool $enablePreventDefault
88      * @see advise_before() for details
89      */
90     public function advise_after()
91     {
92         global $EVENT_HANDLER;
93 
94         $this->mayContinue = true;
95 
96         if ($EVENT_HANDLER !== null) {
97             $EVENT_HANDLER->process_event($this, 'AFTER');
98         } else {
99             Logger::getInstance(Logger::LOG_DEBUG)->
100                 log($this->name . ':AFTER event triggered before event system was initialized');
101         }
102     }
103 
104     /**
105      * trigger
106      *
107      * - advise all registered (<event>_BEFORE) handlers that this event is about to take place
108      * - carry out the default action using $this->data based on $enablePrevent and
109      *   $this->_default, all of which may have been modified by the event handlers.
110      * - advise all registered (<event>_AFTER) handlers that the event has taken place
111      *
112      * @param null|callable $action
113      * @param bool $enablePrevent
114      * @return  mixed $event->results
115      *          the value set by any <event>_before or <event> handlers if the default action is prevented
116      *          or the results of the default action (as modified by <event>_after handlers)
117      *          or NULL no action took place and no handler modified the value
118      */
119     public function trigger($action = null, $enablePrevent = true)
120     {
121 
122         if (!is_callable($action)) {
123             $enablePrevent = false;
124             if ($action !== null) {
125                 trigger_error(
126                     'The default action of ' . $this .
127                     ' is not null but also not callable. Maybe the method is not public?',
128                     E_USER_WARNING
129                 );
130             }
131         }
132 
133         if ($this->advise_before($enablePrevent) && is_callable($action)) {
134             $this->result = call_user_func_array($action, [&$this->data]);
135         }
136 
137         $this->advise_after();
138 
139         return $this->result;
140     }
141 
142     /**
143      * stopPropagation
144      *
145      * stop any further processing of the event by event handlers
146      * this function does not prevent the default action taking place
147      */
148     public function stopPropagation()
149     {
150         $this->mayContinue = false;
151     }
152 
153     /**
154      * may the event propagate to the next handler?
155      *
156      * @return bool
157      */
158     public function mayPropagate()
159     {
160         return $this->mayContinue;
161     }
162 
163     /**
164      * preventDefault
165      *
166      * prevent the default action taking place
167      */
168     public function preventDefault()
169     {
170         $this->runDefault = false;
171     }
172 
173     /**
174      * should the default action be executed?
175      *
176      * @return bool
177      */
178     public function mayRunDefault()
179     {
180         return $this->runDefault;
181     }
182 
183     /**
184      * Convenience method to trigger an event
185      *
186      * Creates, triggers and destroys an event in one go
187      *
188      * @param string $name name for the event
189      * @param mixed $data event data
190      * @param callable $action (optional, default=NULL) default action, a php callback function
191      * @param bool $canPreventDefault (optional, default=true) can hooks prevent the default action
192      *
193      * @return mixed                        the event results value after all event processing is complete
194      *                                      by default this is the return value of the default action however
195      *                                      it can be set or modified by event handler hooks
196      */
197     public static function createAndTrigger($name, &$data, $action = null, $canPreventDefault = true)
198     {
199         $evt = new Event($name, $data);
200         return $evt->trigger($action, $canPreventDefault);
201     }
202 }
203