Projekt

Obecné

Profil

Stáhnout (13.1 KB) Statistiky
| Větev: | Revize:
1
<?php
2

    
3
namespace Illuminate\Events;
4

    
5
use Exception;
6
use ReflectionClass;
7
use Illuminate\Support\Str;
8
use Illuminate\Container\Container;
9
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
10
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
11
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
12
use Illuminate\Contracts\Container\Container as ContainerContract;
13

    
14
class Dispatcher implements DispatcherContract
15
{
16
    /**
17
     * The IoC container instance.
18
     *
19
     * @var \Illuminate\Contracts\Container\Container
20
     */
21
    protected $container;
22

    
23
    /**
24
     * The registered event listeners.
25
     *
26
     * @var array
27
     */
28
    protected $listeners = [];
29

    
30
    /**
31
     * The wildcard listeners.
32
     *
33
     * @var array
34
     */
35
    protected $wildcards = [];
36

    
37
    /**
38
     * The sorted event listeners.
39
     *
40
     * @var array
41
     */
42
    protected $sorted = [];
43

    
44
    /**
45
     * The event firing stack.
46
     *
47
     * @var array
48
     */
49
    protected $firing = [];
50

    
51
    /**
52
     * The queue resolver instance.
53
     *
54
     * @var callable
55
     */
56
    protected $queueResolver;
57

    
58
    /**
59
     * Create a new event dispatcher instance.
60
     *
61
     * @param  \Illuminate\Contracts\Container\Container|null  $container
62
     * @return void
63
     */
64
    public function __construct(ContainerContract $container = null)
65
    {
66
        $this->container = $container ?: new Container;
67
    }
68

    
69
    /**
70
     * Register an event listener with the dispatcher.
71
     *
72
     * @param  string|array  $events
73
     * @param  mixed  $listener
74
     * @param  int  $priority
75
     * @return void
76
     */
77
    public function listen($events, $listener, $priority = 0)
78
    {
79
        foreach ((array) $events as $event) {
80
            if (Str::contains($event, '*')) {
81
                $this->setupWildcardListen($event, $listener);
82
            } else {
83
                $this->listeners[$event][$priority][] = $this->makeListener($listener);
84

    
85
                unset($this->sorted[$event]);
86
            }
87
        }
88
    }
89

    
90
    /**
91
     * Setup a wildcard listener callback.
92
     *
93
     * @param  string  $event
94
     * @param  mixed  $listener
95
     * @return void
96
     */
97
    protected function setupWildcardListen($event, $listener)
98
    {
99
        $this->wildcards[$event][] = $this->makeListener($listener);
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]) || isset($this->wildcards[$eventName]);
111
    }
112

    
113
    /**
114
     * Register an event and payload to be fired later.
115
     *
116
     * @param  string  $event
117
     * @param  array  $payload
118
     * @return void
119
     */
120
    public function push($event, $payload = [])
121
    {
122
        $this->listen($event.'_pushed', function () use ($event, $payload) {
123
            $this->fire($event, $payload);
124
        });
125
    }
126

    
127
    /**
128
     * Register an event subscriber with the dispatcher.
129
     *
130
     * @param  object|string  $subscriber
131
     * @return void
132
     */
133
    public function subscribe($subscriber)
134
    {
135
        $subscriber = $this->resolveSubscriber($subscriber);
136

    
137
        $subscriber->subscribe($this);
138
    }
139

    
140
    /**
141
     * Resolve the subscriber instance.
142
     *
143
     * @param  object|string  $subscriber
144
     * @return mixed
145
     */
146
    protected function resolveSubscriber($subscriber)
147
    {
148
        if (is_string($subscriber)) {
149
            return $this->container->make($subscriber);
150
        }
151

    
152
        return $subscriber;
153
    }
154

    
155
    /**
156
     * Fire an event until the first non-null response is returned.
157
     *
158
     * @param  string|object  $event
159
     * @param  array  $payload
160
     * @return mixed
161
     */
162
    public function until($event, $payload = [])
163
    {
164
        return $this->fire($event, $payload, true);
165
    }
166

    
167
    /**
168
     * Flush a set of pushed events.
169
     *
170
     * @param  string  $event
171
     * @return void
172
     */
173
    public function flush($event)
174
    {
175
        $this->fire($event.'_pushed');
176
    }
177

    
178
    /**
179
     * Get the event that is currently firing.
180
     *
181
     * @return string
182
     */
183
    public function firing()
184
    {
185
        return last($this->firing);
186
    }
187

    
188
    /**
189
     * Fire an event and call the listeners.
190
     *
191
     * @param  string|object  $event
192
     * @param  mixed  $payload
193
     * @param  bool  $halt
194
     * @return array|null
195
     */
196
    public function fire($event, $payload = [], $halt = false)
197
    {
198
        // When the given "event" is actually an object we will assume it is an event
199
        // object and use the class as the event name and this event itself as the
200
        // payload to the handler, which makes object based events quite simple.
201
        if (is_object($event)) {
202
            list($payload, $event) = [[$event], get_class($event)];
203
        }
204

    
205
        $responses = [];
206

    
207
        // If an array is not given to us as the payload, we will turn it into one so
208
        // we can easily use call_user_func_array on the listeners, passing in the
209
        // payload to each of them so that they receive each of these arguments.
210
        if (! is_array($payload)) {
211
            $payload = [$payload];
212
        }
213

    
214
        $this->firing[] = $event;
215

    
216
        if (isset($payload[0]) && $payload[0] instanceof ShouldBroadcast) {
217
            $this->broadcastEvent($payload[0]);
218
        }
219

    
220
        foreach ($this->getListeners($event) as $listener) {
221
            $response = call_user_func_array($listener, $payload);
222

    
223
            // If a response is returned from the listener and event halting is enabled
224
            // we will just return this response, and not call the rest of the event
225
            // listeners. Otherwise we will add the response on the response list.
226
            if (! is_null($response) && $halt) {
227
                array_pop($this->firing);
228

    
229
                return $response;
230
            }
231

    
232
            // If a boolean false is returned from a listener, we will stop propagating
233
            // the event to any further listeners down in the chain, else we keep on
234
            // looping through the listeners and firing every one in our sequence.
235
            if ($response === false) {
236
                break;
237
            }
238

    
239
            $responses[] = $response;
240
        }
241

    
242
        array_pop($this->firing);
243

    
244
        return $halt ? null : $responses;
245
    }
246

    
247
    /**
248
     * Broadcast the given event class.
249
     *
250
     * @param  \Illuminate\Contracts\Broadcasting\ShouldBroadcast  $event
251
     * @return void
252
     */
253
    protected function broadcastEvent($event)
254
    {
255
        if ($this->queueResolver) {
256
            $connection = $event instanceof ShouldBroadcastNow ? 'sync' : null;
257

    
258
            $queue = method_exists($event, 'onQueue') ? $event->onQueue() : null;
259

    
260
            $this->resolveQueue()->connection($connection)->pushOn($queue, 'Illuminate\Broadcasting\BroadcastEvent', [
261
                'event' => serialize(clone $event),
262
            ]);
263
        }
264
    }
265

    
266
    /**
267
     * Get all of the listeners for a given event name.
268
     *
269
     * @param  string  $eventName
270
     * @return array
271
     */
272
    public function getListeners($eventName)
273
    {
274
        $wildcards = $this->getWildcardListeners($eventName);
275

    
276
        if (! isset($this->sorted[$eventName])) {
277
            $this->sortListeners($eventName);
278
        }
279

    
280
        return array_merge($this->sorted[$eventName], $wildcards);
281
    }
282

    
283
    /**
284
     * Get the wildcard listeners for the event.
285
     *
286
     * @param  string  $eventName
287
     * @return array
288
     */
289
    protected function getWildcardListeners($eventName)
290
    {
291
        $wildcards = [];
292

    
293
        foreach ($this->wildcards as $key => $listeners) {
294
            if (Str::is($key, $eventName)) {
295
                $wildcards = array_merge($wildcards, $listeners);
296
            }
297
        }
298

    
299
        return $wildcards;
300
    }
301

    
302
    /**
303
     * Sort the listeners for a given event by priority.
304
     *
305
     * @param  string  $eventName
306
     * @return array
307
     */
308
    protected function sortListeners($eventName)
309
    {
310
        $this->sorted[$eventName] = [];
311

    
312
        // If listeners exist for the given event, we will sort them by the priority
313
        // so that we can call them in the correct order. We will cache off these
314
        // sorted event listeners so we do not have to re-sort on every events.
315
        if (isset($this->listeners[$eventName])) {
316
            krsort($this->listeners[$eventName]);
317

    
318
            $this->sorted[$eventName] = call_user_func_array(
319
                'array_merge', $this->listeners[$eventName]
320
            );
321
        }
322
    }
323

    
324
    /**
325
     * Register an event listener with the dispatcher.
326
     *
327
     * @param  mixed  $listener
328
     * @return mixed
329
     */
330
    public function makeListener($listener)
331
    {
332
        return is_string($listener) ? $this->createClassListener($listener) : $listener;
333
    }
334

    
335
    /**
336
     * Create a class based listener using the IoC container.
337
     *
338
     * @param  mixed  $listener
339
     * @return \Closure
340
     */
341
    public function createClassListener($listener)
342
    {
343
        $container = $this->container;
344

    
345
        return function () use ($listener, $container) {
346
            return call_user_func_array(
347
                $this->createClassCallable($listener, $container), func_get_args()
348
            );
349
        };
350
    }
351

    
352
    /**
353
     * Create the class based event callable.
354
     *
355
     * @param  string  $listener
356
     * @param  \Illuminate\Container\Container  $container
357
     * @return callable
358
     */
359
    protected function createClassCallable($listener, $container)
360
    {
361
        list($class, $method) = $this->parseClassCallable($listener);
362

    
363
        if ($this->handlerShouldBeQueued($class)) {
364
            return $this->createQueuedHandlerCallable($class, $method);
365
        } else {
366
            return [$container->make($class), $method];
367
        }
368
    }
369

    
370
    /**
371
     * Parse the class listener into class and method.
372
     *
373
     * @param  string  $listener
374
     * @return array
375
     */
376
    protected function parseClassCallable($listener)
377
    {
378
        $segments = explode('@', $listener);
379

    
380
        return [$segments[0], count($segments) == 2 ? $segments[1] : 'handle'];
381
    }
382

    
383
    /**
384
     * Determine if the event handler class should be queued.
385
     *
386
     * @param  string  $class
387
     * @return bool
388
     */
389
    protected function handlerShouldBeQueued($class)
390
    {
391
        try {
392
            return (new ReflectionClass($class))->implementsInterface(
393
                'Illuminate\Contracts\Queue\ShouldQueue'
394
            );
395
        } catch (Exception $e) {
396
            return false;
397
        }
398
    }
399

    
400
    /**
401
     * Create a callable for putting an event handler on the queue.
402
     *
403
     * @param  string  $class
404
     * @param  string  $method
405
     * @return \Closure
406
     */
407
    protected function createQueuedHandlerCallable($class, $method)
408
    {
409
        return function () use ($class, $method) {
410
            $arguments = $this->cloneArgumentsForQueueing(func_get_args());
411

    
412
            if (method_exists($class, 'queue')) {
413
                $this->callQueueMethodOnHandler($class, $method, $arguments);
414
            } else {
415
                $this->resolveQueue()->push('Illuminate\Events\CallQueuedHandler@call', [
416
                    'class' => $class, 'method' => $method, 'data' => serialize($arguments),
417
                ]);
418
            }
419
        };
420
    }
421

    
422
    /**
423
     * Clone the given arguments for queueing.
424
     *
425
     * @param  array  $arguments
426
     * @return array
427
     */
428
    protected function cloneArgumentsForQueueing(array $arguments)
429
    {
430
        return array_map(function ($a) {
431
            return is_object($a) ? clone $a : $a;
432
        }, $arguments);
433
    }
434

    
435
    /**
436
     * Call the queue method on the handler class.
437
     *
438
     * @param  string  $class
439
     * @param  string  $method
440
     * @param  array  $arguments
441
     * @return void
442
     */
443
    protected function callQueueMethodOnHandler($class, $method, $arguments)
444
    {
445
        $handler = (new ReflectionClass($class))->newInstanceWithoutConstructor();
446

    
447
        $handler->queue($this->resolveQueue(), 'Illuminate\Events\CallQueuedHandler@call', [
448
            'class' => $class, 'method' => $method, 'data' => serialize($arguments),
449
        ]);
450
    }
451

    
452
    /**
453
     * Remove a set of listeners from the dispatcher.
454
     *
455
     * @param  string  $event
456
     * @return void
457
     */
458
    public function forget($event)
459
    {
460
        if (Str::contains($event, '*')) {
461
            unset($this->wildcards[$event]);
462
        } else {
463
            unset($this->listeners[$event], $this->sorted[$event]);
464
        }
465
    }
466

    
467
    /**
468
     * Forget all of the pushed listeners.
469
     *
470
     * @return void
471
     */
472
    public function forgetPushed()
473
    {
474
        foreach ($this->listeners as $key => $value) {
475
            if (Str::endsWith($key, '_pushed')) {
476
                $this->forget($key);
477
            }
478
        }
479
    }
480

    
481
    /**
482
     * Get the queue implementation from the resolver.
483
     *
484
     * @return \Illuminate\Contracts\Queue\Queue
485
     */
486
    protected function resolveQueue()
487
    {
488
        return call_user_func($this->queueResolver);
489
    }
490

    
491
    /**
492
     * Set the queue resolver implementation.
493
     *
494
     * @param  callable  $resolver
495
     * @return $this
496
     */
497
    public function setQueueResolver(callable $resolver)
498
    {
499
        $this->queueResolver = $resolver;
500

    
501
        return $this;
502
    }
503
}
(2-2/4)