PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

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

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