Test Failed
Push — master ( a25a4c...498f08 )
by Divine Niiquaye
13:17
created

EventHandler   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 72
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 24
c 1
b 0
f 1
dl 0
loc 72
rs 10
wmc 13

4 Methods

Rating   Name   Duplication   Size   Complexity  
A hasListeners() 0 13 4
A getListenersForEvent() 0 15 4
A dispatch() 0 15 4
A addListener() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of DivineNii opensource projects.
7
 *
8
 * PHP version 7.4 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 DivineNii (https://divinenii.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Rade\Handler;
19
20
use Psr\EventDispatcher\{EventDispatcherInterface, ListenerProviderInterface, StoppableEventInterface};
21
22
/**
23
 * A fully strict PSR 14 dispatcher and listener.
24
 *
25
 * @author Divine Niiquaye Ibok <[email protected]>
26
 */
27
class EventHandler implements EventDispatcherInterface, ListenerProviderInterface
28
{
29
    private array $listeners = [], $calledEvents = [];
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function dispatch(object $event): object
35
    {
36
        $stoppable = $event instanceof StoppableEventInterface;
37
        $listeners = $this->calledEvents[\get_class($event)] ?? $this->getListenersForEvent($event);
38
39
        /** @var callable(object) $listener */
40
        foreach ($listeners as $listener) {
41
            if ($stoppable && $event->isPropagationStopped()) {
42
                return $event;
43
            }
44
45
            $listener($event);
46
        }
47
48
        return $event;
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function getListenersForEvent(object $event): iterable
55
    {
56
        if (!\array_key_exists($eventName = \get_class($event), $this->calledEvents)) {
57
            if (empty($listeners = $this->listeners[$eventName] ?? [])) {
58
                return [];
59
            }
60
61
            \krsort($listeners); // Sort Listeners by priority.
62
63
            foreach ($listeners as $calledListeners) {
64
                $this->calledEvents[$eventName] = $calledListeners;
65
            }
66
        }
67
68
        return $this->calledEvents[$eventName];
69
    }
70
71
    /**
72
     * Attaches listener to corresponding event based on the type-hint used for the event argument.
73
     *
74
     * @param callable $listener Any callable could be used be it a closure or invokable object
75
     * @param int $priority The higher this value, the earlier an event listener will be triggered in the chain (defaults to 0)
76
     */
77
    public function addListener(string $eventClass, $listener, int $priority = 0): void
78
    {
79
        $this->listeners[$eventClass][$priority][] = $listener;
80
        unset($this->calledEvents[$eventClass]);
81
    }
82
83
    /**
84
     * Checks if listeners exist for an event, else in general if event name is null.
85
     */
86
    public function hasListeners(string $eventName = null): bool
87
    {
88
        if (null !== $eventName) {
89
            return !empty($this->listeners[$eventName]);
90
        }
91
92
        foreach ($this->listeners as $eventListeners) {
93
            if ($eventListeners) {
94
                return true;
95
            }
96
        }
97
98
        return false;
99
    }
100
}
101