PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

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

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