PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.91
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.91
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Events / Dispatcher.php

Dispatcher.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 1.0.91, at vendor/wpfluent/framework/src/WPFluent/Events/Dispatcher.php

474 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Framework\Events;
4
5 use Closure;
6 use Exception;
7 use ReflectionClass;
8 use FluentCommunity\Framework\Support\Arr;
9 use FluentCommunity\Framework\Support\Str;
10 use FluentCommunity\Framework\Support\Helper;
11 use FluentCommunity\Framework\Container\Container;
12 use FluentCommunity\Framework\Support\MacroableTrait;
13 use FluentCommunity\Framework\Support\ReflectsClosures;
14 use FluentCommunity\Framework\Events\DispatcherInterface;
15 use FluentCommunity\Framework\Container\Contracts\Container as ContainerContract;
16
17
18 class Dispatcher implements DispatcherInterface
19 {
20 use MacroableTrait, ReflectsClosures;
21
22 /**
23 * The IoC container instance.
24 *
25 * @var \FluentCommunity\Framework\Container\Contracts\Container
26 */
27 protected $container;
28
29 /**
30 * The registered event listeners.
31 *
32 * @var array
33 */
34 protected $listeners = [];
35
36 /**
37 * The wildcard listeners.
38 *
39 * @var array
40 */
41 protected $wildcards = [];
42
43 /**
44 * The cached wildcard listeners.
45 *
46 * @var array
47 */
48 protected $wildcardsCache = [];
49
50 /**
51 * Create a new event dispatcher instance.
52 *
53 * @param \FluentCommunity\Framework\Container\Contracts\Container|null $container
54 * @return void
55 */
56 public function __construct(?ContainerContract $container = null)
57 {
58 $this->container = $container ?: new Container;
59 }
60
61 /**
62 * Register an event listener with the dispatcher.
63 *
64 * @param \Closure|string|array $events
65 * @param \Closure|string|array|null $listener
66 * @return void
67 */
68 public function listen($events, $listener = null)
69 {
70 if (class_exists('ReflectionUnionType')) {
71 if ($events instanceof Closure) {
72 return Helper::collect($this->firstClosureParameterTypes($events))
73 ->each(function ($event) use ($events) {
74 $this->listen($event, $events);
75 });
76 }
77 }
78
79 foreach ((array) $events as $event) {
80 if (Str::contains($event, '*')) {
81 $this->setupWildcardListen($event, $listener);
82 } else {
83 $this->listeners[$event][] = $this->makeListener($listener);
84 }
85 }
86 }
87
88 /**
89 * Setup a wildcard listener callback.
90 *
91 * @param string $event
92 * @param \Closure|string $listener
93 * @return void
94 */
95 protected function setupWildcardListen($event, $listener)
96 {
97 $this->wildcards[$event][] = $this->makeListener($listener, true);
98
99 $this->wildcardsCache = [];
100 }
101
102 /**
103 * Determine if a given event has listeners.
104 *
105 * @param string $eventName
106 * @return bool
107 */
108 public function hasListeners($eventName)
109 {
110 return isset($this->listeners[$eventName]) ||
111 isset($this->wildcards[$eventName]) ||
112 $this->hasWildcardListeners($eventName);
113 }
114
115 /**
116 * Determine if the given event has any wildcard listeners.
117 *
118 * @param string $eventName
119 * @return bool
120 */
121 public function hasWildcardListeners($eventName)
122 {
123 foreach ($this->wildcards as $key => $listeners) {
124 if (Str::is($key, $eventName)) {
125 return true;
126 }
127 }
128
129 return false;
130 }
131
132 /**
133 * Register an event and payload to be fired later.
134 *
135 * @param string $event
136 * @param array $payload
137 * @return void
138 */
139 public function push($event, $payload = [])
140 {
141 $this->listen($event.'_pushed', function () use ($event, $payload) {
142 $this->dispatch($event, $payload);
143 });
144 }
145
146 /**
147 * Flush a set of pushed events.
148 *
149 * @param string $event
150 * @return void
151 */
152 public function flush($event)
153 {
154 $this->dispatch($event.'_pushed');
155 }
156
157 /**
158 * Register an event subscriber with the dispatcher.
159 *
160 * @param object|string $subscriber
161 * @return void
162 */
163 public function subscribe($subscriber)
164 {
165 $subscriber = $this->resolveSubscriber($subscriber);
166
167 $events = $subscriber->subscribe($this);
168
169 if (is_array($events)) {
170 foreach ($events as $event => $listeners) {
171 foreach (Arr::wrap($listeners) as $listener) {
172 if (is_string($listener) && method_exists($subscriber, $listener)) {
173 $this->listen($event, [get_class($subscriber), $listener]);
174
175 continue;
176 }
177
178 $this->listen($event, $listener);
179 }
180 }
181 }
182 }
183
184 /**
185 * Resolve the subscriber instance.
186 *
187 * @param object|string $subscriber
188 * @return mixed
189 */
190 protected function resolveSubscriber($subscriber)
191 {
192 if (is_string($subscriber)) {
193 return $this->container->make($subscriber);
194 }
195
196 return $subscriber;
197 }
198
199 /**
200 * Fire an event until the first non-null response is returned.
201 *
202 * @param string|object $event
203 * @param mixed $payload
204 * @return array|null
205 */
206 public function until($event, $payload = [])
207 {
208 return $this->dispatch($event, $payload, true);
209 }
210
211 /**
212 * Fire an event and call the listeners.
213 *
214 * @param string|object $event
215 * @param mixed $payload
216 * @param bool $halt
217 * @return array|null
218 */
219 public function dispatch($event, $payload = [], $halt = false)
220 {
221 // When the given "event" is actually an object we will assume it is an event
222 // object and use the class as the event name and this event itself as the
223 // payload to the handler, which makes object based events quite simple.
224 [$event, $payload] = $this->parseEventAndPayload(
225 $event, $payload
226 );
227
228 $responses = [];
229
230 foreach ($this->getListeners($event) as $listener) {
231 $response = $listener($event, $payload);
232
233 // If a response is returned from the listener and event halting is enabled
234 // we will just return this response, and not call the rest of the event
235 // listeners. Otherwise we will add the response on the response list.
236 if ($halt && ! is_null($response)) {
237 return $response;
238 }
239
240 // If a boolean false is returned from a listener, we will stop propagating
241 // the event to any further listeners down in the chain, else we keep on
242 // looping through the listeners and firing every one in our sequence.
243 if ($response === false) {
244 break;
245 }
246
247 $responses[] = $response;
248 }
249
250 return $halt ? null : $responses;
251 }
252
253 /**
254 * Parse the given event and payload and prepare them for dispatching.
255 *
256 * @param mixed $event
257 * @param mixed $payload
258 * @return array
259 */
260 protected function parseEventAndPayload($event, $payload)
261 {
262 if (is_object($event)) {
263 [$payload, $event] = [[$event], get_class($event)];
264 }
265
266 return [$event, Arr::wrap($payload)];
267 }
268
269 /**
270 * Get all of the listeners for a given event name.
271 *
272 * @param string $eventName
273 * @return array
274 */
275 public function getListeners($eventName)
276 {
277 $listeners = $this->listeners[$eventName] ?? [];
278
279 $listeners = array_merge(
280 $listeners,
281 $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
282 );
283
284 return class_exists($eventName, false)
285 ? $this->addInterfaceListeners($eventName, $listeners)
286 : $listeners;
287 }
288
289 /**
290 * Get the wildcard listeners for the event.
291 *
292 * @param string $eventName
293 * @return array
294 */
295 protected function getWildcardListeners($eventName)
296 {
297 $wildcards = [];
298
299 foreach ($this->wildcards as $key => $listeners) {
300 if (Str::is($key, $eventName)) {
301 $wildcards = array_merge($wildcards, $listeners);
302 }
303 }
304
305 return $this->wildcardsCache[$eventName] = $wildcards;
306 }
307
308 /**
309 * Add the listeners for the event's interfaces to the given array.
310 *
311 * @param string $eventName
312 * @param array $listeners
313 * @return array
314 */
315 protected function addInterfaceListeners($eventName, array $listeners = [])
316 {
317 foreach (class_implements($eventName) as $interface) {
318 if (isset($this->listeners[$interface])) {
319 foreach ($this->listeners[$interface] as $names) {
320 $listeners = array_merge($listeners, (array) $names);
321 }
322 }
323 }
324
325 return $listeners;
326 }
327
328 /**
329 * Register an event listener with the dispatcher.
330 *
331 * @param \Closure|string|array $listener
332 * @param bool $wildcard
333 * @return \Closure
334 */
335 public function makeListener($listener, $wildcard = false)
336 {
337 if (is_string($listener)) {
338 return $this->createClassListener($listener, $wildcard);
339 }
340
341 if (is_array($listener) && isset($listener[0]) && is_string($listener[0])) {
342 return $this->createClassListener($listener, $wildcard);
343 }
344
345 return function ($event, $payload) use ($listener, $wildcard) {
346 if ($wildcard) {
347 return $listener($event, $payload);
348 }
349
350 return $listener(...array_values($payload));
351 };
352 }
353
354 /**
355 * Create a class based listener using the IoC container.
356 *
357 * @param string $listener
358 * @param bool $wildcard
359 * @return \Closure
360 */
361 public function createClassListener($listener, $wildcard = false)
362 {
363 return function ($event, $payload) use ($listener, $wildcard) {
364 if ($wildcard) {
365 return call_user_func($this->createClassCallable($listener), $event, $payload);
366 }
367
368 $callable = $this->createClassCallable($listener);
369
370 return $callable(...array_values($payload));
371 };
372 }
373
374 /**
375 * Create the class based event callable.
376 *
377 * @param array|string $listener
378 * @return callable
379 */
380 protected function createClassCallable($listener)
381 {
382 [$class, $method] = is_array($listener)
383 ? $listener
384 : $this->parseClassCallable($listener);
385
386 if (! method_exists($class, $method)) {
387 $method = '__invoke';
388 }
389
390 $listener = $this->container->make($class);
391
392 return $this->handlerShouldBeDispatchedAfterDatabaseTransactions($listener)
393 ? $this->createCallbackForListenerRunningAfterCommits($listener, $method)
394 : [$listener, $method];
395 }
396
397 /**
398 * Parse the class listener into class and method.
399 *
400 * @param string $listener
401 * @return array
402 */
403 protected function parseClassCallable($listener)
404 {
405 return Str::parseCallback($listener, 'handle');
406 }
407
408 /**
409 * Determine if the given event handler should be dispatched after all database transactions have committed.
410 *
411 * @param object|mixed $listener
412 * @return bool
413 */
414 protected function handlerShouldBeDispatchedAfterDatabaseTransactions($listener)
415 {
416 return ($listener->afterCommit ?? null) && $this->container->bound('db.transactions');
417 }
418
419 /**
420 * Create a callable for dispatching a listener after database transactions.
421 *
422 * @param mixed $listener
423 * @param string $method
424 * @return \Closure
425 */
426 protected function createCallbackForListenerRunningAfterCommits($listener, $method)
427 {
428 return function () use ($method, $listener) {
429 $payload = func_get_args();
430
431 $this->container->make('db.transactions')->addCallback(
432 function () use ($listener, $method, $payload) {
433 $listener->$method(...$payload);
434 }
435 );
436 };
437 }
438
439 /**
440 * Remove a set of listeners from the dispatcher.
441 *
442 * @param string $event
443 * @return void
444 */
445 public function forget($event)
446 {
447 if (Str::contains($event, '*')) {
448 unset($this->wildcards[$event]);
449 } else {
450 unset($this->listeners[$event]);
451 }
452
453 foreach ($this->wildcardsCache as $key => $listeners) {
454 if (Str::is($event, $key)) {
455 unset($this->wildcardsCache[$key]);
456 }
457 }
458 }
459
460 /**
461 * Forget all of the pushed listeners.
462 *
463 * @return void
464 */
465 public function forgetPushed()
466 {
467 foreach ($this->listeners as $key => $value) {
468 if (Str::endsWith($key, '_pushed')) {
469 $this->forget($key);
470 }
471 }
472 }
473 }
474