$class */ public function __construct( private string $class, private ?HandlerList $parentList, private RegisteredListenerCache $handlerCache = new RegisteredListenerCache() ){ for($list = $this; $list !== null; $list = $list->parentList){ $list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache; } } /** * @throws \Exception */ public function register(RegisteredListener $listener) : void{ if(isset($this->handlerSlots[$listener->getPriority()][spl_object_id($listener)])){ throw new \InvalidArgumentException("This listener is already registered to priority {$listener->getPriority()} of event {$this->class}"); } $this->handlerSlots[$listener->getPriority()][spl_object_id($listener)] = $listener; $this->invalidateAffectedCaches(); } /** * @param RegisteredListener[] $listeners */ public function registerAll(array $listeners) : void{ foreach($listeners as $listener){ $this->register($listener); } $this->invalidateAffectedCaches(); } public function unregister(RegisteredListener|Plugin|Listener $object) : void{ if($object instanceof Plugin || $object instanceof Listener){ foreach($this->handlerSlots as $priority => $list){ foreach($list as $hash => $listener){ if(($object instanceof Plugin && $listener->getPlugin() === $object) || ($object instanceof Listener && (new \ReflectionFunction($listener->getHandler()))->getClosureThis() === $object) //this doesn't even need to be a listener :D ){ unset($this->handlerSlots[$priority][$hash]); } } } }else{ unset($this->handlerSlots[$object->getPriority()][spl_object_id($object)]); } $this->invalidateAffectedCaches(); } public function clear() : void{ $this->handlerSlots = []; $this->invalidateAffectedCaches(); } /** * @return RegisteredListener[] */ public function getListenersByPriority(int $priority) : array{ return $this->handlerSlots[$priority] ?? []; } public function getParent() : ?HandlerList{ return $this->parentList; } /** * Invalidates all known caches which might be affected by this list's contents. */ private function invalidateAffectedCaches() : void{ foreach($this->affectedHandlerCaches as $cache){ $cache->list = null; } } /** * @return RegisteredListener[] * @phpstan-return list */ public function getListenerList() : array{ if($this->handlerCache->list !== null){ return $this->handlerCache->list; } $handlerLists = []; for($currentList = $this; $currentList !== null; $currentList = $currentList->parentList){ $handlerLists[] = $currentList; } $listeners = []; $asyncListeners = []; $exclusiveAsyncListeners = []; foreach($handlerLists as $currentList){ foreach($currentList->handlerSlots as $priority => $listenersToSort){ foreach($listenersToSort as $listener){ if(!$listener instanceof RegisteredAsyncListener){ $listeners[$priority][] = $listener; }elseif(!$listener->canBeCalledConcurrently()){ $asyncListeners[$priority][] = $listener; }else{ $exclusiveAsyncListeners[$priority][] = $listener; } } } } $listenersByPriority = array_merge_recursive($listeners, $asyncListeners, $exclusiveAsyncListeners); //TODO: why on earth do the priorities have higher values for lower priority? krsort($listenersByPriority, SORT_NUMERIC); return $this->handlerCache->list = array_merge(...$listenersByPriority); } }