, int> $delegatesCallDepth */ private static array $delegatesCallDepth = []; private const MAX_EVENT_CALL_DEPTH = 50; /** * @phpstan-return Promise */ final public function call() : Promise{ if(!isset(self::$delegatesCallDepth[$class = static::class])){ self::$delegatesCallDepth[$class] = 0; } if(self::$delegatesCallDepth[$class] >= self::MAX_EVENT_CALL_DEPTH){ //this exception will be caught by the parent event call if all else fails throw new \RuntimeException("Recursive event call detected (reached max depth of " . self::MAX_EVENT_CALL_DEPTH . " calls)"); } $timings = Timings::getAsyncEventTimings($this); $timings->startTiming(); ++self::$delegatesCallDepth[$class]; try{ /** @phpstan-var PromiseResolver $globalResolver */ $globalResolver = new PromiseResolver(); $this->processRemainingHandlers(HandlerListManager::global()->getAsyncHandlersFor(static::class), $globalResolver); return $globalResolver->getPromise(); }finally{ --self::$delegatesCallDepth[$class]; $timings->stopTiming(); } } /** * @param AsyncRegisteredListener[] $handlers * @phpstan-param PromiseResolver $globalResolver */ private function processRemainingHandlers(array $handlers, PromiseResolver $globalResolver) : void{ $currentPriority = null; $awaitPromises = []; foreach($handlers as $k => $handler){ $priority = $handler->getPriority(); if(count($awaitPromises) > 0 && $currentPriority !== null && $currentPriority !== $priority){ //wait for concurrent promises from previous priority to complete break; } $currentPriority = $priority; if($handler->canBeCalledConcurrently()){ unset($handlers[$k]); $promise = $handler->callAsync($this); if($promise !== null){ $awaitPromises[] = $promise; } }else{ if(count($awaitPromises) > 0){ //wait for concurrent promises to complete break; } unset($handlers[$k]); $promise = $handler->callAsync($this); if($promise !== null){ $promise->onCompletion( onSuccess: fn() => $this->processRemainingHandlers($handlers, $globalResolver), onFailure: $globalResolver->reject(...) ); return; } } } if(count($awaitPromises) > 0){ Promise::all($awaitPromises)->onCompletion( onSuccess: fn() => $this->processRemainingHandlers($handlers, $globalResolver), onFailure: $globalResolver->reject(...) ); }else{ $globalResolver->resolve($this); } } }