From e2f21bc37c106e2340c79d6f2e72c51af945bbfa Mon Sep 17 00:00:00 2001 From: Divine Niiquaye Ibok Date: Mon, 11 Apr 2022 01:57:48 +0000 Subject: [PATCH] Refactored for performance --- CHANGELOG.md | 29 ++-- src/AnnotationLoader.php | 264 ++++++++++++++---------------- src/ListenerInterface.php | 13 +- src/LoaderInterface.php | 51 ------ src/Locate/Annotation.php | 61 ------- src/Locate/Class_.php | 44 ----- src/Locate/Constant.php | 35 ---- src/Locate/Function_.php | 38 ----- src/Locate/Method.php | 38 ----- src/Locate/Parameter.php | 36 ---- src/Locate/Property.php | 35 ---- tests/AnnotationLoaderTest.php | 26 +-- tests/Fixtures/SampleListener.php | 74 ++++----- 13 files changed, 196 insertions(+), 548 deletions(-) delete mode 100644 src/LoaderInterface.php delete mode 100644 src/Locate/Annotation.php delete mode 100644 src/Locate/Class_.php delete mode 100644 src/Locate/Constant.php delete mode 100644 src/Locate/Function_.php delete mode 100644 src/Locate/Method.php delete mode 100644 src/Locate/Parameter.php delete mode 100644 src/Locate/Property.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 81c9446..9acace6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,14 @@ -# Change Log -All notable changes to this project will be documented in this file. -Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) principles. - -## [Unreleased][unreleased] - -### Added - -### Changed - -### Fixed - -### Removed - -[unreleased]: https://github.com/biurad/php-annotations/compare/v0.1.0...master +CHANGELOG +========= + +1.0 +=== + +* [BC BREAK] Renamed the getAnnotation listener method to getAnnotations +* [BC BREAK] Added string list return type for the listener's getAnnotation method +* [BC BREAK] Use array instead of parsing annotation/attribute into class objects +* [BC BREAK] Changed the annotation's loader class listener method from variadic to add one at a time +* [BC BREAK] Changed the annotation's loader class build method from public to protected +* [BC BREAK] The annotation's loader load method no longer accepts null as a value +* Added alias support listener support for easier loading of listener's annotation/attribute +* Improved performance of loading annotation/attribute diff --git a/src/AnnotationLoader.php b/src/AnnotationLoader.php index 6dd7604..1cf52ea 100644 --- a/src/AnnotationLoader.php +++ b/src/AnnotationLoader.php @@ -24,13 +24,13 @@ * * @author Divine Niiquaye Ibok */ -class AnnotationLoader implements LoaderInterface +class AnnotationLoader { /** @var ReaderInterface|null */ private $reader; - /** @var mixed[] */ - private $annotations; + /** @var array */ + private $loadedAttributes = [], $loadedListeners = [], $aliases = []; /** @var array */ private $listeners = []; @@ -38,7 +38,7 @@ class AnnotationLoader implements LoaderInterface /** @var string[] */ private $resources = []; - /** @var null|callable(string[]) */ + /** @var callable(string[])|null */ private $classLoader; /** @@ -55,17 +55,22 @@ public function __construct(ReaderInterface $reader = null, callable $classLoade } /** - * {@inheritdoc} + * Attach a listener to the loader. */ - public function listener(ListenerInterface ...$listeners): void + public function listener(ListenerInterface $listener, string $alias = null): void { - foreach ($listeners as $listener) { - $this->listeners[$listener->getAnnotation()] = $listener; + $this->listeners[$name = \get_class($listener)] = $listener; + unset($this->loadedListeners[$name]); + + if (null !== $alias) { + $this->aliases[$alias] = $name; } } /** - * {@inheritdoc} + * Attache(s) the given resource(s) to the loader. + * + * @param string ...$resources type of class string, function name, file, or directory */ public function resource(string ...$resources): void { @@ -75,113 +80,106 @@ public function resource(string ...$resources): void } /** - * {@inheritdoc} + * Load annotations/attributes from the given resource(s). + * + * @param string ...$listener the name of class name for registered lister + * annotation/attribute class name or listener's aliased name */ - public function build(?string ...$annotationClass): void + public function load(string ...$listener) { - $this->annotations = $annotations = $files = []; - - if (1 === \count($annotationClass = \array_merge($annotationClass, \array_keys($this->listeners)))) { - $annotationClass = $annotationClass[0]; - } - - foreach ($this->resources as $resource) { - if (\is_dir($resource)) { - $files += $this->findFiles($resource); - } elseif (\function_exists($resource) || \class_exists($resource)) { - $annotations = \array_replace_recursive($annotations, $this->findAnnotations($resource, $annotationClass)); - } - } + $loadedAnnotation = []; - if (!empty($files)) { - foreach ($this->findClasses($files) as $class) { - $annotations = \array_replace_recursive($annotations, $this->findAnnotations($class, $annotationClass)); + if (empty($listener)) { + if (empty($this->loadedListeners)) { + foreach ($this->listeners as $name => $value) { + $this->loadedListeners[$name] = $value->load($this->build(...$value->getAnnotations())); + } } - } - foreach (\array_unique((array) $annotationClass) as $annotation) { - $loadedAnnotation = \array_filter($annotations[$annotation] ?? []); + $loadedAnnotation = $this->loadedListeners; + } else { + foreach ($listener as $name) { + $name = $this->aliases[$name] ?? $name; + $loaded = ($this->loadedListeners[$name] ?? $this->loadedAttributes[$name] ?? null); - if (isset($this->listeners[$annotation])) { - $loadedAnnotation = $this->listeners[$annotation]->load($loadedAnnotation); - } + if (null === $loaded) { + $l = $this->listeners[$name] ?? null; - $this->annotations[$annotation] = $loadedAnnotation; - } - } + if ($l instanceof ListenerInterface) { + $this->loadedListeners[$name] = $l->load($this->build(...$l->getAnnotations())); + } - /** - * {@inheritdoc} - */ - public function load(string $annotationClass = null, bool $stale = true) - { - if (!$stale || null === $this->annotations) { - $this->build($annotationClass); - } + $loaded = $this->loadedListeners[$name] ?? ($this->loadedAttributes[$name] = $this->build($name)); + } - if (isset($annotationClass, $this->annotations[$annotationClass])) { - return $this->annotations[$annotationClass] ?? null; + $loadedAnnotation[$name] = $loaded; + } } - return \array_filter($this->annotations); + return 1 === \count($loadedAnnotation) ? \current($loadedAnnotation) : $loadedAnnotation; } /** - * Finds annotations in the given resource. - * - * @param class-string|string $resource - * @param string[]|string $annotationClass + * Builds the attributes/annotations for the given class or function. * - * @return Locate\Class_[]|Locate\Function_[] + * @return array> */ - private function findAnnotations(string $resource, $annotationClass): iterable + protected function build(string ...$annotationClass): array { - if (empty($annotationClass)) { - return []; - } + $annotations = []; - if (\is_array($annotationClass)) { - $annotations = []; + foreach ($this->resources as $resource) { + $values = []; - foreach ($annotationClass as $annotation) { - $annotations = \array_replace_recursive($annotations, $this->findAnnotations($resource, $annotation)); - } + if (\is_dir($resource)) { + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($resource, \FilesystemIterator::CURRENT_AS_PATHNAME)); + $files = new \RegexIterator($iterator, '/\.php$/'); - return $annotations; - } + if (\iterator_count($files) > 0) { + $values = ($this->classLoader ?? [$this, 'findClasses'])($files); - if (\function_exists($resource)) { - $funcReflection = new \ReflectionFunction($resource); - $annotation = $this->fetchFunctionAnnotation($funcReflection, $this->getAnnotations($funcReflection, $annotationClass), $annotationClass); + foreach ($values as $class) { + $classes = $this->fetchClassAnnotation($class, $annotationClass); - goto annotation; - } + if (!empty($classes)) { + $annotations[] = $classes; + } + } + } + continue; + } - $classReflection = new \ReflectionClass($resource); + if (\function_exists($resource)) { + $values = $this->fetchFunctionAnnotation(new \ReflectionFunction($resource), $annotationClass); + } elseif (\class_exists($resource)) { + $values = $this->fetchClassAnnotation($resource, $annotationClass); + } - if ($classReflection->isAbstract()) { - return []; + if (!empty($values)) { + $annotations[] = $values; + } } - $annotation = $this->fetchAnnotations( - new Locate\Class_($this->getAnnotations($classReflection, $annotationClass), $classReflection), - \array_merge($classReflection->getMethods(), $classReflection->getProperties(), $classReflection->getConstants()), - $annotationClass - ); - - annotation: - return [$annotationClass => [$resource => $annotation]]; + return $annotations; } /** - * @param class-string $annotation + * @param array|string $annotation * - * @return iterable + * @return array */ - private function getAnnotations(\Reflector $reflection, string $annotation): iterable + private function getAnnotations(\Reflector $reflection, $annotation): array { $annotations = []; + if (\is_array($annotation)) { + foreach ($annotation as $annotationClass) { + $annotations = \array_merge($annotations, $this->getAnnotations($reflection, $annotationClass)); + } + + return $annotations; + } + if (null === $this->reader) { return \array_map(static function (\ReflectionAttribute $attribute): object { return $attribute->newInstance(); @@ -200,120 +198,104 @@ private function getAnnotations(\Reflector $reflection, string $annotation): ite $annotations = $this->reader->getParameterMetadata($reflection, $annotation); } - return $annotations instanceof \Generator ? \iterator_to_array($annotations) : $annotations; + return $annotations instanceof \Traversable ? \iterator_to_array($annotations) : $annotations; } /** - * Fetch annotations from methods, constant, property and methods parameter. + * Finds annotations in the given resource. + * + * @param class-string|string $resource + * @param array $annotationClass * - * @param \Reflector[] $reflections + * @return array */ - private function fetchAnnotations(Locate\Class_ $classAnnotation, array $reflections, string $annotationClass): ?Locate\Class_ + private function fetchClassAnnotation(string $resource, array $annotationClass): array { + $classReflection = new \ReflectionClass($resource); + + if ($classReflection->isAbstract()) { + return []; + } + $classRefCount = 0; + $constants = $properties = $methods = []; + $reflections = \array_merge($classReflection->getMethods(), $classReflection->getProperties(), $classReflection->getConstants()); foreach ($reflections as $name => $reflection) { if (\is_string($name)) { - $reflection = new \ReflectionClassConstant((string) $classAnnotation, $name); + $reflection = new \ReflectionClassConstant($classReflection->name, $name); } - $annotations = $this->getAnnotations($reflection, $annotationClass); - if ($reflection instanceof \ReflectionMethod) { - $method = $this->fetchFunctionAnnotation($reflection, $annotations, $annotationClass); + $method = $this->fetchFunctionAnnotation($reflection, $annotationClass); - if ($method instanceof Locate\Method) { - $classAnnotation->methods[] = $method; + if (!empty($method)) { + $methods[] = $method; ++$classRefCount; } continue; } - if ([] === $annotations) { + if (empty($annotations = $this->getAnnotations($reflection, $annotationClass))) { continue; } - ++$classRefCount; if ($reflection instanceof \ReflectionProperty) { - $classAnnotation->properties[] = new Locate\Property($annotations, $reflection); - - continue; - } - - if ($reflection instanceof \ReflectionClassConstant) { - $classAnnotation->constants[] = new Locate\Constant($annotations, $reflection); - - continue; + $properties[] = ['attributes' => $annotations, 'type' => $reflection]; + ++$classRefCount; + } elseif ($reflection instanceof \ReflectionClassConstant) { + $constants[] = ['attributes' => $annotations, 'type' => $reflection]; + ++$classRefCount; } } - if (0 === $classRefCount && [] === $classAnnotation->getAnnotation()) { - return null; + if (empty($class = $this->getAnnotations($classReflection, $annotationClass)) && 0 === $classRefCount) { + return []; } - return $classAnnotation; + return ['attributes' => $class] + \compact('constants', 'properties', 'methods') + ['type' => $classReflection]; } /** - * @return Locate\Method|Locate\Function_|null + * @return array */ - private function fetchFunctionAnnotation(\ReflectionFunctionAbstract $reflection, iterable $annotations, string $annotationClass) + private function fetchFunctionAnnotation(\ReflectionFunctionAbstract $reflection, array $annotationClass) { - if ($reflection instanceof \ReflectionMethod) { - $function = new Locate\Method($annotations, $reflection); - } else { - $function = new Locate\Function_($annotations, $reflection); + $parameters = []; + $annotations = $this->getAnnotations($reflection, $annotationClass); + + if (empty($annotations)) { + return []; } foreach ($reflection->getParameters() as $parameter) { $attributes = $this->getAnnotations($parameter, $annotationClass); - if ([] !== $attributes) { - $function->parameters[] = new Locate\Parameter($attributes, $parameter); + if (!empty($attributes)) { + $parameters[] = ['attributes' => $attributes, 'type' => $parameter]; } } - return ([] !== $annotations || [] !== $function->parameters) ? $function : null; + return ['attributes' => $annotations, 'parameters' => $parameters, 'type' => $reflection]; } /** * Finds classes in the given resource directory. * - * @param string[] $files + * @param \Traversable $files * - * @return string[] + * @return array>int,string> */ - private function findClasses(array $files): array + private static function findClasses(\Traversable $files): array { - if ([] === $files) { - return []; - } - - if (null !== $this->classLoader) { - return ($this->classLoader)($files); - } - $declared = \get_declared_classes(); + $classes = []; foreach ($files as $file) { require_once $file; } - return \array_diff(\get_declared_classes(), $declared); - } - - /** - * Finds files in the given resource. - * - * @return string[] - */ - private function findFiles(string $resource): array - { - $directory = new \RecursiveDirectoryIterator($resource, \FilesystemIterator::CURRENT_AS_PATHNAME); - $iterator = new \RecursiveIteratorIterator($directory); - $files = new \RegexIterator($iterator, '/\.php$/'); - - return \iterator_to_array($files); + return \array_merge($classes, \array_diff(\get_declared_classes(), $declared)); } } diff --git a/src/ListenerInterface.php b/src/ListenerInterface.php index 5d8a5ad..fe684c9 100644 --- a/src/ListenerInterface.php +++ b/src/ListenerInterface.php @@ -17,19 +17,26 @@ namespace Biurad\Annotations; +/** + * A listener class to load attributes/annotations. + * + * @author Divine Niiquaye Ibok + */ interface ListenerInterface { /** * This method utilises found annotations and return collector. * - * @param Locate\Class_[]|Locate\Function_[] $annotations + * @param array> $annotations * * @return mixed */ public function load(array $annotations); /** - * The annotation class to find. + * The annotation/attribute classes to find. + * + * @return array */ - public function getAnnotation(): string; + public function getAnnotations(): array; } diff --git a/src/LoaderInterface.php b/src/LoaderInterface.php deleted file mode 100644 index ad2aa76..0000000 --- a/src/LoaderInterface.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations; - -interface LoaderInterface -{ - /** - * Attache(s) the given resource(s) to the loader. - * - * @param string ...$resources type of class string, function name, file, or directory - */ - public function resource(string ...$resources): void; - - /** - * Attache(s) the given listener(s) to the loader. - * - * @param ListenerInterface ...$listeners - */ - public function listener(ListenerInterface ...$listeners): void; - - /** - * Loads annotations from attached resources. - * - * @param bool $stale If true rebuilding annotations/attributes is locked - * - * @return mixed - */ - public function load(string $annotationClass = null, bool $stale = true); - - /** - * This finds and build annotations once. - * - * @param string|null ...$annotationClass - */ - public function build(?string ...$annotationClass): void; -} diff --git a/src/Locate/Annotation.php b/src/Locate/Annotation.php deleted file mode 100644 index 6b3e6a3..0000000 --- a/src/Locate/Annotation.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations\Locate; - -/** - * A representation of annotated reflection type. - * - * @author Divine Niiquaye Ibok - */ -abstract class Annotation implements \Stringable -{ - /** @var iterable */ - private $annotations = []; - - /** @var \ReflectionClass|\ReflectionClassConstant|\ReflectionProperty|\ReflectionFunctionAbstract|\ReflectionParameter */ - protected $reflection; - - /** - * @param iterable $annotations - */ - public function __construct(iterable $annotations) - { - $this->annotations = $annotations; - } - - /** - * Get the reflection object used in annotation. - */ - abstract public function getReflection(); - - /** - * {@inheritdoc} - */ - public function __toString(): string - { - return $this->reflection->getName(); - } - - /** - * @return iterable - */ - public function getAnnotation(): iterable - { - return $this->annotations; - } -} diff --git a/src/Locate/Class_.php b/src/Locate/Class_.php deleted file mode 100644 index b4a338d..0000000 --- a/src/Locate/Class_.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations\Locate; - -class Class_ extends Annotation -{ - /** @var Constant[] */ - public $constants = []; - - /** @var Method[] */ - public $methods = []; - - /** @var Property[] */ - public $properties = []; - - public function __construct(iterable $annotations, \ReflectionClass $reflection) - { - $this->reflection = $reflection; - parent::__construct($annotations); - } - - /** - * {@inheritdoc} - */ - public function getReflection(): \ReflectionClass - { - return $this->reflection; - } -} diff --git a/src/Locate/Constant.php b/src/Locate/Constant.php deleted file mode 100644 index 226c087..0000000 --- a/src/Locate/Constant.php +++ /dev/null @@ -1,35 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations\Locate; - -class Constant extends Annotation -{ - public function __construct(iterable $annotations, \ReflectionClassConstant $reflection) - { - $this->reflection = $reflection; - parent::__construct($annotations); - } - - /** - * {@inheritdoc} - */ - public function getReflection(): \ReflectionClassConstant - { - return $this->reflection; - } -} diff --git a/src/Locate/Function_.php b/src/Locate/Function_.php deleted file mode 100644 index 6bf7e44..0000000 --- a/src/Locate/Function_.php +++ /dev/null @@ -1,38 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations\Locate; - -class Function_ extends Annotation -{ - /** @var Parameter[] */ - public $parameters = []; - - public function __construct(iterable $annotations, \ReflectionFunction $reflection) - { - $this->reflection = $reflection; - parent::__construct($annotations); - } - - /** - * {@inheritdoc} - */ - public function getReflection(): \ReflectionFunction - { - return $this->reflection; - } -} diff --git a/src/Locate/Method.php b/src/Locate/Method.php deleted file mode 100644 index a4d8849..0000000 --- a/src/Locate/Method.php +++ /dev/null @@ -1,38 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations\Locate; - -class Method extends Annotation -{ - /** @var Parameter[] */ - public $parameters = []; - - public function __construct(iterable $annotations, \ReflectionMethod $reflection) - { - $this->reflection = $reflection; - parent::__construct($annotations); - } - - /** - * {@inheritdoc} - */ - public function getReflection(): \ReflectionMethod - { - return $this->reflection; - } -} diff --git a/src/Locate/Parameter.php b/src/Locate/Parameter.php deleted file mode 100644 index b15f41c..0000000 --- a/src/Locate/Parameter.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations\Locate; - -class Parameter extends Annotation -{ - public function __construct(iterable $annotations, \ReflectionParameter $reflection) - { - $this->reflection = $reflection; - parent::__construct($annotations); - } - - - /** - * {@inheritdoc} - */ - public function getReflection(): \ReflectionParameter - { - return $this->reflection; - } -} diff --git a/src/Locate/Property.php b/src/Locate/Property.php deleted file mode 100644 index 4ff9406..0000000 --- a/src/Locate/Property.php +++ /dev/null @@ -1,35 +0,0 @@ - - * @copyright 2019 Biurad Group (https://biurad.com/) - * @license https://opensource.org/licenses/BSD-3-Clause License - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Biurad\Annotations\Locate; - -class Property extends Annotation -{ - public function __construct(iterable $annotations, \ReflectionProperty $reflection) - { - $this->reflection = $reflection; - parent::__construct($annotations); - } - - /** - * {@inheritdoc} - */ - public function getReflection(): \ReflectionProperty - { - return $this->reflection; - } -} diff --git a/tests/AnnotationLoaderTest.php b/tests/AnnotationLoaderTest.php index 93757be..8bde238 100644 --- a/tests/AnnotationLoaderTest.php +++ b/tests/AnnotationLoaderTest.php @@ -48,9 +48,7 @@ protected function setUp(): void public function testEmptyAnnotations(): void { $annotation = new AnnotationLoader(new AnnotationReader()); - $annotation->build(Fixtures\Sample::class); - - $this->assertEmpty($annotation->load()); + $this->assertEmpty($annotation->load(Fixtures\Sample::class)); } /** @@ -62,13 +60,13 @@ public function testAnnotationListenerWithResource($loader): void $annotation = new AnnotationLoader(new AnnotationReader(), $loader); $result = $names = []; - $annotation->listener(new Fixtures\SampleListener()); + $annotation->listener(new Fixtures\SampleListener(), 'test'); $annotation->resource(...[ __DIR__ . '/Fixtures/Annotation/Valid', 'non-existing-file.php', ]); - $this->assertCount(1, $founds = $annotation->load()); + $this->assertCount(1, $founds = [$annotation->load()]); /** @var Fixtures\SampleCollector $found */ foreach ($founds as $found) { @@ -129,7 +127,7 @@ public function testAnnotationListenerWithResource($loader): void ['handler' => \ReflectionClass::class, 'priority' => 0], ], $result); - $this->assertInstanceOf(Fixtures\SampleCollector::class, $annotation->load(Fixtures\Sample::class)); + $this->assertInstanceOf(Fixtures\SampleCollector::class, $annotation->load('test')); } /** @@ -140,6 +138,10 @@ public function testAnnotationListenerWithResource($loader): void */ public function testAnnotationLoaderWithAttribute($loader): void { + if (null === $loader) { + $this->markTestSkipped('Composer loads tests fixtures classes by default.'); + } + $resources = [ __DIR__ . '/Fixtures/Annotation/Attribute', 'Biurad\\Annotations\\Tests\\Fixtures\\Valid\\annotated_function', @@ -147,13 +149,13 @@ public function testAnnotationLoaderWithAttribute($loader): void $annotation1 = new AnnotationLoader(new AttributeReader(), $loader); $annotation2 = new AnnotationLoader(null, $loader); - $annotation1->listener(new Fixtures\SampleListener()); + $annotation1->listener(new Fixtures\SampleListener(), 'test1'); $annotation1->resource(...$resources); - $annotation2->listener(new Fixtures\SampleListener()); + $annotation2->listener(new Fixtures\SampleListener(), 'test2'); $annotation2->resource(...$resources); - $this->assertInstanceOf(Fixtures\SampleCollector::class, $collector1 = $annotation1->load(Fixtures\Sample::class)); - $this->assertInstanceOf(Fixtures\SampleCollector::class, $collector2 = $annotation1->load(Fixtures\Sample::class)); + $this->assertInstanceOf(Fixtures\SampleCollector::class, $collector1 = $annotation1->load('test1')); + $this->assertInstanceOf(Fixtures\SampleCollector::class, $collector2 = $annotation2->load('test2')); ($collected1 = $collector1->getCollected())->ksort(); ($collected2 = $collector2->getCollected())->ksort(); @@ -196,7 +198,7 @@ public function provideAnnotationLoader(): array ]; } - public function tokenClassLoader(array $files): array + public function tokenClassLoader(\Traversable $files): array { if (!\function_exists('token_get_all')) { $this->markTestSkipped('The Tokenizer extension is required for the annotation loader.'); @@ -210,7 +212,7 @@ public function tokenClassLoader(array $files): array return $classes; } - public function nodeClassLoader(array $files): array + public function nodeClassLoader(\Traversable $files): array { if (!\interface_exists(Node::class)) { $this->markTestSkipped('The PhpParser is required for the annotation loader.'); diff --git a/tests/Fixtures/SampleListener.php b/tests/Fixtures/SampleListener.php index dd045f3..670a40f 100644 --- a/tests/Fixtures/SampleListener.php +++ b/tests/Fixtures/SampleListener.php @@ -18,9 +18,6 @@ namespace Biurad\Annotations\Tests\Fixtures; use Biurad\Annotations\ListenerInterface; -use Biurad\Annotations\Locate\Annotation; -use Biurad\Annotations\Locate\Class_; -use Biurad\Annotations\Locate\Method; class SampleListener implements ListenerInterface { @@ -35,9 +32,9 @@ public function __construct(SampleCollector $collector = null) /** * {@inheritdoc} */ - public function getAnnotation(): string + public function getAnnotations(): array { - return 'Biurad\Annotations\Tests\Fixtures\Sample'; + return ['Biurad\Annotations\Tests\Fixtures\Sample']; } /** @@ -46,32 +43,30 @@ public function getAnnotation(): string public function load(array $annotations): SampleCollector { foreach ($annotations as $annotation) { - if ($annotation instanceof Class_) { - $attributes = \array_merge($annotation->methods, $annotation->properties, $annotation->constants); + $reflector = $annotation['type']; - if ([] === $attributes) { - $this->addSample(null, $annotation); + if ($reflector instanceof \ReflectionClass) { + $attributes = \array_merge($annotation['methods'], $annotation['properties'], $annotation['constants']); - continue; - } + if (!empty($attributes)) { + foreach ($attributes as $attribute) { + $this->addSample($attribute['type'], $attribute['attributes'], $annotation['attributes']); - foreach ($attributes as $attribute) { - $this->addSample($annotation->getAnnotation(), $attribute); - - if ($attribute instanceof Method) { - foreach ($attribute->parameters as $parameter) { - $this->addSample($annotation->getAnnotation(), $parameter); + if (isset($attribute['parameters'])) { + foreach ($attribute['parameters'] as $parameter) { + $this->addSample($parameter['type'], $parameter['attributes']); + } } } + continue; } + $this->addSample($reflector, $annotation['attributes']); + } elseif ($reflector instanceof \ReflectionFunction) { + $this->addSample($reflector, $annotation['attributes']); - continue; - } - - $this->addSample(null, $annotation); - - foreach ($annotation->parameters as $parameter) { - $this->addSample(null, $parameter); + foreach ($annotation['parameters'] as $parameter) { + $this->addSample($parameter['type'], $parameter['attributes']); + } } } @@ -79,29 +74,30 @@ public function load(array $annotations): SampleCollector } /** - * @param Sample[]|Sample|null $attribute + * @param array|Sample $attributes */ - private function addSample($attribute, Annotation $listener): void + private function addSample(\Reflector $reflector, array $annotations, $attribute = []): void { - if (\is_array($attribute) && [] !== $attribute) { + if (\is_array($attribute) && !empty($attribute)) { foreach ($attribute as $annotation) { - $this->addSample($annotation, $listener); + $this->addSample($reflector, $annotations, $annotation); } + } else { + foreach ($annotations as $annotated) { + if (!$annotated instanceof Sample) { + continue; + } - return; - } + $name = $annotated->getName(); + $priority = $annotated->getPriority(); - /** @var Sample $annotated */ - foreach ($listener->getAnnotation() as $annotated) { - $name = $annotated->getName(); - $priority = $annotated->getPriority(); + if ($attribute instanceof Sample) { + $name = $attribute->getName() . '_' . $name; + $priority += $attribute->getPriority(); + } - if ($attribute instanceof Sample) { - $name = $attribute->getName() . '_' . $name; - $priority += $attribute->getPriority(); + $this->collector->add($name, $priority, $reflector); } - - $this->collector->add($name, $priority, $listener->getReflection()); } } }