Completed
Push — master ( 163dca...16a88a )
by Anton
02:43
created

EventEmitterGlobal::loadListeners()   D

Complexity

Conditions 9
Paths 23

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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