populateClassnames(); return array_map( static fn (array $h): Listener => new Listener($h[0]->eventClass, $h[0]->priority, $h[1], $h[2]), iterator_to_array($this->getAttributes(AsListener::class)), ); } /** * @return Handler[] */ public function getHandlers(): array { $this->populateClassnames(); return array_map( static fn (array $h): Handler => new Handler($h[0]->method, $h[0]->path, $h[1], $h[2]), iterator_to_array($this->getAttributes(AsHandler::class)), ); } private function populateClassnames(): void { if ($this->classNames !== []) { return; } if ($this->cached === true && file_exists(self::CACHE_FILE)) { $data = file_get_contents(self::CACHE_FILE); if ($data === false) { return; } /** @var class-string[] $result */ $result = unserialize($data); $this->classNames = $result; return; } $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($this->path), ); /** @var SplFileInfo $file */ foreach ($it as $file) { if (! str_ends_with((string) $file, '.php')) { continue; } $classesBeforeLoad = get_declared_classes(); require_once (string) $file; $classesAfterLoad = get_declared_classes(); $this->classNames = [ ...$this->classNames, ...array_diff($classesAfterLoad, $classesBeforeLoad), ]; } if ($this->cached === true) { file_put_contents(self::CACHE_FILE, serialize($this->classNames)); } } /** * @template T * @param class-string $attributeClass * @return Iterator */ private function getAttributes(string $attributeClass): Iterator { foreach ($this->classNames as $class) { $reflectionClass = new ReflectionClass($class); foreach ($reflectionClass->getMethods() as $method) { foreach ($method->getAttributes($attributeClass) as $attribute) { yield [$attribute->newInstance(), $class, $method->getName()]; } } } } }