Dispatcher.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  1. <?php
  2. namespace Illuminate\Events;
  3. use Closure;
  4. use Exception;
  5. use Illuminate\Bus\UniqueLock;
  6. use Illuminate\Container\Container;
  7. use Illuminate\Contracts\Broadcasting\Factory as BroadcastFactory;
  8. use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
  9. use Illuminate\Contracts\Cache\Repository as Cache;
  10. use Illuminate\Contracts\Container\Container as ContainerContract;
  11. use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
  12. use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
  13. use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
  14. use Illuminate\Contracts\Queue\ShouldBeEncrypted;
  15. use Illuminate\Contracts\Queue\ShouldBeUnique;
  16. use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
  17. use Illuminate\Contracts\Queue\ShouldQueue;
  18. use Illuminate\Contracts\Queue\ShouldQueueAfterCommit;
  19. use Illuminate\Support\Arr;
  20. use Illuminate\Support\Collection;
  21. use Illuminate\Support\Str;
  22. use Illuminate\Support\Traits\Macroable;
  23. use Illuminate\Support\Traits\ReflectsClosures;
  24. use ReflectionClass;
  25. use function Illuminate\Support\enum_value;
  26. class Dispatcher implements DispatcherContract
  27. {
  28. use Macroable, ReflectsClosures;
  29. /**
  30. * The IoC container instance.
  31. *
  32. * @var \Illuminate\Contracts\Container\Container
  33. */
  34. protected $container;
  35. /**
  36. * The registered event listeners.
  37. *
  38. * @var array<string, callable|array|class-string|null>
  39. */
  40. protected $listeners = [];
  41. /**
  42. * The wildcard listeners.
  43. *
  44. * @var array<string, \Closure|string>
  45. */
  46. protected $wildcards = [];
  47. /**
  48. * The cached wildcard listeners.
  49. *
  50. * @var array<string, \Closure|string>
  51. */
  52. protected $wildcardsCache = [];
  53. /**
  54. * The queue resolver instance.
  55. *
  56. * @var callable(): \Illuminate\Contracts\Queue\Queue
  57. */
  58. protected $queueResolver;
  59. /**
  60. * The database transaction manager resolver instance.
  61. *
  62. * @var callable
  63. */
  64. protected $transactionManagerResolver;
  65. /**
  66. * The currently deferred events.
  67. *
  68. * @var array
  69. */
  70. protected $deferredEvents = [];
  71. /**
  72. * Indicates if events should be deferred.
  73. *
  74. * @var bool
  75. */
  76. protected $deferringEvents = false;
  77. /**
  78. * The specific events to defer (null means defer all events).
  79. *
  80. * @var string[]|null
  81. */
  82. protected $eventsToDefer = null;
  83. /**
  84. * Create a new event dispatcher instance.
  85. *
  86. * @param \Illuminate\Contracts\Container\Container|null $container
  87. */
  88. public function __construct(?ContainerContract $container = null)
  89. {
  90. $this->container = $container ?: new Container;
  91. }
  92. /**
  93. * Register an event listener with the dispatcher.
  94. *
  95. * @param \Illuminate\Events\QueuedClosure|callable|array|class-string|string $events
  96. * @param \Illuminate\Events\QueuedClosure|callable|array|class-string|null $listener
  97. * @return void
  98. */
  99. public function listen($events, $listener = null)
  100. {
  101. if ($events instanceof Closure) {
  102. return (new Collection($this->firstClosureParameterTypes($events)))
  103. ->each(function ($event) use ($events) {
  104. $this->listen($event, $events);
  105. });
  106. } elseif ($events instanceof QueuedClosure) {
  107. return (new Collection($this->firstClosureParameterTypes($events->closure)))
  108. ->each(function ($event) use ($events) {
  109. $this->listen($event, $events->resolve());
  110. });
  111. } elseif ($listener instanceof QueuedClosure) {
  112. $listener = $listener->resolve();
  113. }
  114. foreach ((array) $events as $event) {
  115. if (str_contains($event, '*')) {
  116. $this->setupWildcardListen($event, $listener);
  117. } else {
  118. $this->listeners[$event][] = $listener;
  119. }
  120. }
  121. }
  122. /**
  123. * Setup a wildcard listener callback.
  124. *
  125. * @param string $event
  126. * @param \Closure|string $listener
  127. * @return void
  128. */
  129. protected function setupWildcardListen($event, $listener)
  130. {
  131. $this->wildcards[$event][] = $listener;
  132. $this->wildcardsCache = [];
  133. }
  134. /**
  135. * Determine if a given event has listeners.
  136. *
  137. * @param string $eventName
  138. * @return bool
  139. */
  140. public function hasListeners($eventName)
  141. {
  142. return isset($this->listeners[$eventName]) ||
  143. isset($this->wildcards[$eventName]) ||
  144. $this->hasWildcardListeners($eventName);
  145. }
  146. /**
  147. * Determine if the given event has any wildcard listeners.
  148. *
  149. * @param string $eventName
  150. * @return bool
  151. */
  152. public function hasWildcardListeners($eventName)
  153. {
  154. foreach ($this->wildcards as $key => $listeners) {
  155. if (Str::is($key, $eventName)) {
  156. return true;
  157. }
  158. }
  159. return false;
  160. }
  161. /**
  162. * Register an event and payload to be fired later.
  163. *
  164. * @param string $event
  165. * @param object|array $payload
  166. * @return void
  167. */
  168. public function push($event, $payload = [])
  169. {
  170. $this->listen($event.'_pushed', function () use ($event, $payload) {
  171. $this->dispatch($event, $payload);
  172. });
  173. }
  174. /**
  175. * Flush a set of pushed events.
  176. *
  177. * @param string $event
  178. * @return void
  179. */
  180. public function flush($event)
  181. {
  182. $this->dispatch($event.'_pushed');
  183. }
  184. /**
  185. * Register an event subscriber with the dispatcher.
  186. *
  187. * @param object|string $subscriber
  188. * @return void
  189. */
  190. public function subscribe($subscriber)
  191. {
  192. $subscriber = $this->resolveSubscriber($subscriber);
  193. $events = $subscriber->subscribe($this);
  194. if (is_array($events)) {
  195. foreach ($events as $event => $listeners) {
  196. foreach (Arr::wrap($listeners) as $listener) {
  197. if (is_string($listener) && method_exists($subscriber, $listener)) {
  198. $this->listen($event, [get_class($subscriber), $listener]);
  199. continue;
  200. }
  201. $this->listen($event, $listener);
  202. }
  203. }
  204. }
  205. }
  206. /**
  207. * Resolve the subscriber instance.
  208. *
  209. * @param object|class-string $subscriber
  210. * @return ($subscriber is object ? object : mixed)
  211. */
  212. protected function resolveSubscriber($subscriber)
  213. {
  214. if (is_string($subscriber)) {
  215. return $this->container->make($subscriber);
  216. }
  217. return $subscriber;
  218. }
  219. /**
  220. * Fire an event until the first non-null response is returned.
  221. *
  222. * @param string|object $event
  223. * @param mixed $payload
  224. * @return array|null
  225. */
  226. public function until($event, $payload = [])
  227. {
  228. return $this->dispatch($event, $payload, true);
  229. }
  230. /**
  231. * Fire an event and call the listeners.
  232. *
  233. * @param string|object $event
  234. * @param mixed $payload
  235. * @param bool $halt
  236. * @return array|null
  237. */
  238. public function dispatch($event, $payload = [], $halt = false)
  239. {
  240. // When the given "event" is actually an object, we will assume it is an event
  241. // object, and use the class as the event name and this event itself as the
  242. // payload to the handler, which makes object-based events quite simple.
  243. [$isEventObject, $parsedEvent, $parsedPayload] = [
  244. is_object($event),
  245. ...$this->parseEventAndPayload($event, $payload),
  246. ];
  247. if ($this->shouldDeferEvent($parsedEvent)) {
  248. $this->deferredEvents[] = func_get_args();
  249. return null;
  250. }
  251. // If the event is not intended to be dispatched unless the current database
  252. // transaction is successful, we'll register a callback which will handle
  253. // dispatching this event on the next successful DB transaction commit.
  254. if ($isEventObject &&
  255. $parsedPayload[0] instanceof ShouldDispatchAfterCommit &&
  256. ! is_null($transactions = $this->resolveTransactionManager())) {
  257. $transactions->addCallback(
  258. fn () => $this->invokeListeners($parsedEvent, $parsedPayload, $halt)
  259. );
  260. return null;
  261. }
  262. return $this->invokeListeners($parsedEvent, $parsedPayload, $halt);
  263. }
  264. /**
  265. * Broadcast an event and call its listeners.
  266. *
  267. * @param string|object $event
  268. * @param mixed $payload
  269. * @param bool $halt
  270. * @return array|null
  271. */
  272. protected function invokeListeners($event, $payload, $halt = false)
  273. {
  274. if ($this->shouldBroadcast($payload)) {
  275. $this->broadcastEvent($payload[0]);
  276. }
  277. $responses = [];
  278. foreach ($this->getListeners($event) as $listener) {
  279. $response = $listener($event, $payload);
  280. // If a response is returned from the listener and event halting is enabled
  281. // we will just return this response, and not call the rest of the event
  282. // listeners. Otherwise we will add the response on the response list.
  283. if ($halt && ! is_null($response)) {
  284. return $response;
  285. }
  286. // If a boolean false is returned from a listener, we will stop propagating
  287. // the event to any further listeners down in the chain, else we keep on
  288. // looping through the listeners and firing every one in our sequence.
  289. if ($response === false) {
  290. break;
  291. }
  292. $responses[] = $response;
  293. }
  294. return $halt ? null : $responses;
  295. }
  296. /**
  297. * Parse the given event and payload and prepare them for dispatching.
  298. *
  299. * @param mixed $event
  300. * @param mixed $payload
  301. * @return array{string, array}
  302. */
  303. protected function parseEventAndPayload($event, $payload)
  304. {
  305. if (is_object($event)) {
  306. [$payload, $event] = [[$event], get_class($event)];
  307. }
  308. return [$event, Arr::wrap($payload)];
  309. }
  310. /**
  311. * Determine if the payload has a broadcastable event.
  312. *
  313. * @param array $payload
  314. * @return bool
  315. */
  316. protected function shouldBroadcast(array $payload)
  317. {
  318. return isset($payload[0]) &&
  319. $payload[0] instanceof ShouldBroadcast &&
  320. $this->broadcastWhen($payload[0]);
  321. }
  322. /**
  323. * Check if the event should be broadcasted by the condition.
  324. *
  325. * @param mixed $event
  326. * @return bool
  327. */
  328. protected function broadcastWhen($event)
  329. {
  330. return method_exists($event, 'broadcastWhen')
  331. ? $event->broadcastWhen()
  332. : true;
  333. }
  334. /**
  335. * Broadcast the given event class.
  336. *
  337. * @param \Illuminate\Contracts\Broadcasting\ShouldBroadcast $event
  338. * @return void
  339. */
  340. protected function broadcastEvent($event)
  341. {
  342. $this->container->make(BroadcastFactory::class)->queue($event);
  343. }
  344. /**
  345. * Get all of the listeners for a given event name.
  346. *
  347. * @param string $eventName
  348. * @return array
  349. */
  350. public function getListeners($eventName)
  351. {
  352. $listeners = array_merge(
  353. $this->prepareListeners($eventName),
  354. $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
  355. );
  356. return class_exists($eventName, false)
  357. ? $this->addInterfaceListeners($eventName, $listeners)
  358. : $listeners;
  359. }
  360. /**
  361. * Get the wildcard listeners for the event.
  362. *
  363. * @param string $eventName
  364. * @return array
  365. */
  366. protected function getWildcardListeners($eventName)
  367. {
  368. $wildcards = [];
  369. foreach ($this->wildcards as $key => $listeners) {
  370. if (Str::is($key, $eventName)) {
  371. foreach ($listeners as $listener) {
  372. $wildcards[] = $this->makeListener($listener, true);
  373. }
  374. }
  375. }
  376. return $this->wildcardsCache[$eventName] = $wildcards;
  377. }
  378. /**
  379. * Add the listeners for the event's interfaces to the given array.
  380. *
  381. * @param string $eventName
  382. * @param array $listeners
  383. * @return array
  384. */
  385. protected function addInterfaceListeners($eventName, array $listeners = [])
  386. {
  387. foreach (class_implements($eventName) as $interface) {
  388. if (isset($this->listeners[$interface])) {
  389. foreach ($this->prepareListeners($interface) as $names) {
  390. $listeners = array_merge($listeners, (array) $names);
  391. }
  392. }
  393. }
  394. return $listeners;
  395. }
  396. /**
  397. * Prepare the listeners for a given event.
  398. *
  399. * @param string $eventName
  400. * @return \Closure[]
  401. */
  402. protected function prepareListeners(string $eventName)
  403. {
  404. $listeners = [];
  405. foreach ($this->listeners[$eventName] ?? [] as $listener) {
  406. $listeners[] = $this->makeListener($listener);
  407. }
  408. return $listeners;
  409. }
  410. /**
  411. * Register an event listener with the dispatcher.
  412. *
  413. * @param \Closure|string|array{class-string, string} $listener
  414. * @param bool $wildcard
  415. * @return \Closure
  416. */
  417. public function makeListener($listener, $wildcard = false)
  418. {
  419. if (is_string($listener)) {
  420. return $this->createClassListener($listener, $wildcard);
  421. }
  422. if (is_array($listener) && isset($listener[0]) && is_string($listener[0])) {
  423. return $this->createClassListener($listener, $wildcard);
  424. }
  425. return function ($event, $payload) use ($listener, $wildcard) {
  426. if ($wildcard) {
  427. return $listener($event, $payload);
  428. }
  429. return $listener(...array_values($payload));
  430. };
  431. }
  432. /**
  433. * Create a class based listener using the IoC container.
  434. *
  435. * @param string $listener
  436. * @param bool $wildcard
  437. * @return \Closure
  438. */
  439. public function createClassListener($listener, $wildcard = false)
  440. {
  441. return function ($event, $payload) use ($listener, $wildcard) {
  442. if ($wildcard) {
  443. return call_user_func($this->createClassCallable($listener), $event, $payload);
  444. }
  445. $callable = $this->createClassCallable($listener);
  446. return $callable(...array_values($payload));
  447. };
  448. }
  449. /**
  450. * Create the class based event callable.
  451. *
  452. * @param array{class-string, string}|string $listener
  453. * @return callable
  454. */
  455. protected function createClassCallable($listener)
  456. {
  457. [$class, $method] = is_array($listener)
  458. ? $listener
  459. : $this->parseClassCallable($listener);
  460. if (! method_exists($class, $method)) {
  461. $method = '__invoke';
  462. }
  463. if ($this->handlerShouldBeQueued($class)) {
  464. return $this->createQueuedHandlerCallable($class, $method);
  465. }
  466. $listener = $this->container->make($class);
  467. return $this->handlerShouldBeDispatchedAfterDatabaseTransactions($listener)
  468. && ! in_array($method, ['creating', 'updating', 'saving', 'deleting', 'restoring', 'forceDeleting'])
  469. ? $this->createCallbackForListenerRunningAfterCommits($listener, $method)
  470. : [$listener, $method];
  471. }
  472. /**
  473. * Parse the class listener into class and method.
  474. *
  475. * @param string $listener
  476. * @return array{class-string, string}
  477. */
  478. protected function parseClassCallable($listener)
  479. {
  480. return Str::parseCallback($listener, 'handle');
  481. }
  482. /**
  483. * Determine if the event handler class should be queued.
  484. *
  485. * @param class-string $class
  486. * @return bool
  487. *
  488. * @phpstan-assert-if-true class-string<\Illuminate\Contracts\Queue\ShouldQueue> $class
  489. */
  490. protected function handlerShouldBeQueued($class)
  491. {
  492. try {
  493. return (new ReflectionClass($class))->implementsInterface(
  494. ShouldQueue::class
  495. );
  496. } catch (Exception) {
  497. return false;
  498. }
  499. }
  500. /**
  501. * Create a callable for putting an event handler on the queue.
  502. *
  503. * @param class-string $class
  504. * @param string $method
  505. * @return \Closure(): void
  506. */
  507. protected function createQueuedHandlerCallable($class, $method)
  508. {
  509. return function () use ($class, $method) {
  510. $arguments = array_map(function ($a) {
  511. return is_object($a) ? clone $a : $a;
  512. }, func_get_args());
  513. if ($this->handlerWantsToBeQueued($class, $arguments)) {
  514. $this->queueHandler($class, $method, $arguments);
  515. }
  516. };
  517. }
  518. /**
  519. * Determine if the given event handler should be dispatched after all database transactions have committed.
  520. *
  521. * @param mixed $listener
  522. * @return bool
  523. */
  524. protected function handlerShouldBeDispatchedAfterDatabaseTransactions($listener)
  525. {
  526. return (($listener->afterCommit ?? null) ||
  527. $listener instanceof ShouldHandleEventsAfterCommit) &&
  528. $this->resolveTransactionManager();
  529. }
  530. /**
  531. * Create a callable for dispatching a listener after database transactions.
  532. *
  533. * @param mixed $listener
  534. * @param string $method
  535. * @return \Closure
  536. */
  537. protected function createCallbackForListenerRunningAfterCommits($listener, $method)
  538. {
  539. return function () use ($method, $listener) {
  540. $payload = func_get_args();
  541. $this->resolveTransactionManager()->addCallback(
  542. function () use ($listener, $method, $payload) {
  543. $listener->$method(...$payload);
  544. }
  545. );
  546. };
  547. }
  548. /**
  549. * Determine if the event handler wants to be queued.
  550. *
  551. * @param class-string $class
  552. * @param array $arguments
  553. * @return bool
  554. */
  555. protected function handlerWantsToBeQueued($class, $arguments)
  556. {
  557. $instance = $this->container->make($class);
  558. if (method_exists($instance, 'shouldQueue')) {
  559. return $instance->shouldQueue($arguments[0]);
  560. }
  561. return true;
  562. }
  563. /**
  564. * Queue the handler class.
  565. *
  566. * @param string $class
  567. * @param string $method
  568. * @param array $arguments
  569. * @return void
  570. */
  571. protected function queueHandler($class, $method, $arguments)
  572. {
  573. [$listener, $job] = $this->createListenerAndJob($class, $method, $arguments);
  574. if ($job->shouldBeUnique &&
  575. ! (new UniqueLock($this->container->make(Cache::class)))->acquire($job)) {
  576. return;
  577. }
  578. $connection = $this->resolveQueue()->connection(method_exists($listener, 'viaConnection')
  579. ? (isset($arguments[0]) ? $listener->viaConnection($arguments[0]) : $listener->viaConnection())
  580. : $listener->connection ?? null);
  581. $queue = method_exists($listener, 'viaQueue')
  582. ? (isset($arguments[0]) ? $listener->viaQueue($arguments[0]) : $listener->viaQueue())
  583. : $listener->queue ?? null;
  584. $delay = method_exists($listener, 'withDelay')
  585. ? (isset($arguments[0]) ? $listener->withDelay($arguments[0]) : $listener->withDelay())
  586. : $listener->delay ?? null;
  587. is_null($delay)
  588. ? $connection->pushOn(enum_value($queue), $job)
  589. : $connection->laterOn(enum_value($queue), $delay, $job);
  590. }
  591. /**
  592. * Create the listener and job for a queued listener.
  593. *
  594. * @template TListener
  595. *
  596. * @param class-string<TListener> $class
  597. * @param string $method
  598. * @param array $arguments
  599. * @return array{TListener, mixed}
  600. */
  601. protected function createListenerAndJob($class, $method, $arguments)
  602. {
  603. $listener = (new ReflectionClass($class))->newInstanceWithoutConstructor();
  604. return [$listener, $this->propagateListenerOptions(
  605. $listener, new CallQueuedListener($class, $method, $arguments)
  606. )];
  607. }
  608. /**
  609. * Propagate listener options to the job.
  610. *
  611. * @param mixed $listener
  612. * @param \Illuminate\Events\CallQueuedListener $job
  613. * @return \Illuminate\Events\CallQueuedListener
  614. */
  615. protected function propagateListenerOptions($listener, $job)
  616. {
  617. return tap($job, function ($job) use ($listener) {
  618. $data = array_values($job->data);
  619. if ($listener instanceof ShouldQueueAfterCommit) {
  620. $job->afterCommit = true;
  621. } else {
  622. $job->afterCommit = property_exists($listener, 'afterCommit') ? $listener->afterCommit : null;
  623. }
  624. $job->backoff = method_exists($listener, 'backoff') ? $listener->backoff(...$data) : ($listener->backoff ?? null);
  625. $job->maxExceptions = $listener->maxExceptions ?? null;
  626. $job->retryUntil = method_exists($listener, 'retryUntil') ? $listener->retryUntil(...$data) : null;
  627. $job->shouldBeEncrypted = $listener instanceof ShouldBeEncrypted;
  628. $job->timeout = $listener->timeout ?? null;
  629. $job->failOnTimeout = $listener->failOnTimeout ?? false;
  630. $job->tries = method_exists($listener, 'tries') ? $listener->tries(...$data) : ($listener->tries ?? null);
  631. $job->messageGroup = method_exists($listener, 'messageGroup') ? $listener->messageGroup(...$data) : ($listener->messageGroup ?? null);
  632. $job->withDeduplicator(method_exists($listener, 'deduplicator')
  633. ? $listener->deduplicator(...$data)
  634. : (method_exists($listener, 'deduplicationId') ? $listener->deduplicationId(...) : null)
  635. );
  636. $job->through(array_merge(
  637. method_exists($listener, 'middleware') ? $listener->middleware(...$data) : [],
  638. $listener->middleware ?? []
  639. ));
  640. $job->shouldBeUnique = $listener instanceof ShouldBeUnique;
  641. $job->shouldBeUniqueUntilProcessing = $listener instanceof ShouldBeUniqueUntilProcessing;
  642. if ($job->shouldBeUnique) {
  643. $job->uniqueId = method_exists($listener, 'uniqueId')
  644. ? $listener->uniqueId(...$data)
  645. : ($listener->uniqueId ?? null);
  646. $job->uniqueFor = method_exists($listener, 'uniqueFor')
  647. ? $listener->uniqueFor(...$data)
  648. : ($listener->uniqueFor ?? 0);
  649. }
  650. });
  651. }
  652. /**
  653. * Remove a set of listeners from the dispatcher.
  654. *
  655. * @param string $event
  656. * @return void
  657. */
  658. public function forget($event)
  659. {
  660. if (str_contains($event, '*')) {
  661. unset($this->wildcards[$event]);
  662. } else {
  663. unset($this->listeners[$event]);
  664. }
  665. foreach ($this->wildcardsCache as $key => $listeners) {
  666. if (Str::is($event, $key)) {
  667. unset($this->wildcardsCache[$key]);
  668. }
  669. }
  670. }
  671. /**
  672. * Forget all of the pushed listeners.
  673. *
  674. * @return void
  675. */
  676. public function forgetPushed()
  677. {
  678. foreach ($this->listeners as $key => $value) {
  679. if (str_ends_with($key, '_pushed')) {
  680. $this->forget($key);
  681. }
  682. }
  683. }
  684. /**
  685. * Get the queue implementation from the resolver.
  686. *
  687. * @return \Illuminate\Contracts\Queue\Queue
  688. */
  689. protected function resolveQueue()
  690. {
  691. return call_user_func($this->queueResolver);
  692. }
  693. /**
  694. * Set the queue resolver implementation.
  695. *
  696. * @param callable(): \Illuminate\Contracts\Queue\Queue $resolver
  697. * @return $this
  698. */
  699. public function setQueueResolver(callable $resolver)
  700. {
  701. $this->queueResolver = $resolver;
  702. return $this;
  703. }
  704. /**
  705. * Get the database transaction manager implementation from the resolver.
  706. *
  707. * @return \Illuminate\Database\DatabaseTransactionsManager|null
  708. */
  709. protected function resolveTransactionManager()
  710. {
  711. return call_user_func($this->transactionManagerResolver);
  712. }
  713. /**
  714. * Set the database transaction manager resolver implementation.
  715. *
  716. * @param (callable(): (\Illuminate\Database\DatabaseTransactionsManager|null)) $resolver
  717. * @return $this
  718. */
  719. public function setTransactionManagerResolver(callable $resolver)
  720. {
  721. $this->transactionManagerResolver = $resolver;
  722. return $this;
  723. }
  724. /**
  725. * Execute the given callback while deferring events, then dispatch all deferred events.
  726. *
  727. * @template TResult
  728. *
  729. * @param callable(): TResult $callback
  730. * @param string[]|null $events
  731. * @return TResult
  732. */
  733. public function defer(callable $callback, ?array $events = null)
  734. {
  735. $wasDeferring = $this->deferringEvents;
  736. $previousDeferredEvents = $this->deferredEvents;
  737. $previousEventsToDefer = $this->eventsToDefer;
  738. $this->deferringEvents = true;
  739. $this->deferredEvents = [];
  740. $this->eventsToDefer = $events;
  741. try {
  742. $result = $callback();
  743. $this->deferringEvents = false;
  744. foreach ($this->deferredEvents as $args) {
  745. $this->dispatch(...$args);
  746. }
  747. return $result;
  748. } finally {
  749. $this->deferringEvents = $wasDeferring;
  750. $this->deferredEvents = $previousDeferredEvents;
  751. $this->eventsToDefer = $previousEventsToDefer;
  752. }
  753. }
  754. /**
  755. * Determine if the given event should be deferred.
  756. *
  757. * @param string $event
  758. * @return bool
  759. */
  760. protected function shouldDeferEvent(string $event)
  761. {
  762. return $this->deferringEvents && ($this->eventsToDefer === null || in_array($event, $this->eventsToDefer));
  763. }
  764. /**
  765. * Gets the raw, unprepared listeners.
  766. *
  767. * @return array
  768. */
  769. public function getRawListeners()
  770. {
  771. return $this->listeners;
  772. }
  773. }