Vous êtes connecté en tant que anonymous Se Deconnecter
vendor/illuminate/events/Dispatcher.php
4f5a31d2
 <?php
 
 namespace Illuminate\Events;
 
 use Exception;
 use ReflectionClass;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Str;
 use Illuminate\Container\Container;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
 use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
 use Illuminate\Contracts\Broadcasting\Factory as BroadcastFactory;
 use Illuminate\Contracts\Container\Container as ContainerContract;
 
 class Dispatcher implements DispatcherContract
 {
     /**
      * The IoC container instance.
      *
      * @var \Illuminate\Contracts\Container\Container
      */
     protected $container;
 
     /**
      * The registered event listeners.
      *
      * @var array
      */
     protected $listeners = [];
 
     /**
      * The wildcard listeners.
      *
      * @var array
      */
     protected $wildcards = [];
 
     /**
      * The cached wildcard listeners.
      *
      * @var array
      */
     protected $wildcardsCache = [];
 
     /**
      * The queue resolver instance.
      *
      * @var callable
      */
     protected $queueResolver;
 
     /**
      * Create a new event dispatcher instance.
      *
      * @param  \Illuminate\Contracts\Container\Container|null  $container
      * @return void
      */
     public function __construct(ContainerContract $container = null)
     {
         $this->container = $container ?: new Container;
     }
 
     /**
      * Register an event listener with the dispatcher.
      *
      * @param  string|array  $events
      * @param  mixed  $listener
      * @return void
      */
     public function listen($events, $listener)
     {
         foreach ((array) $events as $event) {
             if (Str::contains($event, '*')) {
                 $this->setupWildcardListen($event, $listener);
             } else {
                 $this->listeners[$event][] = $this->makeListener($listener);
             }
         }
     }
 
     /**
      * Setup a wildcard listener callback.
      *
      * @param  string  $event
      * @param  mixed  $listener
      * @return void
      */
     protected function setupWildcardListen($event, $listener)
     {
         $this->wildcards[$event][] = $this->makeListener($listener, true);
 
         $this->wildcardsCache = [];
     }
 
     /**
      * Determine if a given event has listeners.
      *
      * @param  string  $eventName
      * @return bool
      */
     public function hasListeners($eventName)
     {
         return isset($this->listeners[$eventName]) || isset($this->wildcards[$eventName]);
     }
 
     /**
      * Register an event and payload to be fired later.
      *
      * @param  string  $event
      * @param  array  $payload
      * @return void
      */
     public function push($event, $payload = [])
     {
         $this->listen($event.'_pushed', function () use ($event, $payload) {
             $this->dispatch($event, $payload);
         });
     }
 
     /**
      * Flush a set of pushed events.
      *
      * @param  string  $event
      * @return void
      */
     public function flush($event)
     {
         $this->dispatch($event.'_pushed');
     }
 
     /**
      * Register an event subscriber with the dispatcher.
      *
      * @param  object|string  $subscriber
      * @return void
      */
     public function subscribe($subscriber)
     {
         $subscriber = $this->resolveSubscriber($subscriber);
 
         $subscriber->subscribe($this);
     }
 
     /**
      * Resolve the subscriber instance.
      *
      * @param  object|string  $subscriber
      * @return mixed
      */
     protected function resolveSubscriber($subscriber)
     {
         if (is_string($subscriber)) {
             return $this->container->make($subscriber);
         }
 
         return $subscriber;
     }
 
     /**
      * Fire an event until the first non-null response is returned.
      *
      * @param  string|object  $event
      * @param  mixed  $payload
      * @return array|null
      */
     public function until($event, $payload = [])
     {
         return $this->dispatch($event, $payload, true);
     }
 
     /**
      * Fire an event and call the listeners.
      *
      * @param  string|object  $event
      * @param  mixed  $payload
      * @param  bool  $halt
      * @return array|null
      */
     public function dispatch($event, $payload = [], $halt = false)
     {
         // When the given "event" is actually an object we will assume it is an event
         // object and use the class as the event name and this event itself as the
         // payload to the handler, which makes object based events quite simple.
         [$event, $payload] = $this->parseEventAndPayload(
             $event, $payload
         );
 
         if ($this->shouldBroadcast($payload)) {
             $this->broadcastEvent($payload[0]);
         }
 
         $responses = [];
 
         foreach ($this->getListeners($event) as $listener) {
             $response = $listener($event, $payload);
 
             // If a response is returned from the listener and event halting is enabled
             // we will just return this response, and not call the rest of the event
             // listeners. Otherwise we will add the response on the response list.
             if ($halt && ! is_null($response)) {
                 return $response;
             }
 
             // If a boolean false is returned from a listener, we will stop propagating
             // the event to any further listeners down in the chain, else we keep on
             // looping through the listeners and firing every one in our sequence.
             if ($response === false) {
                 break;
             }
 
             $responses[] = $response;
         }
 
         return $halt ? null : $responses;
     }
 
     /**
      * Parse the given event and payload and prepare them for dispatching.
      *
      * @param  mixed  $event
      * @param  mixed  $payload
      * @return array
      */
     protected function parseEventAndPayload($event, $payload)
     {
         if (is_object($event)) {
             [$payload, $event] = [[$event], get_class($event)];
         }
 
         return [$event, Arr::wrap($payload)];
     }
 
     /**
      * Determine if the payload has a broadcastable event.
      *
      * @param  array  $payload
      * @return bool
      */
     protected function shouldBroadcast(array $payload)
     {
         return isset($payload[0]) &&
                $payload[0] instanceof ShouldBroadcast &&
                $this->broadcastWhen($payload[0]);
     }
 
     /**
      * Check if event should be broadcasted by condition.
      *
      * @param  mixed  $event
      * @return bool
      */
     protected function broadcastWhen($event)
     {
         return method_exists($event, 'broadcastWhen')
                 ? $event->broadcastWhen() : true;
     }
 
     /**
      * Broadcast the given event class.
      *
      * @param  \Illuminate\Contracts\Broadcasting\ShouldBroadcast  $event
      * @return void
      */
     protected function broadcastEvent($event)
     {
         $this->container->make(BroadcastFactory::class)->queue($event);
     }
 
     /**
      * Get all of the listeners for a given event name.
      *
      * @param  string  $eventName
      * @return array
      */
     public function getListeners($eventName)
     {
         $listeners = $this->listeners[$eventName] ?? [];
 
         $listeners = array_merge(
             $listeners,
             $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
         );
 
         return class_exists($eventName, false)
                     ? $this->addInterfaceListeners($eventName, $listeners)
                     : $listeners;
     }
 
     /**
      * Get the wildcard listeners for the event.
      *
      * @param  string  $eventName
      * @return array
      */
     protected function getWildcardListeners($eventName)
     {
         $wildcards = [];
 
         foreach ($this->wildcards as $key => $listeners) {
             if (Str::is($key, $eventName)) {
                 $wildcards = array_merge($wildcards, $listeners);
             }
         }
 
         return $this->wildcardsCache[$eventName] = $wildcards;
     }
 
     /**
      * Add the listeners for the event's interfaces to the given array.
      *
      * @param  string  $eventName
      * @param  array  $listeners
      * @return array
      */
     protected function addInterfaceListeners($eventName, array $listeners = [])
     {
         foreach (class_implements($eventName) as $interface) {
             if (isset($this->listeners[$interface])) {
                 foreach ($this->listeners[$interface] as $names) {
                     $listeners = array_merge($listeners, (array) $names);
                 }
             }
         }
 
         return $listeners;
     }
 
     /**
      * Register an event listener with the dispatcher.
      *
      * @param  \Closure|string  $listener
      * @param  bool  $wildcard
      * @return \Closure
      */
     public function makeListener($listener, $wildcard = false)
     {
         if (is_string($listener)) {
             return $this->createClassListener($listener, $wildcard);
         }
 
         return function ($event, $payload) use ($listener, $wildcard) {
             if ($wildcard) {
                 return $listener($event, $payload);
             }
 
             return $listener(...array_values($payload));
         };
     }
 
     /**
      * Create a class based listener using the IoC container.
      *
      * @param  string  $listener
      * @param  bool  $wildcard
      * @return \Closure
      */
     public function createClassListener($listener, $wildcard = false)
     {
         return function ($event, $payload) use ($listener, $wildcard) {
             if ($wildcard) {
                 return call_user_func($this->createClassCallable($listener), $event, $payload);
             }
 
             return call_user_func_array(
                 $this->createClassCallable($listener), $payload
             );
         };
     }
 
     /**
      * Create the class based event callable.
      *
      * @param  string  $listener
      * @return callable
      */
     protected function createClassCallable($listener)
     {
         [$class, $method] = $this->parseClassCallable($listener);
 
         if ($this->handlerShouldBeQueued($class)) {
             return $this->createQueuedHandlerCallable($class, $method);
         }
 
         return [$this->container->make($class), $method];
     }
 
     /**
      * Parse the class listener into class and method.
      *
      * @param  string  $listener
      * @return array
      */
     protected function parseClassCallable($listener)
     {
         return Str::parseCallback($listener, 'handle');
     }
 
     /**
      * Determine if the event handler class should be queued.
      *
      * @param  string  $class
      * @return bool
      */
     protected function handlerShouldBeQueued($class)
     {
         try {
             return (new ReflectionClass($class))->implementsInterface(
                 ShouldQueue::class
             );
         } catch (Exception $e) {
             return false;
         }
     }
 
     /**
      * Create a callable for putting an event handler on the queue.
      *
      * @param  string  $class
      * @param  string  $method
      * @return \Closure
      */
     protected function createQueuedHandlerCallable($class, $method)
     {
         return function () use ($class, $method) {
             $arguments = array_map(function ($a) {
                 return is_object($a) ? clone $a : $a;
             }, func_get_args());
 
             if ($this->handlerWantsToBeQueued($class, $arguments)) {
                 $this->queueHandler($class, $method, $arguments);
             }
         };
     }
 
     /**
      * Determine if the event handler wants to be queued.
      *
      * @param  string  $class
      * @param  array  $arguments
      * @return bool
      */
     protected function handlerWantsToBeQueued($class, $arguments)
     {
         if (method_exists($class, 'shouldQueue')) {
             return $this->container->make($class)->shouldQueue($arguments[0]);
         }
 
         return true;
     }
 
     /**
      * Queue the handler class.
      *
      * @param  string  $class
      * @param  string  $method
      * @param  array  $arguments
      * @return void
      */
     protected function queueHandler($class, $method, $arguments)
     {
         [$listener, $job] = $this->createListenerAndJob($class, $method, $arguments);
 
         $connection = $this->resolveQueue()->connection(
             $listener->connection ?? null
         );
 
         $queue = $listener->queue ?? null;
 
         isset($listener->delay)
                     ? $connection->laterOn($queue, $listener->delay, $job)
                     : $connection->pushOn($queue, $job);
     }
 
     /**
      * Create the listener and job for a queued listener.
      *
      * @param  string  $class
      * @param  string  $method
      * @param  array  $arguments
      * @return array
      */
     protected function createListenerAndJob($class, $method, $arguments)
     {
         $listener = (new ReflectionClass($class))->newInstanceWithoutConstructor();
 
         return [$listener, $this->propagateListenerOptions(
             $listener, new CallQueuedListener($class, $method, $arguments)
         )];
     }
 
     /**
      * Propagate listener options to the job.
      *
      * @param  mixed  $listener
      * @param  mixed  $job
      * @return mixed
      */
     protected function propagateListenerOptions($listener, $job)
     {
         return tap($job, function ($job) use ($listener) {
             $job->tries = $listener->tries ?? null;
             $job->timeout = $listener->timeout ?? null;
             $job->timeoutAt = method_exists($listener, 'retryUntil')
                                 ? $listener->retryUntil() : null;
         });
     }
 
     /**
      * Remove a set of listeners from the dispatcher.
      *
      * @param  string  $event
      * @return void
      */
     public function forget($event)
     {
         if (Str::contains($event, '*')) {
             unset($this->wildcards[$event]);
         } else {
             unset($this->listeners[$event]);
         }
     }
 
     /**
      * Forget all of the pushed listeners.
      *
      * @return void
      */
     public function forgetPushed()
     {
         foreach ($this->listeners as $key => $value) {
             if (Str::endsWith($key, '_pushed')) {
                 $this->forget($key);
             }
         }
     }
 
     /**
      * Get the queue implementation from the resolver.
      *
      * @return \Illuminate\Contracts\Queue\Queue
      */
     protected function resolveQueue()
     {
         return call_user_func($this->queueResolver);
     }
 
     /**
      * Set the queue resolver implementation.
      *
      * @param  callable  $resolver
      * @return $this
      */
     public function setQueueResolver(callable $resolver)
     {
         $this->queueResolver = $resolver;
 
         return $this;
     }
 }