Dispatcher.php 24 KB

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