Passed
Push — master ( 943f18...629e50 )
by Dmitriy
13:05 queued 10:31
created

Classifier   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Test Coverage

Coverage 98.21%

Importance

Changes 6
Bugs 1 Features 0
Metric Value
eloc 52
c 6
b 1
f 0
dl 0
loc 122
ccs 55
cts 56
cp 0.9821
rs 10
wmc 22

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A withInterface() 0 7 2
A withParent() 0 5 1
A withAttribute() 0 7 2
A scanFiles() 0 10 2
C find() 0 53 14
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Classifier;
6
7
use ReflectionAttribute;
8
use ReflectionClass;
9
use Symfony\Component\Finder\Finder;
10
11
final class Classifier
12
{
13
    /**
14
     * @var string[]
15
     */
16
    private array $interfaces = [];
17
    /**
18
     * @var string[]
19
     */
20
    private array $attributes = [];
21
    /**
22
     * @psalm-var class-string
23
     */
24
    private ?string $parent = null;
25
26 12
    public function __construct(private string $directory)
27
    {
28 12
    }
29
30
    /**
31
     * @param string|string[] $interfaces
32
     */
33 9
    public function withInterface(string|array $interfaces): self
34
    {
35 9
        $new = clone $this;
36 9
        foreach ((array) $interfaces as $interface) {
37 7
            $new->interfaces[] = $interface;
38
        }
39 9
        return $new;
40
    }
41
42
    /**
43
     * @psalm-param class-string $parent
44
     */
45 1
    public function withParent(string $parent): self
46
    {
47 1
        $new = clone $this;
48 1
        $new->parent = $parent;
49 1
        return $new;
50
    }
51
52
    /**
53
     * @param string|string[] $attributes
54
     */
55 4
    public function withAttribute(string|array $attributes): self
56
    {
57 4
        $new = clone $this;
58 4
        foreach ((array) $attributes as $attribute) {
59 2
            $new->attributes[] = $attribute;
60
        }
61 4
        return $new;
62
    }
63
64 12
    public function find(): iterable
65
    {
66 12
        $countInterfaces = count($this->interfaces);
67 12
        $countAttributes = count($this->attributes);
68
69 12
        if ($countInterfaces === 0 && $countAttributes === 0 && $this->parent === null) {
70 3
            return [];
71
        }
72
73 9
        $this->scanFiles();
74
75 9
        $classesToFind = get_declared_classes();
76
77 9
        $baseDirectory = $this->directory;
78 9
        if (DIRECTORY_SEPARATOR === '\\') {
79
            $baseDirectory = str_replace('/', '\\', $baseDirectory);
80
        }
81
82 9
        foreach ($classesToFind as $className) {
83 9
            $reflection = new ReflectionClass($className);
84 9
            $filePath = $reflection->getFileName();
85 9
            if ($filePath === false || !str_starts_with($filePath, $baseDirectory)) {
86 9
                continue;
87
            }
88
89 9
            if ($countInterfaces > 0) {
90 7
                $interfaces = $reflection->getInterfaces();
91 7
                $interfaces = array_map(fn(ReflectionClass $class) => $class->getName(), $interfaces);
92
93 7
                if (count(array_intersect($this->interfaces, $interfaces)) !== $countInterfaces) {
94 5
                    continue;
95
                }
96
            }
97
98 9
            if ($countAttributes > 0) {
99 2
                $attributes = $reflection->getAttributes();
100 2
                $attributes = array_map(
101 2
                    static fn(ReflectionAttribute $attribute) => $attribute->getName(),
102 2
                    $attributes
103 2
                );
104
105 2
                if (count(array_intersect($this->attributes, $attributes)) !== $countAttributes) {
106 2
                    continue;
107
                }
108
            }
109
110 9
            if ($this->parent !== null) {
111 1
                if (!is_subclass_of($className, $this->parent)) {
112 1
                    continue;
113
                }
114
            }
115
116 9
            yield $className;
117
        }
118
    }
119
120
    /**
121
     * @psalm-suppress UnresolvableInclude
122
     */
123 9
    private function scanFiles(): void
124
    {
125 9
        $files = (new Finder())
126 9
            ->in($this->directory)
127 9
            ->name('*.php')
128 9
            ->sortByName()
129 9
            ->files();
130
131 9
        foreach ($files as $file) {
132 9
            require_once $file;
133
        }
134
    }
135
}
136