@@ -168,4 +168,96 @@ public static function getAttributesFromAny(
168168
169169 return array_merge (...$ attrs );
170170 }
171+
172+ /**
173+ * @template Member of \Reflector
174+ * @template Attr of object
175+ *
176+ * @param string|object $ref
177+ * @param class-string<Attr> $name
178+ * @param int $flags
179+ * @param class-string<Member>|\Closure|null $filter
180+ *
181+ * @return \Generator<string, array{ Member, ReflectionAttribute<Attr> }>
182+ */
183+ public static function getMembersWithAttribute (
184+ string |object $ ref ,
185+ string $ name ,
186+ int $ flags = 0 ,
187+ \Closure |string |null $ filter = null
188+ ): \Generator {
189+ $ ref = ReflectAccessor::reflect ($ ref );
190+
191+ foreach (static ::findAttributeOfMembers ($ ref , $ name , $ flags ) as $ refName => [$ memberRef , $ attr ]) {
192+ if (is_string ($ filter ) && !is_a ($ memberRef , $ filter , true )) {
193+ continue ;
194+ }
195+
196+ if ($ filter instanceof \Closure && !$ filter ($ memberRef , $ attr )) {
197+ continue ;
198+ }
199+
200+ yield $ refName => [$ memberRef , $ attr ];
201+ }
202+ }
203+
204+ /**
205+ * @template Member of \Reflector
206+ * @template Attr of object
207+ *
208+ * @param string|object $ref
209+ * @param class-string<Attr> $name
210+ * @param int $flags
211+ * @param class-string<Member>|\Closure|null $filter
212+ *
213+ * @return array{ Member, ReflectionAttribute<Attr> }|null
214+ */
215+ public static function getFirstMemberWithAttribute (
216+ string |object $ ref ,
217+ string $ name ,
218+ int $ flags = 0 ,
219+ \Closure |string |null $ filter = null
220+ ): ?array {
221+ return static ::getMembersWithAttribute ($ ref , $ name , $ flags , $ filter )->current ();
222+ }
223+
224+ /**
225+ * @param ReflectionClass $ref
226+ * @param string $name
227+ * @param int $flags
228+ *
229+ * @return \Generator<string, array{ \Reflector, ReflectionAttribute }>
230+ */
231+ private static function findAttributeOfMembers (
232+ \ReflectionClass $ ref ,
233+ string $ name ,
234+ int $ flags = 0
235+ ): \Generator {
236+ /** @var \ReflectionClassConstant $constant */
237+ foreach ($ ref ->getConstants () as $ constant ) {
238+ if ($ attr = static ::getFirstAttribute ($ constant , $ name , $ flags )) {
239+ yield $ constant ->getName () => [$ constant , $ attr ];
240+ }
241+ }
242+
243+ foreach ($ ref ->getProperties () as $ property ) {
244+ if ($ attr = static ::getFirstAttribute ($ property , $ name , $ flags )) {
245+ yield $ property ->getName () => [$ property , $ attr ];
246+ }
247+ }
248+
249+ foreach ($ ref ->getMethods () as $ method ) {
250+ if ($ attr = static ::getFirstAttribute ($ method , $ name , $ flags )) {
251+ yield $ method ->getName () => [$ method , $ attr ];
252+ }
253+ }
254+
255+ if ($ ref instanceof \ReflectionEnum) {
256+ foreach ($ ref ->getCases () as $ case ) {
257+ if ($ attr = static ::getFirstAttribute ($ case , $ name , $ flags )) {
258+ yield $ case ->getName () => [$ case , $ attr ];
259+ }
260+ }
261+ }
262+ }
171263}
0 commit comments