1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Yiistack\Annotated; |
||||
6 | |||||
7 | use Doctrine\Common\Annotations\AnnotationReader; |
||||
8 | use Generator; |
||||
9 | use ReflectionClass; |
||||
10 | use Spiral\Tokenizer\ClassesInterface; |
||||
11 | |||||
12 | final class AnnotationLoader |
||||
13 | { |
||||
14 | private ClassesInterface $classLocator; |
||||
15 | private AnnotationReader $reader; |
||||
16 | private array $targets = []; |
||||
17 | |||||
18 | 5 | public function __construct(ClassesInterface $classLocator, AnnotationReader $reader = null) |
|||
19 | { |
||||
20 | 5 | $this->classLocator = $classLocator; |
|||
21 | 5 | $this->reader = $reader ?? new AnnotationReader(); |
|||
22 | } |
||||
23 | |||||
24 | 3 | public function withTargets(array $targets): self |
|||
25 | { |
||||
26 | 3 | $new = clone $this; |
|||
27 | 3 | $new->targets = $targets; |
|||
28 | |||||
29 | 3 | return $new; |
|||
30 | } |
||||
31 | |||||
32 | /** |
||||
33 | * Find all classes with given annotation. |
||||
34 | * |
||||
35 | * @param string $annotation |
||||
36 | * |
||||
37 | * @psalm-suppress ArgumentTypeCoercion |
||||
38 | * @psalm-return Generator<AnnotatedClass> |
||||
39 | * |
||||
40 | * @return AnnotatedClass[]|Generator |
||||
41 | */ |
||||
42 | 1 | public function findClasses(string $annotation, ?ReflectionClass $class = null): Generator |
|||
43 | { |
||||
44 | 1 | if ($class !== null) { |
|||
45 | $annotations = $this->reader->getClassAnnotations($class); |
||||
46 | foreach ($annotations as $classAnnotation) { |
||||
47 | if ($classAnnotation instanceof $annotation) { |
||||
48 | yield new AnnotatedClass($class, $classAnnotation); |
||||
49 | } |
||||
50 | } |
||||
51 | } |
||||
52 | 1 | foreach ($this->getTargets() as $target) { |
|||
53 | 1 | $annotations = $this->reader->getClassAnnotations($target); |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
54 | 1 | foreach ($annotations as $classAnnotation) { |
|||
55 | 1 | if ($classAnnotation instanceof $annotation) { |
|||
56 | 1 | yield new AnnotatedClass($target, $classAnnotation); |
|||
0 ignored issues
–
show
$target of type ReflectionClass[] is incompatible with the type ReflectionClass expected by parameter $class of Yiistack\Annotated\AnnotatedClass::__construct() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
57 | } |
||||
58 | } |
||||
59 | } |
||||
60 | } |
||||
61 | |||||
62 | /** |
||||
63 | * Find all methods with given annotation. |
||||
64 | * |
||||
65 | * @param string $annotation |
||||
66 | * @param ReflectionClass|null $class |
||||
67 | * |
||||
68 | * @psalm-suppress ArgumentTypeCoercion |
||||
69 | * @psalm-return Generator<AnnotatedMethod> |
||||
70 | * |
||||
71 | * @return AnnotatedMethod[]|Generator |
||||
72 | */ |
||||
73 | 2 | public function findMethods(string $annotation, ?ReflectionClass $class = null): Generator |
|||
74 | { |
||||
75 | 2 | if ($class !== null) { |
|||
76 | 1 | foreach ($class->getMethods() as $method) { |
|||
77 | 1 | $annotations = $this->reader->getMethodAnnotations($method); |
|||
78 | 1 | foreach ($annotations as $methodAnnotation) { |
|||
79 | 1 | if ($methodAnnotation instanceof $annotation) { |
|||
80 | 1 | yield new AnnotatedMethod($method, $methodAnnotation); |
|||
81 | } |
||||
82 | } |
||||
83 | } |
||||
84 | 1 | return; |
|||
85 | } |
||||
86 | 1 | foreach ($this->getTargets() as $target) { |
|||
87 | 1 | foreach ($target->getMethods() as $method) { |
|||
88 | 1 | $annotations = $this->reader->getMethodAnnotations($method); |
|||
89 | 1 | foreach ($annotations as $methodAnnotation) { |
|||
90 | 1 | if ($methodAnnotation instanceof $annotation) { |
|||
91 | 1 | yield new AnnotatedMethod($method, $methodAnnotation); |
|||
92 | } |
||||
93 | } |
||||
94 | } |
||||
95 | } |
||||
96 | } |
||||
97 | |||||
98 | /** |
||||
99 | * Find all properties with given annotation. |
||||
100 | * |
||||
101 | * @param string $annotation |
||||
102 | * @param ReflectionClass|null $class |
||||
103 | * |
||||
104 | * @psalm-suppress ArgumentTypeCoercion |
||||
105 | * @psalm-return Generator<AnnotatedProperty> |
||||
106 | * |
||||
107 | * @return AnnotatedProperty[]|Generator |
||||
108 | */ |
||||
109 | 2 | public function findProperties(string $annotation, ?ReflectionClass $class = null): Generator |
|||
110 | { |
||||
111 | 2 | if ($class !== null) { |
|||
112 | 1 | foreach ($class->getProperties() as $property) { |
|||
113 | 1 | $annotations = $this->reader->getPropertyAnnotations($property); |
|||
114 | 1 | foreach ($annotations as $propertyAnnotation) { |
|||
115 | 1 | if ($propertyAnnotation instanceof $annotation) { |
|||
116 | 1 | yield new AnnotatedProperty($property, $propertyAnnotation); |
|||
117 | } |
||||
118 | } |
||||
119 | } |
||||
120 | 1 | return; |
|||
121 | } |
||||
122 | 1 | foreach ($this->getTargets() as $target) { |
|||
123 | 1 | foreach ($target->getProperties() as $property) { |
|||
124 | 1 | $annotations = $this->reader->getPropertyAnnotations($property); |
|||
125 | 1 | foreach ($annotations as $propertyAnnotation) { |
|||
126 | 1 | if ($propertyAnnotation instanceof $annotation) { |
|||
127 | 1 | yield new AnnotatedProperty($property, $propertyAnnotation); |
|||
128 | } |
||||
129 | } |
||||
130 | } |
||||
131 | } |
||||
132 | } |
||||
133 | |||||
134 | 3 | private function getTargets(): Generator |
|||
135 | { |
||||
136 | 3 | if ($this->targets === []) { |
|||
137 | yield from $this->classLocator->getClasses(); |
||||
138 | return; |
||||
139 | } |
||||
140 | |||||
141 | 3 | foreach ($this->targets as $target) { |
|||
142 | 3 | yield from $this->classLocator->getClasses($target); |
|||
143 | } |
||||
144 | } |
||||
145 | } |
||||
146 |