This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php declare(strict_types=1); |
||
2 | |||
3 | namespace Limoncello\Events; |
||
4 | |||
5 | /** |
||
6 | * Copyright 2015-2019 [email protected] |
||
7 | * |
||
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||
9 | * you may not use this file except in compliance with the License. |
||
10 | * You may obtain a copy of the License at |
||
11 | * |
||
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
||
13 | * |
||
14 | * Unless required by applicable law or agreed to in writing, software |
||
15 | * distributed under the License is distributed on an "AS IS" BASIS, |
||
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
17 | * See the License for the specific language governing permissions and |
||
18 | * limitations under the License. |
||
19 | */ |
||
20 | |||
21 | use Closure; |
||
22 | use Limoncello\Events\Contracts\EventDispatcherInterface; |
||
23 | use Limoncello\Events\Contracts\EventEmitterInterface; |
||
24 | use Limoncello\Events\Contracts\EventInterface; |
||
25 | use Limoncello\Events\Exceptions\EventNotFoundException; |
||
26 | use ReflectionException; |
||
27 | use ReflectionMethod; |
||
28 | use function assert; |
||
29 | use function array_filter; |
||
30 | use function array_key_exists; |
||
31 | use function call_user_func_array; |
||
32 | use function count; |
||
33 | use function explode; |
||
34 | use function get_class; |
||
35 | use function is_array; |
||
36 | use function is_string; |
||
37 | |||
38 | /** |
||
39 | * @package Limoncello\Events |
||
40 | */ |
||
41 | class SimpleEventEmitter implements EventEmitterInterface, EventDispatcherInterface |
||
42 | { |
||
43 | /** |
||
44 | * All events known to system with or without corresponding event handler. |
||
45 | * |
||
46 | * @var array |
||
47 | */ |
||
48 | private $allowedEvents = []; |
||
49 | |||
50 | /** |
||
51 | * @var array |
||
52 | */ |
||
53 | private $subscribers = []; |
||
54 | |||
55 | /** |
||
56 | * @var bool |
||
57 | */ |
||
58 | private $cancellingEnabled = false; |
||
59 | |||
60 | /** |
||
61 | * @inheritdoc |
||
62 | */ |
||
63 | 5 | public function emit(string $eventName, array $arguments = []): void |
|
64 | { |
||
65 | 5 | $this->isCancellingEnabled() === true ? |
|
66 | 1 | $this->emitWithCancellingPropagationCheck($eventName, $arguments) : |
|
67 | 5 | $this->emitWithoutCancellingPropagationCheck($eventName, $arguments); |
|
68 | } |
||
69 | |||
70 | /** |
||
71 | * @inheritdoc |
||
72 | */ |
||
73 | 1 | public function dispatch(EventInterface $event): void |
|
74 | { |
||
75 | 1 | $this->emit(get_class($event), [$event]); |
|
76 | } |
||
77 | |||
78 | /** |
||
79 | * @param string $eventName |
||
80 | * |
||
81 | * @return SimpleEventEmitter |
||
82 | */ |
||
83 | 5 | public function addAllowedEvent(string $eventName): self |
|
84 | { |
||
85 | 5 | $this->allowedEvents[$eventName] = true; |
|
86 | |||
87 | 5 | return $this; |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * @param string[] $eventNames |
||
92 | * |
||
93 | * @return SimpleEventEmitter |
||
94 | */ |
||
95 | 2 | public function addAllowedEvents(array $eventNames): self |
|
96 | { |
||
97 | 2 | foreach ($eventNames as $eventName) { |
|
98 | 2 | $this->addAllowedEvent($eventName); |
|
99 | } |
||
100 | |||
101 | 2 | return $this; |
|
102 | } |
||
103 | |||
104 | /** |
||
105 | * @param string $eventName |
||
106 | * @param callable $subscriber |
||
107 | * |
||
108 | * @return self |
||
109 | * |
||
110 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
111 | */ |
||
112 | 5 | public function subscribe(string $eventName, callable $subscriber): self |
|
113 | { |
||
114 | 5 | if ($subscriber instanceof Closure || ($staticMethod = $this->parseStaticMethod($subscriber)) === null) { |
|
115 | 3 | $this->subscribers[$eventName][] = $subscriber; |
|
116 | } else { |
||
117 | 5 | assert($staticMethod !== null); |
|
118 | 5 | $this->subscribers[$eventName][] = $this->getUnifiedStaticMethodRepresentation($staticMethod); |
|
119 | } |
||
120 | |||
121 | 5 | $this->addAllowedEvent($eventName); |
|
122 | |||
123 | 5 | return $this; |
|
124 | } |
||
125 | |||
126 | /** |
||
127 | * @param string $eventName |
||
128 | * @param callable $subscriber |
||
129 | * |
||
130 | * @return self |
||
131 | */ |
||
132 | 1 | public function unSubscribe(string $eventName, callable $subscriber): self |
|
133 | { |
||
134 | 1 | if (($subscriber instanceof Closure) === false && |
|
135 | 1 | ($staticMethod = $this->parseStaticMethod($subscriber)) !== null |
|
136 | ) { |
||
137 | 1 | $subscriber = $this->getUnifiedStaticMethodRepresentation($staticMethod); |
|
138 | } |
||
139 | |||
140 | 1 | $eventSubscribers = $this->getEventSubscribers($eventName); |
|
141 | $eventSubscribers = array_filter($eventSubscribers, function ($curSubscriber) use ($subscriber) { |
||
142 | 1 | return $curSubscriber !== $subscriber; |
|
143 | 1 | }); |
|
144 | |||
145 | 1 | return $this->setEventSubscribers($eventName, $eventSubscribers); |
|
146 | } |
||
147 | |||
148 | /** |
||
149 | * @return bool |
||
150 | */ |
||
151 | 5 | public function isCancellingEnabled(): bool |
|
152 | { |
||
153 | 5 | return $this->cancellingEnabled; |
|
154 | } |
||
155 | |||
156 | /** |
||
157 | * @return self |
||
158 | */ |
||
159 | 2 | public function enableCancelling(): self |
|
160 | { |
||
161 | 2 | $this->cancellingEnabled = true; |
|
162 | |||
163 | 2 | return $this; |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * @return self |
||
168 | */ |
||
169 | 3 | public function disableCancelling(): self |
|
170 | { |
||
171 | 3 | $this->cancellingEnabled = false; |
|
172 | |||
173 | 3 | return $this; |
|
174 | } |
||
175 | |||
176 | /** |
||
177 | * @param array $data |
||
178 | * |
||
179 | * @return self |
||
0 ignored issues
–
show
|
|||
180 | */ |
||
181 | 3 | public function setData(array $data): self |
|
182 | { |
||
183 | 3 | assert(count($data) == 2); |
|
184 | |||
185 | 3 | [$allowedEvents, $subscribers] = $data; |
|
0 ignored issues
–
show
The variable
$allowedEvents does not exist. Did you forget to declare it?
This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.
Loading history...
The variable
$subscribers does not exist. Did you forget to declare it?
This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.
Loading history...
|
|||
186 | |||
187 | 3 | assert($this->checkAllSubscribersAreStatic($subscribers) === true); |
|
188 | |||
189 | 3 | return $this->setAllowedEvents($allowedEvents)->setSubscribers($subscribers); |
|
190 | } |
||
191 | |||
192 | /** |
||
193 | * @return array |
||
194 | */ |
||
195 | 4 | public function getData(): array |
|
196 | { |
||
197 | 4 | $subscribers = []; |
|
198 | |||
199 | 4 | foreach ($this->getSubscribers() as $eventName => $subscribersList) { |
|
200 | 4 | $eventSubscribers = []; |
|
201 | 4 | foreach ($subscribersList as $subscriber) { |
|
202 | 4 | if (($staticMethod = $this->parseStaticMethod($subscriber)) !== null) { |
|
203 | 4 | $eventSubscribers[] = $this->getUnifiedStaticMethodRepresentation($staticMethod); |
|
204 | } |
||
205 | } |
||
206 | |||
207 | 4 | if (empty($eventSubscribers) === false) { |
|
208 | 4 | $subscribers[$eventName] = $eventSubscribers; |
|
209 | } |
||
210 | } |
||
211 | |||
212 | 4 | $data = [$this->getAllowedEvents(), $subscribers]; |
|
213 | |||
214 | 4 | return $data; |
|
215 | } |
||
216 | |||
217 | /** |
||
218 | * @param string $eventName |
||
219 | * |
||
220 | * @return bool |
||
221 | */ |
||
222 | 5 | protected function isEventAllowed(string $eventName): bool |
|
223 | { |
||
224 | 5 | return array_key_exists($eventName, $this->allowedEvents); |
|
225 | } |
||
226 | |||
227 | /** |
||
228 | * @return array |
||
229 | */ |
||
230 | 4 | protected function getAllowedEvents(): array |
|
231 | { |
||
232 | 4 | return $this->allowedEvents; |
|
233 | } |
||
234 | |||
235 | /** |
||
236 | * @param array $allowedEvents |
||
237 | * |
||
238 | * @return self |
||
239 | */ |
||
240 | 3 | protected function setAllowedEvents(array $allowedEvents): self |
|
241 | { |
||
242 | 3 | $this->allowedEvents = $allowedEvents; |
|
243 | |||
244 | 3 | return $this; |
|
245 | } |
||
246 | |||
247 | /** |
||
248 | * @return array |
||
249 | */ |
||
250 | 5 | protected function getSubscribers(): array |
|
251 | { |
||
252 | 5 | return $this->subscribers; |
|
253 | } |
||
254 | |||
255 | /** |
||
256 | * @param callable[] $subscribers |
||
257 | * |
||
258 | * @return self |
||
259 | */ |
||
260 | 3 | protected function setSubscribers(array $subscribers): self |
|
261 | { |
||
262 | 3 | $this->subscribers = $subscribers; |
|
263 | |||
264 | 3 | return $this; |
|
265 | } |
||
266 | |||
267 | /** |
||
268 | * @param string $eventName |
||
269 | * @param array $arguments |
||
270 | * |
||
271 | * @return void |
||
272 | */ |
||
273 | 5 | protected function emitWithoutCancellingPropagationCheck(string $eventName, array $arguments = []): void |
|
274 | { |
||
275 | 5 | foreach ($this->getEventSubscribers($eventName) as $subscriber) { |
|
276 | 4 | call_user_func_array($subscriber, $arguments); |
|
277 | } |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * @param string $eventName |
||
282 | * @param array $arguments |
||
283 | * |
||
284 | * @return void |
||
285 | */ |
||
286 | 1 | protected function emitWithCancellingPropagationCheck(string $eventName, array $arguments = []): void |
|
287 | { |
||
288 | 1 | foreach ($this->getEventSubscribers($eventName) as $subscriber) { |
|
289 | 1 | if (call_user_func_array($subscriber, $arguments) === false) { |
|
290 | 1 | break; |
|
291 | } |
||
292 | } |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * @param string $eventName |
||
297 | * |
||
298 | * @return array |
||
299 | */ |
||
300 | 5 | private function getEventSubscribers(string $eventName): array |
|
301 | { |
||
302 | 5 | if ($this->isEventAllowed($eventName) === false) { |
|
303 | 1 | throw new EventNotFoundException($eventName); |
|
304 | } |
||
305 | |||
306 | 4 | $result = $this->getSubscribers()[$eventName] ?? []; |
|
307 | |||
308 | 4 | return $result; |
|
309 | } |
||
310 | |||
311 | /** |
||
312 | * @param string $eventName |
||
313 | * @param callable[] $eventSubscribers |
||
314 | * |
||
315 | * @return self |
||
316 | */ |
||
317 | 1 | private function setEventSubscribers(string $eventName, array $eventSubscribers): self |
|
318 | { |
||
319 | 1 | $this->subscribers[$eventName] = $eventSubscribers; |
|
320 | |||
321 | 1 | return $this; |
|
322 | } |
||
323 | |||
324 | /** |
||
325 | * This debugging function checks subscribers are |
||
326 | * [ |
||
327 | * ... |
||
328 | * 'string_event_name' => [static callable, static callable, ...], |
||
329 | * ... |
||
330 | * ] |
||
331 | * |
||
332 | * @param array $subscribers |
||
333 | * |
||
334 | * @return bool |
||
335 | */ |
||
336 | 4 | private function checkAllSubscribersAreStatic(array $subscribers): bool |
|
337 | { |
||
338 | 4 | $result = true; |
|
339 | 4 | foreach ($subscribers as $eventName => $callableList) { |
|
340 | 4 | if (is_string($eventName) === false || is_array($callableList) === false) { |
|
341 | 1 | $result = false; |
|
342 | 1 | break; |
|
343 | } |
||
344 | 4 | foreach ($callableList as $mightBeCallable) { |
|
345 | 4 | $method = $this->parseStaticMethod($mightBeCallable); |
|
346 | 4 | if ($method === null || $method->isStatic() === false) { |
|
347 | 1 | $result = false; |
|
348 | 4 | break; |
|
349 | } |
||
350 | } |
||
351 | } |
||
352 | |||
353 | 4 | return $result; |
|
354 | } |
||
355 | |||
356 | /** |
||
357 | * @param $mightBeCallable |
||
358 | * |
||
359 | * @return null|ReflectionMethod |
||
360 | * |
||
361 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
362 | */ |
||
363 | 6 | private function parseStaticMethod($mightBeCallable): ?ReflectionMethod |
|
364 | { |
||
365 | // static callable could be in form of 'ClassName::methodName' or ['ClassName', 'methodName'] |
||
366 | 6 | if (is_string($mightBeCallable) === true && |
|
367 | 6 | count($mightBeCallablePair = explode('::', $mightBeCallable, 2)) === 2 |
|
368 | ) { |
||
369 | 1 | list ($mightBeClassName, $mightBeMethodName) = $mightBeCallablePair; |
|
370 | 6 | } elseif (is_array($mightBeCallable) === true && count($mightBeCallable) === 2) { |
|
371 | 6 | list ($mightBeClassName, $mightBeMethodName) = $mightBeCallable; |
|
372 | } else { |
||
373 | 1 | return null; |
|
374 | } |
||
375 | |||
376 | try { |
||
377 | 6 | $reflectionMethod = new ReflectionMethod($mightBeClassName, $mightBeMethodName); |
|
378 | 1 | } catch (ReflectionException $exception) { |
|
379 | 1 | return null; |
|
380 | } |
||
381 | |||
382 | 5 | if ($reflectionMethod->isStatic() === false) { |
|
383 | 3 | return null; |
|
384 | } |
||
385 | |||
386 | 5 | return $reflectionMethod; |
|
387 | } |
||
388 | |||
389 | /** |
||
390 | * @param ReflectionMethod $staticMethod |
||
391 | * |
||
392 | * @return callable |
||
393 | */ |
||
394 | 5 | private function getUnifiedStaticMethodRepresentation(ReflectionMethod $staticMethod): callable |
|
395 | { |
||
396 | 5 | return [$staticMethod->class, $staticMethod->name]; |
|
397 | } |
||
398 | } |
||
399 |
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.