Passed
Push — master ( 6bc648...aea75c )
by Rustam
11:48
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 JetBrains\PhpStorm\Pure;
8
use ReflectionClass;
9
use ReflectionException;
10
use RustamWin\Attributes\Dto\ResolvedAttribute;
11
use RustamWin\Attributes\Handler\AttributeHandlerInterface;
12
use RustamWin\Attributes\Instantiator\Instantiator;
13
use Spiral\Attributes\AttributeReader;
14
use Spiral\Attributes\ReaderInterface;
15
use Spiral\Tokenizer\ClassLocator;
16
use Symfony\Component\Finder\Finder;
17
18
final class Attributes
19
{
20
    private ReaderInterface $attributeReader;
21
    private AttributeHandlerInterface $attributeHandler;
22
    /**
23
     * @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...
24
     */
25
    private array $directories = [];
26
    /**
27
     * @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...
28
     */
29
    private array $classes = [];
30
31 1
    #[Pure]
32
    public function __construct(
33
        AttributeHandlerInterface $attributeHandler,
34
        ?ReaderInterface $attributeReader = null
35
    ) {
36 1
        $this->attributeHandler = $attributeHandler;
37 1
        $this->attributeReader = $attributeReader ?? new AttributeReader(new Instantiator());
38
    }
39
40
    /**
41
     * @throws ReflectionException
42
     */
43 1
    public function handle(): void
44
    {
45
        /** @var ReflectionClass[] $classes */
46 1
        $classes = array_unique(array_merge($this->getClasses(), $this->loadClasses()));
47 1
        foreach ($classes as $reflectionClass) {
48 1
            $resolvedAttributes = $this->resolveAttributes($reflectionClass);
49 1
            $this->attributeHandler->handle($reflectionClass, $resolvedAttributes);
50
        }
51
    }
52
53
    /**
54
     * @psalm-param array<class-string> $classes
55
     *
56
     * @return $this
57
     */
58 1
    public function setClasses(array $classes): self
59
    {
60 1
        $this->classes = $classes;
61
62 1
        return $this;
63
    }
64
65
    /**
66
     * @psalm-param array<array-key, string> $directories
67
     *
68
     * @return $this
69
     */
70 1
    public function setDirectories(array $directories): self
71
    {
72 1
        $this->directories = $directories;
73
74 1
        return $this;
75
    }
76
77 1
    private function resolveAttributes(ReflectionClass $ref): iterable
78
    {
79
        // class attributes
80 1
        foreach ($this->attributeReader->getClassMetadata($ref) as $attribute) {
81 1
            yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $ref);
82
        }
83
        // properties
84 1
        foreach ($ref->getProperties() as $property) {
85
            foreach ($this->attributeReader->getPropertyMetadata($property) as $attribute) {
86
                yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $property);
87
            }
88
        }
89
        // constants
90 1
        foreach ($ref->getConstants() as $constant) {
91
            foreach ($this->attributeReader->getConstantMetadata($constant) as $attribute) {
92
                yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $constant);
93
            }
94
        }
95
        // methods
96 1
        foreach ($ref->getMethods() as $method) {
97 1
            foreach ($this->attributeReader->getFunctionMetadata($method) as $attribute) {
98 1
                yield new ResolvedAttribute(attribute: $attribute, reflectionTarget: $method);
99
                // parameters
100 1
                foreach ($method->getParameters() as $parameter) {
101 1
                    foreach ($this->attributeReader->getParameterMetadata($parameter) as $parameterAttribute) {
102 1
                        yield new ResolvedAttribute(attribute: $parameterAttribute, reflectionTarget: $parameter);
103
                    }
104
                }
105
            }
106
        }
107
    }
108
109
    /**
110
     * @throws ReflectionException
111
     *
112
     * @psalm-return array<array-key, ReflectionClass<object>>
113
     */
114 1
    private function getClasses(): array
115
    {
116 1
        return array_map(
117 1
            static fn (string $className) => new ReflectionClass($className),
118 1
            $this->classes
119
        );
120
    }
121
122
    /**
123
     * @return ReflectionClass[]
124
     *
125
     * @psalm-return list<ReflectionClass<object>>
126
     */
127 1
    private function loadClasses(): array
128
    {
129 1
        if ($this->directories === []) {
130
            return [];
131
        }
132 1
        $finder = Finder::create()->name('*.php')->in($this->directories);
133 1
        $classes = (new ClassLocator($finder))->getClasses();
134
135 1
        return array_values($classes);
136
    }
137
}
138