PageRenderTime 55ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php

https://gitlab.com/Laolballs/cbtapp
PHP | 486 lines | 217 code | 60 blank | 209 comment | 14 complexity | cbcea6c7ae87048022a5975a65d5d423 MD5 | raw file
  1. <?php namespace Illuminate\Events;
  2. use Exception;
  3. use ReflectionClass;
  4. use Illuminate\Container\Container;
  5. use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
  6. use Illuminate\Contracts\Container\Container as ContainerContract;
  7. class Dispatcher implements DispatcherContract {
  8. /**
  9. * The IoC container instance.
  10. *
  11. * @var \Illuminate\Contracts\Container\Container
  12. */
  13. protected $container;
  14. /**
  15. * The registered event listeners.
  16. *
  17. * @var array
  18. */
  19. protected $listeners = array();
  20. /**
  21. * The wildcard listeners.
  22. *
  23. * @var array
  24. */
  25. protected $wildcards = array();
  26. /**
  27. * The sorted event listeners.
  28. *
  29. * @var array
  30. */
  31. protected $sorted = array();
  32. /**
  33. * The event firing stack.
  34. *
  35. * @var array
  36. */
  37. protected $firing = array();
  38. /**
  39. * The queue resolver instance.
  40. *
  41. * @var callable
  42. */
  43. protected $queueResolver;
  44. /**
  45. * Create a new event dispatcher instance.
  46. *
  47. * @param \Illuminate\Contracts\Container\Container $container
  48. * @return void
  49. */
  50. public function __construct(ContainerContract $container = null)
  51. {
  52. $this->container = $container ?: new Container;
  53. }
  54. /**
  55. * Register an event listener with the dispatcher.
  56. *
  57. * @param string|array $events
  58. * @param mixed $listener
  59. * @param int $priority
  60. * @return void
  61. */
  62. public function listen($events, $listener, $priority = 0)
  63. {
  64. foreach ((array) $events as $event)
  65. {
  66. if (str_contains($event, '*'))
  67. {
  68. $this->setupWildcardListen($event, $listener);
  69. }
  70. else
  71. {
  72. $this->listeners[$event][$priority][] = $this->makeListener($listener);
  73. unset($this->sorted[$event]);
  74. }
  75. }
  76. }
  77. /**
  78. * Setup a wildcard listener callback.
  79. *
  80. * @param string $event
  81. * @param mixed $listener
  82. * @return void
  83. */
  84. protected function setupWildcardListen($event, $listener)
  85. {
  86. $this->wildcards[$event][] = $this->makeListener($listener);
  87. }
  88. /**
  89. * Determine if a given event has listeners.
  90. *
  91. * @param string $eventName
  92. * @return bool
  93. */
  94. public function hasListeners($eventName)
  95. {
  96. return isset($this->listeners[$eventName]);
  97. }
  98. /**
  99. * Register an event and payload to be fired later.
  100. *
  101. * @param string $event
  102. * @param array $payload
  103. * @return void
  104. */
  105. public function push($event, $payload = array())
  106. {
  107. $this->listen($event.'_pushed', function() use ($event, $payload)
  108. {
  109. $this->fire($event, $payload);
  110. });
  111. }
  112. /**
  113. * Register an event subscriber with the dispatcher.
  114. *
  115. * @param string $subscriber
  116. * @return void
  117. */
  118. public function subscribe($subscriber)
  119. {
  120. $subscriber = $this->resolveSubscriber($subscriber);
  121. $subscriber->subscribe($this);
  122. }
  123. /**
  124. * Resolve the subscriber instance.
  125. *
  126. * @param mixed $subscriber
  127. * @return mixed
  128. */
  129. protected function resolveSubscriber($subscriber)
  130. {
  131. if (is_string($subscriber))
  132. {
  133. return $this->container->make($subscriber);
  134. }
  135. return $subscriber;
  136. }
  137. /**
  138. * Fire an event until the first non-null response is returned.
  139. *
  140. * @param string $event
  141. * @param array $payload
  142. * @return mixed
  143. */
  144. public function until($event, $payload = array())
  145. {
  146. return $this->fire($event, $payload, true);
  147. }
  148. /**
  149. * Flush a set of pushed events.
  150. *
  151. * @param string $event
  152. * @return void
  153. */
  154. public function flush($event)
  155. {
  156. $this->fire($event.'_pushed');
  157. }
  158. /**
  159. * Get the event that is currently firing.
  160. *
  161. * @return string
  162. */
  163. public function firing()
  164. {
  165. return last($this->firing);
  166. }
  167. /**
  168. * Fire an event and call the listeners.
  169. *
  170. * @param string|object $event
  171. * @param mixed $payload
  172. * @param bool $halt
  173. * @return array|null
  174. */
  175. public function fire($event, $payload = array(), $halt = false)
  176. {
  177. // When the given "event" is actually an object we will assume it is an event
  178. // object and use the class as the event name and this event itself as the
  179. // payload to the handler, which makes object based events quite simple.
  180. if (is_object($event))
  181. {
  182. list($payload, $event) = [[$event], get_class($event)];
  183. }
  184. $responses = array();
  185. // If an array is not given to us as the payload, we will turn it into one so
  186. // we can easily use call_user_func_array on the listeners, passing in the
  187. // payload to each of them so that they receive each of these arguments.
  188. if ( ! is_array($payload)) $payload = array($payload);
  189. $this->firing[] = $event;
  190. foreach ($this->getListeners($event) as $listener)
  191. {
  192. $response = call_user_func_array($listener, $payload);
  193. // If a response is returned from the listener and event halting is enabled
  194. // we will just return this response, and not call the rest of the event
  195. // listeners. Otherwise we will add the response on the response list.
  196. if ( ! is_null($response) && $halt)
  197. {
  198. array_pop($this->firing);
  199. return $response;
  200. }
  201. // If a boolean false is returned from a listener, we will stop propagating
  202. // the event to any further listeners down in the chain, else we keep on
  203. // looping through the listeners and firing every one in our sequence.
  204. if ($response === false) break;
  205. $responses[] = $response;
  206. }
  207. array_pop($this->firing);
  208. return $halt ? null : $responses;
  209. }
  210. /**
  211. * Get all of the listeners for a given event name.
  212. *
  213. * @param string $eventName
  214. * @return array
  215. */
  216. public function getListeners($eventName)
  217. {
  218. $wildcards = $this->getWildcardListeners($eventName);
  219. if ( ! isset($this->sorted[$eventName]))
  220. {
  221. $this->sortListeners($eventName);
  222. }
  223. return array_merge($this->sorted[$eventName], $wildcards);
  224. }
  225. /**
  226. * Get the wildcard listeners for the event.
  227. *
  228. * @param string $eventName
  229. * @return array
  230. */
  231. protected function getWildcardListeners($eventName)
  232. {
  233. $wildcards = array();
  234. foreach ($this->wildcards as $key => $listeners)
  235. {
  236. if (str_is($key, $eventName)) $wildcards = array_merge($wildcards, $listeners);
  237. }
  238. return $wildcards;
  239. }
  240. /**
  241. * Sort the listeners for a given event by priority.
  242. *
  243. * @param string $eventName
  244. * @return array
  245. */
  246. protected function sortListeners($eventName)
  247. {
  248. $this->sorted[$eventName] = array();
  249. // If listeners exist for the given event, we will sort them by the priority
  250. // so that we can call them in the correct order. We will cache off these
  251. // sorted event listeners so we do not have to re-sort on every events.
  252. if (isset($this->listeners[$eventName]))
  253. {
  254. krsort($this->listeners[$eventName]);
  255. $this->sorted[$eventName] = call_user_func_array(
  256. 'array_merge', $this->listeners[$eventName]
  257. );
  258. }
  259. }
  260. /**
  261. * Register an event listener with the dispatcher.
  262. *
  263. * @param mixed $listener
  264. * @return mixed
  265. */
  266. public function makeListener($listener)
  267. {
  268. return is_string($listener) ? $this->createClassListener($listener) : $listener;
  269. }
  270. /**
  271. * Create a class based listener using the IoC container.
  272. *
  273. * @param mixed $listener
  274. * @return \Closure
  275. */
  276. public function createClassListener($listener)
  277. {
  278. $container = $this->container;
  279. return function() use ($listener, $container)
  280. {
  281. return call_user_func_array(
  282. $this->createClassCallable($listener, $container), func_get_args()
  283. );
  284. };
  285. }
  286. /**
  287. * Create the class based event callable.
  288. *
  289. * @param string $listener
  290. * @param \Illuminate\Container\Container $container
  291. * @return callable
  292. */
  293. protected function createClassCallable($listener, $container)
  294. {
  295. list($class, $method) = $this->parseClassCallable($listener);
  296. if ($this->handlerShouldBeQueued($class))
  297. {
  298. return $this->createQueuedHandlerCallable($class, $method);
  299. }
  300. else
  301. {
  302. return array($container->make($class), $method);
  303. }
  304. }
  305. /**
  306. * Parse the class listener into class and method.
  307. *
  308. * @param string $listener
  309. * @return array
  310. */
  311. protected function parseClassCallable($listener)
  312. {
  313. $segments = explode('@', $listener);
  314. return [$segments[0], count($segments) == 2 ? $segments[1] : 'handle'];
  315. }
  316. /**
  317. * Determine if the event handler class should be queued.
  318. *
  319. * @param string $class
  320. * @return bool
  321. */
  322. protected function handlerShouldBeQueued($class)
  323. {
  324. try
  325. {
  326. return (new ReflectionClass($class))->implementsInterface(
  327. 'Illuminate\Contracts\Queue\ShouldBeQueued'
  328. );
  329. }
  330. catch (Exception $e)
  331. {
  332. return false;
  333. }
  334. }
  335. /**
  336. * Create a callable for putting an event handler on the queue.
  337. *
  338. * @param string $class
  339. * @param string $method
  340. * @return \Closure
  341. */
  342. protected function createQueuedHandlerCallable($class, $method)
  343. {
  344. return function() use ($class, $method)
  345. {
  346. $arguments = $this->cloneArgumentsForQueueing(func_get_args());
  347. if (method_exists($class, 'queue'))
  348. {
  349. $this->callQueueMethodOnHandler($class, $method, $arguments);
  350. }
  351. else
  352. {
  353. $this->resolveQueue()->push('Illuminate\Events\CallQueuedHandler@call', [
  354. 'class' => $class, 'method' => $method, 'data' => serialize($arguments),
  355. ]);
  356. }
  357. };
  358. }
  359. /**
  360. * Clone the given arguments for queueing.
  361. *
  362. * @param array $arguments
  363. * @return array
  364. */
  365. protected function cloneArgumentsForQueueing(array $arguments)
  366. {
  367. return array_map(function($a) { return is_object($a) ? clone $a : $a; }, $arguments);
  368. }
  369. /**
  370. * Call the queue method on the handler class.
  371. *
  372. * @param string $class
  373. * @param string $method
  374. * @param array $arguments
  375. * @return void
  376. */
  377. protected function callQueueMethodOnHandler($class, $method, $arguments)
  378. {
  379. $handler = (new ReflectionClass($class))->newInstanceWithoutConstructor();
  380. $handler->queue($this->resolveQueue(), 'Illuminate\Events\CallQueuedHandler@call', [
  381. 'class' => $class, 'method' => $method, 'data' => serialize($arguments),
  382. ]);
  383. }
  384. /**
  385. * Remove a set of listeners from the dispatcher.
  386. *
  387. * @param string $event
  388. * @return void
  389. */
  390. public function forget($event)
  391. {
  392. unset($this->listeners[$event], $this->sorted[$event]);
  393. }
  394. /**
  395. * Forget all of the pushed listeners.
  396. *
  397. * @return void
  398. */
  399. public function forgetPushed()
  400. {
  401. foreach ($this->listeners as $key => $value)
  402. {
  403. if (ends_with($key, '_pushed')) $this->forget($key);
  404. }
  405. }
  406. /**
  407. * Get the queue implementation from the resolver.
  408. *
  409. * @return \Illuminate\Contracts\Queue\Queue
  410. */
  411. protected function resolveQueue()
  412. {
  413. return call_user_func($this->queueResolver);
  414. }
  415. /**
  416. * Set the queue resolver implementation.
  417. *
  418. * @param callable $resolver
  419. * @return $this
  420. */
  421. public function setQueueResolver(callable $resolver)
  422. {
  423. $this->queueResolver = $resolver;
  424. return $this;
  425. }
  426. }