Passed
Push — master ( 4ef022...000630 )
by Rustam
02:22
created

Attributes::getClasses()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace RustamWin\Attributes;
6
7
use ReflectionClass;
8
use ReflectionException;
9
use RustamWin\Attributes\Dto\ResolvedAttribute;
10
use RustamWin\Attributes\Handler\AttributeHandlerInterface;
11
use RustamWin\Attributes\Instantiator\Instantiator;
12
use Spiral\Attributes\AttributeReader;
13
use Spiral\Attributes\ReaderInterface;
14
use Spiral\Tokenizer\ClassLocator;
15
use Symfony\Component\Finder\Finder;
16
17
final class Attributes
18
{
19
    private ReaderInterface $attributeReader;
20
    private AttributeHandlerInterface $attributeHandler;
21
    /**
22
     * @var array<array-key, string>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, string>.
Loading history...
23
     */
24
    private array $directories = [];
25
    /**
26
     * @var array<class-string>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string>.
Loading history...
27
     */
28
    private array $classes = [];
29
30 1
    public function __construct(
31
        AttributeHandlerInterface $attributeHandler,
32
        ?ReaderInterface $attributeReader = null
33
    ) {
34 1
        $this->attributeHandler = $attributeHandler;
35 1
        $this->attributeReader = $attributeReader ?? new AttributeReader(new Instantiator());
36
    }
37
38
    /**
39
     * @throws ReflectionException
40
     */
41 1
    public function handle(): void
42
    {
43
        /** @var ReflectionClass[] $classes */
44 1
        $classes = array_unique(array_merge($this->getClasses(), $this->loadClasses()));
45 1
        foreach ($classes as $reflectionClass) {
46 1
            $resolvedAttributes = $this->resolveAttributes($reflectionClass);
47 1
            $this->attributeHandler->handle($reflectionClass, $resolvedAttributes);
48
        }
49
    }
50
51
    /**
52
     * @psalm-param array<class-string> $classes
53
     *
54
     * @return $this
55
     */
56 1
    public function setClasses(array $classes): self
57
    {
58 1
        $this->classes = $classes;
59
60 1
        return $this;
61
    }
62
63
    /**
64
     * @psalm-param array<array-key, string> $directories
65
     *
66
     * @return $this
67
     */
68 1
    public function setDirectories(array $directories): self
69
    {
70 1
        $this->directories = $directories;
71
72 1
        return $this;
73
    }
74
75 1
    private function resolveAttributes(ReflectionClass $ref): iterable
76
    {
77
        // class attributes
78 1
        foreach ($this->attributeReader->getClassMetadata($ref) as $attribute) {
79 1
            yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $ref);
80
        }
81
        // properties
82 1
        foreach ($ref->getProperties() as $property) {
83 1
            foreach ($this->attributeReader->getPropertyMetadata($property) as $attribute) {
84 1
                yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $property);
85
            }
86
        }
87
        // constants
88 1
        foreach ($ref->getReflectionConstants() as $constant) {
89 1
            foreach ($this->attributeReader->getConstantMetadata($constant) as $attribute) {
90 1
                yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $constant);
91
            }
92
        }
93
        // methods
94 1
        foreach ($ref->getMethods() as $method) {
95 1
            foreach ($this->attributeReader->getFunctionMetadata($method) as $attribute) {
96 1
                yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $method);
97
                // parameters
98 1
                foreach ($method->getParameters() as $parameter) {
99 1
                    foreach ($this->attributeReader->getParameterMetadata($parameter) as $parameterAttribute) {
100 1
                        yield new ResolvedAttribute(attribute: $parameterAttribute, reflectionTarget: $parameter);
101
                    }
102
                }
103
            }
104
        }
105
    }
106
107
    /**
108
     * @throws ReflectionException
109
     *
110
     * @psalm-return array<array-key, ReflectionClass<object>>
111
     */
112 1
    private function getClasses(): array
113
    {
114 1
        return array_map(
115 1
            static fn (string $className) => new ReflectionClass($className),
116 1
            $this->classes
117
        );
118
    }
119
120
    /**
121
     * @return ReflectionClass[]
122
     *
123
     * @psalm-return list<ReflectionClass<object>>
124
     */
125 1
    private function loadClasses(): array
126
    {
127 1
        if ($this->directories === []) {
128
            return [];
129
        }
130 1
        $finder = Finder::create()->name('*.php')->in($this->directories);
131 1
        $classes = (new ClassLocator($finder))->getClasses();
132
133 1
        return array_values($classes);
134
    }
135
}
136