1<?php
2
3// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
4
5namespace dokuwiki\Extension;
6
7/**
8 * Controls the registration and execution of all events,
9 */
10class EventHandler
11{
12    // public properties:  none
13
14    // private properties
15    protected $hooks = [];          // array of events and their registered handlers
16
17    /**
18     * event_handler
19     *
20     * constructor, loads all action plugins and calls their register() method giving them
21     * an opportunity to register any hooks they require
22     */
23    public function __construct()
24    {
25
26        // load action plugins
27        /** @var ActionPlugin $plugin */
28        $plugin = null;
29        $pluginlist = plugin_list('action');
30
31        foreach ($pluginlist as $plugin_name) {
32            $plugin = plugin_load('action', $plugin_name);
33
34            if ($plugin instanceof PluginInterface) $plugin->register($this);
35        }
36    }
37
38    /**
39     * register_hook
40     *
41     * register a hook for an event
42     *
43     * @param string $event name used by the event
44     * @param string $advise BEFORE|AFTER
45     * @param object $obj scope for the method be executed on, NULL for global function or callable
46     * @param string|callable $method event handler function
47     * @param mixed $param data passed to the event handler
48     * @param int $seq sequence number for ordering hook execution (ascending)
49     */
50    public function register_hook($event, $advise, $obj, $method, $param = null, $seq = 0)
51    {
52        $seq = (int)$seq;
53        $doSort = !isset($this->hooks[$event . '_' . $advise][$seq]);
54        $this->hooks[$event . '_' . $advise][$seq][] = [$obj, $method, $param];
55
56        if ($doSort) {
57            ksort($this->hooks[$event . '_' . $advise]);
58        }
59    }
60
61    /**
62     * process the before/after event
63     *
64     * @param Event $event
65     * @param string $advise BEFORE or AFTER
66     */
67    public function process_event($event, $advise = '')
68    {
69
70        $evt_name = $event->name . ($advise ? '_' . $advise : '_BEFORE');
71
72        if (!empty($this->hooks[$evt_name])) {
73            foreach ($this->hooks[$evt_name] as $sequenced_hooks) {
74                foreach ($sequenced_hooks as $hook) {
75                    [$obj, $method, $param] = $hook;
76
77                    if ($obj === null) {
78                        $method($event, $param);
79                    } else {
80                        $obj->$method($event, $param);
81                    }
82
83                    if (!$event->mayPropagate()) return;
84                }
85            }
86        }
87    }
88
89    /**
90     * Check if an event has any registered handlers
91     *
92     * When $advise is empty, both BEFORE and AFTER events will be considered,
93     * otherwise only the given advisory is checked
94     *
95     * @param string $name Name of the event
96     * @param string $advise BEFORE, AFTER or empty
97     * @return bool
98     */
99    public function hasHandlerForEvent($name, $advise = '')
100    {
101        if ($advise) {
102            return isset($this->hooks[$name . '_' . $advise]);
103        }
104
105        return isset($this->hooks[$name . '_BEFORE']) || isset($this->hooks[$name . '_AFTER']);
106    }
107
108    /**
109     * Get all hooks and their currently registered handlers
110     *
111     * The handlers are sorted by sequence, then by register time
112     *
113     * @return array
114     */
115    public function getEventHandlers()
116    {
117        return $this->hooks;
118    }
119}
120