Event::listen()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 3
Bugs 0 Features 1
Metric Value
cc 3
eloc 5
c 3
b 0
f 1
nc 4
nop 3
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 3
rs 10
1
<?php
2
3
/**
4
 * @author Marwan Al-Soltany <[email protected]>
5
 * @copyright Marwan Al-Soltany 2021
6
 * For the full copyright and license information, please view
7
 * the LICENSE file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace MAKS\Velox\Backend;
13
14
/**
15
 * A class that offers simple events handling functionality (dispatching and listening).
16
 *
17
 * Example:
18
 * ```
19
 * // listening on an event
20
 * Event::listen('some.event', function ($arg1, $arg2) {
21
 *     // do some stuff ...
22
 * });
23
 *
24
 * // dispatching an event
25
 * Event::dispatch('some.event', [$arg1, $arg2]);
26
 *
27
 * // check if an event has listeners
28
 * Event::hasListener('some.event');
29
 *
30
 * // check if an event is dispatched
31
 * Event::isDispatch('some.event');
32
 *
33
 * // get a registered or a new event object
34
 * Event::get('some.event');
35
 *
36
 * // get a all registered events
37
 * Event::getRegisteredEvents();
38
 * ```
39
 *
40
 * @package Velox\Backend
41
 * @since 1.2.0
42
 * @api
43
 */
44
class Event
45
{
46
    /**
47
     * Here live all bindings.
48
     */
49
    protected static array $events = [];
50
51
52
    /**
53
     * Dispatches the passed event by executing all attached listeners and passes them the passed arguments.
54
     *
55
     * @param string $event Event name.
56
     * @param array $arguments [optional] Arguments array.
57
     *      Note that the arguments will be spread (`...$args`) on the callback and an additional argument of the event name will be appended to the arguments.
58
     * @param object|null $callbackThis [optional] The object the callback should be bound to.
59
     *
60
     * @return void
61
     */
62 30
    public static function dispatch(string $event, ?array $arguments = null, ?object $callbackThis = null): void
63
    {
64 30
        if (static::isDispatched($event) === false) {
65 11
            static::get($event)->dispatched = true;
66
        }
67
68 30
        if (static::hasListener($event) === false) {
69 21
            return;
70
        }
71
72 27
        $callbacks = &static::get($event)->listeners;
73
74 27
        $parameters = array_merge(array_values($arguments ?? []), [$event]);
75
76
        // array_walk is used instead of foreach to give the possibility
77
        // for the callback to attach new listeners to the current event
78 27
        array_walk($callbacks, function (&$callback) use (&$callbackThis, &$parameters) {
79
            /** @var \Closure $callback */
80 27
            if ($callbackThis) {
81 10
                $callback->call($callbackThis, ...$parameters);
82
83 10
                return;
84
            }
85
86 20
            $callback(...$parameters);
87
        });
88
    }
89
90
    /**
91
     * Listens on the passed event and attaches the passed callback to it.
92
     *
93
     * @param string $event Event name.
94
     * @param callable $callback A callback to process the event.
95
     * @param int $priority [optional] The priority of the listener.
96
     *      Higher number means higher priority, numbers can be positive only (negative numbers are treated as zero).
97
     *      Zero (`0`) is reserved to add to the end of the stack (lowest priority).
98
     *
99
     * @return void
100
     */
101 1
    public static function listen(string $event, callable $callback, int $priority = 0): void
102
    {
103 1
        $callback = $callback instanceof \Closure ? $callback : \Closure::fromCallable($callback);
104
105 1
        $listeners = &static::get($event)->listeners;
106
107 1
        $priority <= 0
108 1
            ? array_push($listeners, $callback)
109 1
            : array_splice($listeners, $priority * -1, 0, $callback);
110
    }
111
112
    /**
113
     * Checks whether an event has already been dispatched or not.
114
     *
115
     * @param string $event Event name.
116
     *
117
     * @return bool
118
     *
119
     * @since 1.5.0
120
     */
121 30
    public static function isDispatched(string $event): bool
122
    {
123 30
        return static::get($event)->dispatched === true;
124
    }
125
126
    /**
127
     * Checks whether an event has any listeners or not.
128
     *
129
     * @param string $event Event name.
130
     *
131
     * @return bool
132
     *
133
     * @since 1.5.0
134
     */
135 30
    public static function hasListener(string $event): bool
136
    {
137 30
        return empty(static::get($event)->listeners) === false;
138
    }
139
140
    /**
141
     * Returns an event object by its name or creates it if it does not exist.
142
     * The event object consists of the following properties:
143
     * - `name`: The event name.
144
     * - `dispatched`: A boolean flag indicating whether the event has been dispatched or not.
145
     * - `listeners`: An array of callbacks.
146
     *
147
     * @param string $event Event name.
148
     *
149
     * @return object
150
     *
151
     * @since 1.5.0
152
     */
153 30
    public static function get(string $event): object
154
    {
155 30
        return static::$events[$event] ?? static::create($event);
156
    }
157
158
    /**
159
     * Returns array of all registered events as an array `['event.name' => $eventObject, ...]`.
160
     *
161
     * @return object[]
162
     */
163 1
    public static function getRegisteredEvents(): array
164
    {
165 1
        return static::$events;
166
    }
167
168
    /**
169
     * Creates an event object and adds it to the registered events.
170
     *
171
     * @param string $event Event name.
172
     *
173
     * @return object
174
     *
175
     * @since 1.5.0
176
     */
177 11
    protected static function create(string $event): object
178
    {
179 11
        return static::$events[$event] = (object)[
180
            'name'       => $event,
181
            'dispatched' => false,
182
            'listeners'  => [],
183
        ];
184
    }
185
}
186