Passed
Push — master ( 339e28...0dda89 )
by Anton
02:55
created

EventEmitterGlobal::isValidCallback()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 1
nc 9
nop 1
dl 0
loc 2
rs 8.8571
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 on(string $className, string $eventName, $callback) :self {
87
        if (!isset($this->listeners[$className][$eventName])) {
88
            $this->listeners[$className][$eventName] = [];
89
        }
90
91
        self::storeCallback($this->listeners[$className], $eventName, $callback, $this->maxListenersCount, false, false);
92
93
        return $this;
94
    }
95
96
    public function once(string $className, string $eventName, $callback) :self {
97
        if (!isset($this->listeners[$className][$eventName])) {
98
            $this->listeners[$className][$eventName] = [];
99
        }
100
101
        self::storeCallback($this->listeners[$className], $eventName, $callback, $this->maxListenersCount, true, false);
102
103
        return $this;
104
    }
105
106
    public function prependListener(string $className, string $eventName, $callback) :self {
107
        if (!isset($this->listeners[$className][$eventName])) {
108
            $this->listeners[$className][$eventName] = [];
109
        }
110
111
        self::storeCallback($this->listeners[$className], $eventName, $callback, $this->maxListenersCount, false, true);
112
113
        return $this;
114
    }
115
116
    public function prependOnceListener(string $className, string $eventName, $callback) :self {
117
        if (!isset($this->listeners[$className][$eventName])) {
118
            $this->listeners[$className][$eventName] = [];
119
        }
120
121
        self::storeCallback($this->listeners[$className], $eventName, $callback, $this->maxListenersCount, true, true);
122
123
        return $this;
124
    }
125
126
    public function off(string $className, string $eventName, $callback) :self {
127
        if (empty($this->listeners[$className][$eventName])) {
128
            return $this;
129
        }
130
131
        $this->listeners[$className][$eventName] = \array_values(\array_filter($this->listeners[$className][$eventName], function ($item) use (&$callback) { return $item[1] !== $callback; }));
132
133
        if (empty($this->listeners[$className][$eventName])) {
134
            unset($this->listeners[$className][$eventName]);
135
        }
136
137
        if (empty($this->listeners[$className])) {
138
            unset($this->listeners[$className]);
139
        }
140
141
        return $this;
142
    }
143
144
    public function removeAllListeners(?string $className = null, ?string $eventName = null) :self {
145
        if ($className === null) {
146
            $this->listeners = [];
147
148
            return $this;
149
        }
150
151
        if (empty($this->listeners[$className])) {
152
            return $this;
153
        }
154
155
        if ($eventName === null) {
156
            unset($this->listeners[$className]);
157
158
            return $this;
159
        }
160
161
        if (empty($this->listeners[$className][$eventName])) {
162
            return $this;
163
        }
164
165
        unset($this->listeners[$className][$eventName]);
166
167
        if (empty($this->listeners[$className])) {
168
            unset($this->listeners[$className]);
169
        }
170
171
        return $this;
172
    }
173
174
    public function getEventNames(string $className) :array {
175
        return empty($this->listeners[$className]) ? [] : \array_keys($this->listeners[$className]);
176
    }
177
178
    public function getListeners(?string $className = null, ?string $eventName = null) :array {
179
        return $className ? $eventName ? $this->listeners[$className][$eventName] ?? [] : $this->listeners[$className] ?? [] : $this->listeners;
180
    }
181
182
    public function getMaxListenersCount() :int {
183
        return $this->maxListenersCount;
184
    }
185
186
    public function setMaxListenersCount(int $maxListenersCount) :self {
187
        if ($maxListenersCount < 0) {
188
            throw new \InvalidArgumentException('Listeners count must be greater or equal 0, got ' . $maxListenersCount);
189
        }
190
191
        $this->maxListenersCount = $maxListenersCount;
192
193
        return $this;
194
    }
195
196
    public function propagateEventGlobal(Event $event) :bool {
197
        if (substr($event->getSourceClass(), 0, 15) === 'class@anonymous') {
198
            return true;
199
        }
200
201
        if (empty($this->listeners[$event->getSourceClass()])) {
202
            return true;
203
        }
204
205
        return self::propagateEvent($event, $this->listeners[$event->getSourceClass()]);
206
    }
207
}