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