2626 */
2727class AnnotationLoader implements LoaderInterface
2828{
29- /** @var ReaderInterface */
29+ /** @var ReaderInterface|null */
3030 private $ reader ;
3131
3232 /** @var mixed[] */
3333 private $ annotations ;
3434
35- /** @var ListenerInterface[] */
35+ /** @var array<string, ListenerInterface> */
3636 private $ listeners = [];
3737
3838 /** @var string[] */
@@ -44,8 +44,12 @@ class AnnotationLoader implements LoaderInterface
4444 /**
4545 * @param callable $classLoader
4646 */
47- public function __construct (ReaderInterface $ reader , callable $ classLoader = null )
47+ public function __construct (ReaderInterface $ reader = null , callable $ classLoader = null )
4848 {
49+ if (\PHP_VERSION_ID < 80000 && null === $ reader ) {
50+ throw new \RuntimeException (\sprintf ('A "%s" instance to read annotations/attributes not available. ' , ReaderInterface::class));
51+ }
52+
4953 $ this ->reader = $ reader ;
5054 $ this ->classLoader = $ classLoader ;
5155 }
@@ -55,119 +59,118 @@ public function __construct(ReaderInterface $reader, callable $classLoader = nul
5559 */
5660 public function listener (ListenerInterface ...$ listeners ): void
5761 {
58- $ this ->listeners += $ listeners ;
62+ foreach ($ listeners as $ listener ) {
63+ $ this ->listeners [$ listener ->getAnnotation ()] = $ listener ;
64+ }
5965 }
6066
6167 /**
6268 * {@inheritdoc}
6369 */
6470 public function resource (string ...$ resources ): void
6571 {
66- $ this ->resources += $ resources ;
72+ foreach ($ resources as $ resource ) {
73+ $ this ->resources [] = $ resource ;
74+ }
6775 }
6876
6977 /**
7078 * {@inheritdoc}
7179 */
72- public function build (): void
80+ public function build (? string ... $ annotationClass ): void
7381 {
74- $ this ->annotations = $ annotations = $ classes = $ files = [];
82+ $ this ->annotations = $ annotations = $ files = [];
83+
84+ if (1 === \count ($ annotationClass = \array_merge ($ annotationClass , \array_keys ($ this ->listeners )))) {
85+ $ annotationClass = $ annotationClass [0 ];
86+ }
7587
7688 foreach ($ this ->resources as $ resource ) {
7789 if (\is_dir ($ resource )) {
7890 $ files += $ this ->findFiles ($ resource );
79-
80- continue ;
81- }
82-
83- if (!(\class_exists ($ resource ) || \function_exists ($ resource ))) {
84- continue ;
91+ } elseif (\function_exists ($ resource ) || \class_exists ($ resource )) {
92+ $ annotations = \array_replace_recursive ($ annotations , $ this ->findAnnotations ($ resource , $ annotationClass ));
8593 }
86-
87- $ classes [] = $ resource ;
8894 }
8995
90- $ classes += $ this -> findClasses ( $ files );
91-
92- foreach ( $ classes as $ class) {
93- $ annotations [] = $ this -> findAnnotations ( $ class );
96+ if (! empty ( $ files )) {
97+ foreach ( $ this -> findClasses ( $ files ) as $ class ) {
98+ $ annotations = \array_replace_recursive ( $ annotations , $ this -> findAnnotations ( $ class, $ annotationClass ));
99+ }
94100 }
95101
96- foreach ($ this -> listeners as $ listener ) {
97- $ listenerAnnotations = [] ;
102+ foreach (( array ) $ annotationClass as $ annotation ) {
103+ $ loadedAnnotation = \array_filter ( $ annotations [ $ annotation ] ?? []) ;
98104
99- foreach ($ annotations as $ annotation ) {
100- if (isset ($ annotation [$ listener ->getAnnotation ()])) {
101- $ listenerAnnotations [] = $ annotation [$ listener ->getAnnotation ()];
102- }
105+ if (isset ($ this ->listeners [$ annotation ])) {
106+ $ loadedAnnotation = $ this ->listeners [$ annotation ]->load ($ loadedAnnotation );
103107 }
104108
105- $ found = $ listener ->load ($ listenerAnnotations );
106-
107- if (null !== $ found ) {
108- $ this ->annotations [] = $ found ;
109- }
109+ $ this ->annotations [$ annotation ] = $ loadedAnnotation ;
110110 }
111-
112- \gc_mem_caches ();
113111 }
114112
115113 /**
116114 * {@inheritdoc}
117115 */
118- public function load (): iterable
116+ public function load (string $ annotationClass = null , bool $ stale = true )
119117 {
120- if (null === $ this ->annotations ) {
121- $ this ->build ();
118+ if (! $ stale || null === $ this ->annotations ) {
119+ $ this ->build ($ annotationClass );
122120 }
123121
124- return $ this ->annotations ;
122+ if (isset ($ annotationClass , $ this ->annotations [$ annotationClass ])) {
123+ return $ this ->annotations [$ annotationClass ] ?? null ;
124+ }
125+
126+ return \array_filter ($ this ->annotations );
125127 }
126128
127129 /**
128130 * Finds annotations in the given resource.
129131 *
130132 * @param class-string|string $resource
133+ * @param string[]|string $annotationClass
131134 *
132135 * @return Locate\Class_[]|Locate\Function_[]
133136 */
134- private function findAnnotations (string $ resource)
137+ private function findAnnotations (string $ resource, $ annotationClass ): iterable
135138 {
136- $ annotations = [];
137-
138- foreach ($ this ->listeners as $ listener ) {
139- $ annotationClass = $ listener ->getAnnotation ();
140-
141- if (\function_exists ($ resource )) {
142- $ funcReflection = new \ReflectionFunction ($ resource );
143- $ function = $ this ->fetchFunctionAnnotation ($ funcReflection , $ this ->getAnnotations ($ funcReflection , $ annotationClass ), $ annotationClass );
139+ if (empty ($ annotationClass )) {
140+ return [];
141+ }
144142
145- if (null !== $ function ) {
146- $ annotations [$ annotationClass ] = $ function ;
147- }
143+ if (\is_array ($ annotationClass )) {
144+ $ annotations = [];
148145
149- continue ;
146+ foreach ($ annotationClass as $ annotation ) {
147+ $ annotations = \array_replace_recursive ($ annotations , $ this ->findAnnotations ($ resource , $ annotation ));
150148 }
151149
152- $ classReflection = new \ReflectionClass ($ resource );
150+ return $ annotations ;
151+ }
153152
154- if ($ classReflection -> isAbstract ( )) {
155- continue ;
156- }
153+ if (\function_exists ( $ resource )) {
154+ $ funcReflection = new \ ReflectionFunction ( $ resource ) ;
155+ $ annotation = $ this -> fetchFunctionAnnotation ( $ funcReflection , $ this -> getAnnotations ( $ funcReflection , $ annotationClass ), $ annotationClass );
157156
158- $ annotation = new Locate \Class_ ($ this ->getAnnotations ($ classReflection , $ annotationClass ), $ classReflection );
157+ goto annotation;
158+ }
159159
160- // Reflections belonging to class object.
161- $ reflections = \array_merge (
162- $ classReflection ->getMethods (),
163- $ classReflection ->getProperties (),
164- $ classReflection ->getConstants ()
165- );
160+ $ classReflection = new \ReflectionClass ($ resource );
166161
167- $ annotations [$ annotationClass ] = $ this ->fetchAnnotations ($ annotation , $ reflections , $ annotationClass );
162+ if ($ classReflection ->isAbstract ()) {
163+ return [];
168164 }
169165
170- return $ annotations ;
166+ $ annotation = $ this ->fetchAnnotations (
167+ new Locate \Class_ ($ this ->getAnnotations ($ classReflection , $ annotationClass ), $ classReflection ),
168+ \array_merge ($ classReflection ->getMethods (), $ classReflection ->getProperties (), $ classReflection ->getConstants ()),
169+ $ annotationClass
170+ );
171+
172+ annotation:
173+ return [$ annotationClass => [$ resource => $ annotation ]];
171174 }
172175
173176 /**
@@ -179,29 +182,22 @@ private function getAnnotations(\Reflector $reflection, string $annotation): ite
179182 {
180183 $ annotations = [];
181184
182- switch (true ) {
183- case $ reflection instanceof \ReflectionClass:
184- $ annotations = $ this ->reader ->getClassMetadata ($ reflection , $ annotation );
185-
186- break ;
187-
188- case $ reflection instanceof \ReflectionFunctionAbstract:
189- $ annotations = $ this ->reader ->getFunctionMetadata ($ reflection , $ annotation );
190-
191- break ;
192-
193- case $ reflection instanceof \ReflectionProperty:
194- $ annotations = $ this ->reader ->getPropertyMetadata ($ reflection , $ annotation );
195-
196- break ;
197-
198- case $ reflection instanceof \ReflectionClassConstant:
199- $ annotations = $ this ->reader ->getConstantMetadata ($ reflection , $ annotation );
200-
201- break ;
185+ if (null === $ this ->reader ) {
186+ return \array_map (static function (\ReflectionAttribute $ attribute ): object {
187+ return $ attribute ->newInstance ();
188+ }, $ reflection ->getAttributes ($ annotation ));
189+ }
202190
203- case $ reflection instanceof \ReflectionParameter:
204- $ annotations = $ this ->reader ->getParameterMetadata ($ reflection , $ annotation );
191+ if ($ reflection instanceof \ReflectionClass) {
192+ $ annotations = $ this ->reader ->getClassMetadata ($ reflection , $ annotation );
193+ } elseif ($ reflection instanceof \ReflectionFunctionAbstract) {
194+ $ annotations = $ this ->reader ->getFunctionMetadata ($ reflection , $ annotation );
195+ } elseif ($ reflection instanceof \ReflectionProperty) {
196+ $ annotations = $ this ->reader ->getPropertyMetadata ($ reflection , $ annotation );
197+ } elseif ($ reflection instanceof \ReflectionClassConstant) {
198+ $ annotations = $ this ->reader ->getConstantMetadata ($ reflection , $ annotation );
199+ } elseif ($ reflection instanceof \ReflectionParameter) {
200+ $ annotations = $ this ->reader ->getParameterMetadata ($ reflection , $ annotation );
205201 }
206202
207203 return $ annotations instanceof \Generator ? \iterator_to_array ($ annotations ) : $ annotations ;
@@ -286,7 +282,7 @@ private function fetchFunctionAnnotation(\ReflectionFunctionAbstract $reflection
286282 *
287283 * @param string[] $files
288284 *
289- * @return class- string[]
285+ * @return string[]
290286 */
291287 private function findClasses (array $ files ): array
292288 {
0 commit comments