44
55namespace Yiisoft \Classifier ;
66
7+ use ReflectionClass ;
78use Symfony \Component \Finder \Finder ;
9+ use Yiisoft \Classifier \Filter \FilterInterface ;
810
911/**
1012 * Base implementation for {@see ClassifierInterface} with common filters.
1113 */
1214abstract class AbstractClassifier implements ClassifierInterface
1315{
1416 /**
15- * @var string[]
17+ * @var array<class- string|trait-string, ReflectionClass>
1618 */
17- protected array $ interfaces = [];
18- /**
19- * @var string[]
20- */
21- protected array $ attributes = [];
19+ protected static array $ reflectionsCache = [];
20+
2221 /**
23- * @psalm- var class-string
22+ * @var FilterInterface[]
2423 */
25- protected ? string $ parentClass = null ;
24+ private array $ filters = [] ;
2625 /**
2726 * @var string[]
2827 */
@@ -33,48 +32,25 @@ public function __construct(string $directory, string ...$directories)
3332 $ this ->directories = [$ directory , ...array_values ($ directories )];
3433 }
3534
36- /**
37- * @psalm-param class-string ...$interfaces
38- */
39- public function withInterface (string ...$ interfaces ): self
40- {
41- $ new = clone $ this ;
42- array_push ($ new ->interfaces , ...array_values ($ interfaces ));
43-
44- return $ new ;
45- }
46-
47- /**
48- * @psalm-param class-string $parentClass
49- */
50- public function withParentClass (string $ parentClass ): self
51- {
52- $ new = clone $ this ;
53- $ new ->parentClass = $ parentClass ;
54- return $ new ;
55- }
56-
57- /**
58- * @psalm-param class-string ...$attributes
59- */
60- public function withAttribute (string ...$ attributes ): self
35+ public function withFilter (FilterInterface ...$ filter ): static
6136 {
6237 $ new = clone $ this ;
63- array_push ($ new ->attributes , ...array_values ($ attributes ));
38+ array_push ($ new ->filters , ...array_values ($ filter ));
6439
6540 return $ new ;
6641 }
6742
6843 /**
69- * @psalm- return iterable<class-string>
44+ * @return iterable<class-string>
7045 */
7146 public function find (): iterable
7247 {
73- if (empty ($ this ->interfaces ) && empty ($ this ->attributes ) && $ this ->parentClass === null ) {
74- return [];
48+ foreach ($ this ->getAvailableDeclarations () as $ declaration ) {
49+ if ($ this ->skipDeclaration ($ declaration )) {
50+ continue ;
51+ }
52+ yield $ declaration ;
7553 }
76-
77- yield from $ this ->getAvailableClasses ();
7854 }
7955
8056 protected function getFiles (): Finder
@@ -87,7 +63,31 @@ protected function getFiles(): Finder
8763 }
8864
8965 /**
90- * @return iterable<class-string>
66+ * @param class-string|trait-string $declaration
67+ */
68+ private function skipDeclaration (string $ declaration ): bool
69+ {
70+ try {
71+ $ reflectionClass = self ::$ reflectionsCache [$ declaration ] ??= new ReflectionClass ($ declaration );
72+ } catch (\Throwable ) {
73+ return true ;
74+ }
75+
76+ if ($ reflectionClass ->isInternal () || $ reflectionClass ->isAnonymous ()) {
77+ return true ;
78+ }
79+
80+ foreach ($ this ->filters as $ filter ) {
81+ if (!$ filter ->match ($ reflectionClass )) {
82+ return true ;
83+ }
84+ }
85+
86+ return false ;
87+ }
88+
89+ /**
90+ * @return iterable<class-string|trait-string>
9191 */
92- abstract protected function getAvailableClasses (): iterable ;
92+ abstract protected function getAvailableDeclarations (): iterable ;
9393}
0 commit comments