1<?php
2
3// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
4
5namespace dokuwiki\Extension;
6
7use dokuwiki\Logger;
8
9/**
10 * The Action plugin event
11 */
12class 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