From 26086372107283aad399fb0613d1c284561398be Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 1 Aug 2023 17:37:49 +0100 Subject: [PATCH] HandlerListManager: track RegisteredListenerCache directly This change improves the performance of calling an event with 0 handlers by about 10% with no other changes. Since we have to access the list eventually anyway, we can cut out some unnecessary work by returning the handlers from the cache directly, instead of fetching the HandlerList for no reason. This also improves the performance of Event::hasHandlers() by about 40%, which is pretty significant (120 ns -> 80 ns). --- src/event/Event.php | 6 +++--- src/event/HandlerList.php | 6 ++---- src/event/HandlerListManager.php | 25 ++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/event/Event.php b/src/event/Event.php index 49652fcc6..21b8ae36a 100644 --- a/src/event/Event.php +++ b/src/event/Event.php @@ -55,11 +55,11 @@ abstract class Event{ $timings = Timings::getEventTimings($this); $timings->startTiming(); - $handlerList = HandlerListManager::global()->getListFor(get_class($this)); + $handlers = HandlerListManager::global()->getHandlersFor(static::class); ++self::$eventCallDepth; try{ - foreach($handlerList->getListenerList() as $registration){ + foreach($handlers as $registration){ $registration->callEvent($this); } }finally{ @@ -75,6 +75,6 @@ abstract class Event{ * Usage: SomeEventClass::hasHandlers() */ public static function hasHandlers() : bool{ - return count(HandlerListManager::global()->getListFor(static::class)->getListenerList()) > 0; + return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0; } } diff --git a/src/event/HandlerList.php b/src/event/HandlerList.php index 37811e959..74eedf3a4 100644 --- a/src/event/HandlerList.php +++ b/src/event/HandlerList.php @@ -33,8 +33,6 @@ class HandlerList{ /** @var RegisteredListener[][] */ private array $handlerSlots = []; - private RegisteredListenerCache $handlerCache; - /** @var RegisteredListenerCache[] */ private array $affectedHandlerCaches = []; @@ -44,9 +42,9 @@ class HandlerList{ */ public function __construct( private string $class, - private ?HandlerList $parentList + private ?HandlerList $parentList, + private RegisteredListenerCache $handlerCache = new RegisteredListenerCache() ){ - $this->handlerCache = new RegisteredListenerCache(); for($list = $this; $list !== null; $list = $list->parentList){ $list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache; } diff --git a/src/event/HandlerListManager.php b/src/event/HandlerListManager.php index ab94674cf..047632f54 100644 --- a/src/event/HandlerListManager.php +++ b/src/event/HandlerListManager.php @@ -36,6 +36,11 @@ class HandlerListManager{ /** @var HandlerList[] classname => HandlerList */ private array $allLists = []; + /** + * @var RegisteredListenerCache[] event class name => cache + * @phpstan-var array, RegisteredListenerCache> + */ + private array $handlerCaches = []; /** * Unregisters all the listeners @@ -98,7 +103,25 @@ class HandlerListManager{ } $parent = self::resolveNearestHandleableParent($class); - return $this->allLists[$event] = new HandlerList($event, $parent !== null ? $this->getListFor($parent->getName()) : null); + $cache = new RegisteredListenerCache(); + $this->handlerCaches[$event] = $cache; + return $this->allLists[$event] = new HandlerList( + $event, + parentList: $parent !== null ? $this->getListFor($parent->getName()) : null, + handlerCache: $cache + ); + } + + /** + * @phpstan-template TEvent of Event + * @phpstan-param class-string $event + * + * @return RegisteredListener[] + */ + public function getHandlersFor(string $event) : array{ + $cache = $this->handlerCaches[$event] ?? null; + //getListFor() will populate the cache for the next call + return $cache?->list ?? $this->getListFor($event)->getListenerList(); } /**