Completed
Push — master ( be7a44...a89c67 )
by Filipe
01:07 queued 10s
created

EventDispatcher::match()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
/**
4
 * This file is part of slick/event package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Event\Application;
11
12
use Psr\EventDispatcher\ListenerProviderInterface;
13
use Psr\EventDispatcher\StoppableEventInterface;
14
use Slick\Event\Domain\EventGeneratorMethods;
15
use Slick\Event\Event;
16
use Slick\Event\EventDispatcher as SlickEventDispatcher;
17
use Slick\Event\EventGenerator;
18
use Slick\Event\EventListener;
19
20
/**
21
 * EventDispatcher
22
 *
23
 * @package Slick\Event\Application
24
 */
25
final class EventDispatcher implements SlickEventDispatcher, EventGenerator
26
{
27
28
    use EventGeneratorMethods;
29
30
    /**
31
     * @var bool
32
     */
33
    private static $dispatching = false;
34
35
    /**
36
     * @var array
37
     */
38
    private $listeners = [];
39
40
    /**
41
     * Dispatches events form an event generator
42
     *
43
     * @param EventGenerator $generator
44
     * @return Event[] the list of dispatched events
45
     */
46
    public function dispatchEventsFrom(EventGenerator $generator): array
47
    {
48
        $events = [];
49
        foreach ($generator->releaseEvents() as $event) {
50
            $events[] = $this->dispatch($event);
51
        }
52
        return $events;
53
    }
54
55
    /**
56
     * Adds an event listener
57
     *
58
     * @param string $event
59
     * @param EventListener|Callable|ListenerProviderInterface $listener
60
     * @param int $priority
61
     */
62
    public function addListener(string $event, $listener, int $priority = SlickEventDispatcher::P_NORMAL): void
63
    {
64
        $this->listeners[$event][] = (object) [
65
            'listener' => $listener,
66
            'priority' => $priority
67
        ];
68
    }
69
70
    /**
71
     * Provide all relevant listeners with an event to process.
72
     *
73
     * @param object $event
74
     *   The object to process.
75
     *
76
     * @return object
77
     *   The Event that was passed, now modified by listeners.
78
     */
79
    public function dispatch(object $event)
80
    {
81
        if (self::$dispatching) {
82
            $this->recordThat($event);
83
            return $event;
84
        }
85
86
        self::$dispatching = true;
87
88
        foreach ($this->listenersFor($event) as $listener) {
89
            $event = $this->invokeListener($listener, $event);
90
            if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
91
                break;
92
            }
93
        }
94
95
        self::$dispatching = false;
96
        $this->dispatchEventsFrom($this);
97
        return $event;
98
    }
99
100
    /**
101
     * Prioritized list of listeners for provided event
102
     *
103
     * @param object $event
104
     * @return array
105
     */
106
    private function listenersFor(object $event): array
107
    {
108
        $listeners = [];
109
        $unordered = $this->orderedListeners(
110
            $this->matchedListeners($event)
111
        );
112
        foreach ($unordered as $data) {
113
            if ($data->listener instanceof ListenerProviderInterface === false) {
114
                $listeners[] = $data->listener;
115
                continue;
116
            }
117
118
            foreach ($data->listener->getListenersForEvent($event) as $listener) {
119
                $listeners[] = $listener;
120
            }
121
        }
122
        return $listeners;
123
    }
124
125
    /**
126
     * Invoke listener
127
     *
128
     * @param $listener
129
     * @param Object $event
130
     * @return Object
131
     */
132
    private function invokeListener($listener, Object $event)
133
    {
134
        if (is_callable($listener)) {
135
            return $listener($event);
136
        }
137
138
        if ($listener instanceof  EventListener) {
139
            return $listener->handle($event);
140
        }
141
142
        return $event;
143
    }
144
145
    /**
146
     * Matches the listener register pattern with event
147
     *
148
     * @param $pattern
149
     * @param object $event
150
     * @return bool
151
     */
152
    private function match($pattern, object $event): bool
153
    {
154
        $regEx = str_replace(['\\', '*'], ['\\\\', '(.*)'], $pattern);
155
        $regEx = "/$regEx/i";
156
        $name = get_class($event);
157
        return (bool) preg_match($regEx, $name);
158
    }
159
160
    /**
161
     * Filters the matched listeners
162
     *
163
     * @param object $event
164
     * @return array
165
     */
166
    private function matchedListeners(object $event): array
167
    {
168
        $unordered = [];
169
        foreach ($this->listeners as $pattern => $data) {
170
            if (!$this->match($pattern, $event)) {
171
                continue;
172
            }
173
174
            foreach ($data as $datum) {
175
                $unordered[] = $datum;
176
            }
177
        }
178
        return $unordered;
179
    }
180
181
    /**
182
     * orderedListeners
183
     *
184
     * @param array $unordered
185
     * @return array
186
     */
187
    private function orderedListeners(array $unordered): array
188
    {
189
        usort($unordered, function ($a, $b) {
190
            if ($a->priority > $b->priority) {
191
                return -1;
192
            }
193
194
            if ($a->priority === $b->priority) {
195
                return 0;
196
            }
197
198
            return 1;
199
        });
200
        return $unordered;
201
    }
202
}
203