Passed
Push — master ( dfe2d2...d6a9e4 )
by Anton
02:18
created

EventEmitterStatic::isValidCallback()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 1
nc 5
nop 1
dl 0
loc 2
rs 8.8571
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * @Author : a.zinovyev
6
 * @Package: emittr
7
 * @License: http://www.opensource.org/licenses/mit-license.php
8
 */
9
10
namespace xobotyi\emittr;
11
12
/**
13
 * Class EventEmitterStatic
14
 *
15
 * @method static void      emit(string $evtName, $payload = null)
16
 * @method static int       getMaxListeners()
17
 * @method static void      setMaxListeners(int $listenersCount)
18
 * @method static array     getEventNames()
19
 * @method static array     getListeners(?string $eventName = null)
20
 * @method static void      on(string $evtName, callable $callback)
21
 * @method static void      once(string $evtName, callable $callback)
22
 * @method static void      prependListener(string $evtName, callable $callback)
23
 * @method static void      prependOnceListener(string $evtName, callable $callback)
24
 * @method static void      removeAllListeners(?string $evtName = null)
25
 * @method static void      removeListener(string $evtName, callable $callback)
26
 *
27
 * @package xobotyi\emittr
28
 */
29
class EventEmitterStatic
30
{
31
    public const EVENT_LISTENER_ADDED   = 'listenerAdded';
32
    public const EVENT_LISTENER_REMOVED = 'listenerRemoved';
33
34
    protected static $staticListeners    = [];
35
    protected static $staticMaxListeners = [];
36
37
    public static function __callStatic($name, $arguments) {
38
        $calledClass = get_called_class();
39
        if (method_exists($calledClass, '_' . $name . 'Static')) {
40
            return forward_static_call_array([$calledClass, '_' . $name . 'Static'], $arguments);
41
        }
42
43
        throw new \Error('Call to undefined method ' . $calledClass . '::' . $name . '()');
44
    }
45
46
    private static function _emitStatic(string $evtName, $payload = null) :void {
47
        $calledClass = get_called_class();
48
49
        $event = new Event($evtName, $payload, $calledClass);
50
        if (!(self::$staticListeners[$calledClass] ?? false) || self::propagateEvent($event, self::$staticListeners[$calledClass])) {
51
            EventEmitterGlobal::propagateClassEvent($event);
52
        }
53
    }
54
55
    protected static function propagateEvent(Event $evt, array &$listeners) :bool {
56
        if (!($listeners = &$listeners[$evt->getEventName()] ?? false)) {
57
            return true;
58
        }
59
60
        $res = true;
61
62
        foreach ($listeners as $key => &$listener) {
63
            if (($evt->getEventName() === self::EVENT_LISTENER_ADDED || $evt->getEventName() === self::EVENT_LISTENER_REMOVED) &&
64
                $listener[1] === $evt->getPayload()['callback']) {
65
                continue;
66
            }
67
68
            call_user_func($listener[1], $evt);
69
70
            if ($listener[0]) {
71
                unset($listeners[$key]);
72
            }
73
74
            if (!$evt->isPropagatable()) {
75
                $res = false;
76
                break;
77
            }
78
        }
79
80
        if (!count($listeners)) {
81
            unset($listeners);
82
        }
83
84
        return $res;
85
    }
86
87
    private static function _getEventNamesStatic() :array {
88
        return \array_keys(self::$staticListeners[get_called_class()] ?? []);
89
    }
90
91
    private static function _getListenersStatic(?string $eventName = null) :array {
92
        return $eventName ? self::$staticListeners[get_called_class()][$eventName] ?? [] : self::$staticListeners[get_called_class()];
93
    }
94
95
    private static function _onStatic(string $evtName, callable $callback) :void {
96
        $calledClass = get_called_class();
97
98
        if (!isset(self::$staticListeners[$calledClass])) {
99
            self::$staticListeners[$calledClass] = [];
100
        }
101
102
        self::storeCallback(self::$staticListeners[$calledClass], $evtName, $callback, false, false, self::_getMaxListenersStatic());
103
104
        self::emit(self::EVENT_LISTENER_ADDED, ['eventName' => $evtName, 'callback' => $callback, 'once' => false]);
105
    }
106
107
    private static function _onceStatic(string $evtName, $callback) :void {
108
        $calledClass = get_called_class();
109
110
        if (!isset(self::$staticListeners[$calledClass])) {
111
            self::$staticListeners[$calledClass] = [];
112
        }
113
114
        self::storeCallback(self::$staticListeners[$calledClass], $evtName, $callback, true, false, self::_getMaxListenersStatic());
115
116
        self::emit(self::EVENT_LISTENER_ADDED, ['eventName' => $evtName, 'callback' => $callback, 'once' => true]);
117
    }
118
119
    private static function _prependListenerStatic(string $evtName, $callback) :void {
120
        $calledClass = get_called_class();
121
122
        if (!isset(self::$staticListeners[$calledClass])) {
123
            self::$staticListeners[$calledClass] = [];
124
        }
125
126
        self::storeCallback(self::$staticListeners[$calledClass], $evtName, $callback, false, true, self::_getMaxListenersStatic());
127
128
        self::emit(self::EVENT_LISTENER_ADDED, ['eventName' => $evtName, 'callback' => $callback, 'once' => false]);
129
    }
130
131
    private static function _prependOnceListenerStatic(string $evtName, $callback) :void {
132
        $calledClass = get_called_class();
133
134
        if (!isset(self::$staticListeners[$calledClass])) {
135
            self::$staticListeners[$calledClass] = [];
136
        }
137
138
        self::storeCallback(self::$staticListeners[$calledClass], $evtName, $callback, true, true, self::_getMaxListenersStatic());
139
140
        self::emit(self::EVENT_LISTENER_ADDED, ['eventName' => $evtName, 'callback' => $callback, 'once' => true]);
141
    }
142
143
    private static function _removeAllListenersStatic(?string $evtName = null) :void {
144
        $calledClass = get_called_class();
145
146
        if (!isset(self::$staticListeners[$calledClass])) {
147
            return;
148
        }
149
150
        if (!$evtName) {
151
            foreach (self::$staticListeners as $eventname => &$listeners) {
152
                foreach ($listeners as &$callback) {
153
                    self::emit(self::EVENT_LISTENER_REMOVED, ['eventName' => $evtName, 'callback' => &$callback[1]]);
154
                }
155
            }
156
157
            self::$staticListeners[$calledClass] = [];
158
159
            return;
160
        }
161
162
        if (self::$staticListeners[$calledClass][$evtName] ?? false) {
163
            foreach (self::$staticListeners[$calledClass][$evtName] as &$callback) {
164
                self::emit(self::EVENT_LISTENER_REMOVED, ['eventName' => $evtName, 'callback' => &$callback[1]]);
165
            }
166
167
            unset(self::$staticListeners[$calledClass][$evtName]);
168
        }
169
    }
170
171
    private static function _removeListenerStatic(string $evtName, callable $callback) :void {
172
        $calledClass = get_called_class();
173
        if (!(self::$staticListeners[$calledClass][$evtName] ?? false)) {
174
            return;
175
        }
176
177
        self::$staticListeners[$calledClass][$evtName] = array_filter(self::$staticListeners[$calledClass][$evtName], function ($item) use (&$callback) { return $item[1] !== $callback; });
178
179
        if (empty(self::$staticListeners[$calledClass][$evtName])) {
180
            unset(self::$staticListeners[$calledClass][$evtName]);
181
        }
182
183
        self::emit(self::EVENT_LISTENER_REMOVED, ['eventName' => $evtName, 'callback' => &$callback]);
184
    }
185
186
    private static function _setMaxListenersStatic(int $listenersCount) :void {
187
        if ($listenersCount < 0) {
188
            throw new \InvalidArgumentException('Listeners count must be greater or equal 0, got ' . $listenersCount);
189
        }
190
191
        self::$staticMaxListeners[get_called_class()] = $listenersCount;
192
    }
193
194
    private static function _getMaxListenersStatic() :int {
195
        return self::$staticMaxListeners[get_called_class()] ?? 10;
196
    }
197
198
    protected static function isValidCallback($callback) :bool {
199
        return is_callable($callback) || (is_array($callback) && count($callback) === 2 && is_string($callback[0]) && is_string($callback[1]));
200
    }
201
202
    protected static function storeCallback(array &$arrayToStore, string $eventName, &$callback, bool $once = false, bool $prepend = false, ?int $maxListeners = null) :void {
203
        if (!self::isValidCallback($callback)) {
204
            throw new Exception\EventEmitter("Event callback has to be a callable or an array of two elements representing classname and method to call");
205
        }
206
207
        $maxListeners = $maxListeners === null ? self::_getMaxListenersStatic() : $maxListeners;
208
209
        if (($arrayToStore[$eventName] ?? false) && $maxListeners && count($arrayToStore[$eventName]) === $maxListeners) {
210
            throw new Exception\EventEmitter("Maximum amount of listeners reached for event " . $eventName);
211
        }
212
213
        if (!isset($arrayToStore[$eventName])) {
214
            $arrayToStore[$eventName] = [];
215
        }
216
217
        $prepend
218
            ? array_unshift($arrayToStore[$eventName], [$once, $callback])
219
            : $arrayToStore[$eventName][] = [$once, $callback];
220
    }
221
}