Completed
Push — dev ( 236c7b...42488b )
by Aleksander
01:57
created

EventDispatcher::injectDispatcherParameters()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.2
cc 4
eloc 6
nc 2
nop 2
1
<?php
2
3
/**
4
 * @copyright 2017 Aleksander Stelmaczonek <[email protected]>
5
 * @license MIT License, see license file distributed with this source code
6
 */
7
8
namespace Koriit\EventDispatcher;
9
10
use DI\InvokerInterface;
11
use Koriit\EventDispatcher\Exceptions\InvalidPriority;
12
use Koriit\EventDispatcher\Exceptions\OverriddenParameter;
13
14
/**
15
 * @author Aleksander Stelmaczonek <[email protected]>
16
 */
17
class EventDispatcher implements EventDispatcherInterface
18
{
19
    /**
20
     * @var array
21
     */
22
    protected $listeners = [];
23
24
    /**
25
     * @var InvokerInterface
26
     */
27
    protected $invoker;
28
29
    /**
30
     * Indicates whether listeners array can be assumed to be sorted.
31
     *
32
     * @var bool
33
     */
34
    protected $listenersSorted = true;
35
36
    public function __construct(InvokerInterface $invoker)
37
    {
38
        $this->invoker = $invoker;
39
    }
40
41
    public function dispatch($eventName, $parameters = [])
42
    {
43
        $eventContext = new EventContext($eventName);
44
        $this->injectDispatcherParameters($eventContext, $parameters);
45
46
        if (isset($this->listeners[$eventName])) {
47
            $this->sortListenersByPriority();
48
49
            foreach ($this->listeners[$eventName] as $listenersByPriority) {
50
                foreach ($listenersByPriority as $listener) {
51
                    $this->invokeListener($eventContext, $listener, $parameters);
52
                }
53
            }
54
        }
55
56
        return $eventContext;
57
    }
58
59
    public function addListener($eventName, $listener, $priority = 0)
60
    {
61
        $this->validatePriority($priority);
62
63
        $this->listeners[$eventName][$priority][] = $listener;
64
        $this->listenersSorted = false;
65
    }
66
67
    public function addListeners($listeners)
68
    {
69
        foreach ($listeners as $eventName => $listenersByPriority) {
70
            foreach ($listenersByPriority as $priority => $newListeners) {
71
                $this->validatePriority($priority);
72
73
                foreach ($newListeners as $listener) {
74
                    $this->listeners[$eventName][$priority][] = $listener;
75
                }
76
            }
77
        }
78
79
        $this->listenersSorted = false;
80
    }
81
82
    public function getListeners($eventName)
83
    {
84
        return isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [];
85
    }
86
87
    public function getAllListeners()
88
    {
89
        return $this->listeners;
90
    }
91
92
    public function removeListener($eventName, $listener)
93
    {
94
        if (!isset($this->listeners[$eventName])) {
95
            return;
96
        }
97
98
        foreach ($this->listeners[$eventName] as $priority => $listeners) {
99
            $key = array_search($listener, $listeners, true);
100
            if ($key !== false) {
101
                unset($this->listeners[$eventName][$priority][$key]);
102
                if (empty($this->listeners[$eventName][$priority])) {
103
                    unset($this->listeners[$eventName][$priority]);
104
                }
105
            }
106
        }
107
108
        if (empty($this->listeners[$eventName])) {
109
            unset($this->listeners[$eventName]);
110
        }
111
    }
112
113
    public function hasListeners($eventName = null)
114
    {
115
        return $eventName !== null ? !empty($this->listeners[$eventName]) : !empty($this->listeners);
116
    }
117
118
    /**
119
     * Sorts internal listeners array by priority.
120
     */
121
    protected function sortListenersByPriority()
122
    {
123
        if ($this->listenersSorted) {
124
            return;
125
        }
126
127
        foreach (array_keys($this->listeners) as $eventName) {
128
            ksort($this->listeners[$eventName]);
129
        }
130
131
        $this->listenersSorted = true;
132
    }
133
134
    /**
135
     * @param EventContext $eventContext
136
     * @param callable $listener
137
     * @param array $parameters
138
     */
139
    protected function invokeListener($eventContext, $listener, $parameters = [])
140
    {
141
        if ($eventContext->isStopped()) {
142
            $eventContext->addStoppedListener($listener);
143
            return;
144
        }
145
146
        $eventContext->ignoreReturnValue(false);
147
        $returnValue = $this->invoker->call($listener, $parameters);
148
149
        if ($eventContext->isStopped()) {
150
            $eventContext->setStopValue(true);
151
        } else if (!$eventContext->isReturnValueIgnored() && $returnValue) {
152
            $eventContext->setStopped(true);
153
            $eventContext->setStopValue($returnValue);
154
        }
155
156
        $eventContext->addExecutedListener($listener);
157
    }
158
159
    /**
160
     * Injects predefined disptacher objects into parameters array.
161
     *
162
     * @param EventContextInterface $eventContext
163
     * @param array $parameters
164
     */
165
    protected function injectDispatcherParameters($eventContext, &$parameters)
166
    {
167
        if (isset($parameters['eventName']) || isset($parameters['eventContext']) || isset($parameters['eventDispatcher'])) {
168
            throw new OverriddenParameter('Following parameters cannot be passed in parameters array: eventName, eventContext, eventDispatcher.');
169
        }
170
171
        $parameters['eventName'] = $eventContext->getEventName();
172
        $parameters['eventContext'] = $eventContext;
173
        $parameters['eventDispatcher'] = $this;
174
    }
175
176
    /**
177
     * @param mixed $priority
178
     */
179
    protected function validatePriority($priority)
180
    {
181
        if (!is_int($priority) || $priority < 0) {
182
            throw new InvalidPriority('Expected non-negative integer priority. Provided: ' . $priority);
183
        }
184
    }
185
}
186