Test Failed
Pull Request — master (#878)
by butschster
17:28 queued 07:56
created

TargetAttribute::getCacheKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Tokenizer\Attribute;
6
7
use Spiral\Attributes\Factory;
8
use Spiral\Attributes\NamedArgumentConstructor;
9
use Spiral\Tokenizer\TokenizationListenerInterface;
10
11
/**
12
 * When applied to a listener, this attribute will instruct the tokenizer to listen for classes that use attributes of
13
 * the given class.
14
 * @see TokenizationListenerInterface
15
 */
16
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE), NamedArgumentConstructor]
17
final class TargetAttribute implements ListenerDefinitionInterface
18
{
19
    /**
20
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
21
     * @param non-empty-string|null $scope
22
     */
23
    public function __construct(
24
        public readonly string $class,
25
        public readonly ?string $scope = null,
26
    ) {
27
    }
28
29
    public function filter(array $classes): \Generator
30
    {
31
        $target = new \ReflectionClass($this->class);
32
        $attribute = $target->getAttributes(\Attribute::class)[0] ?? null;
33
        $reader = (new Factory())->create();
34
35
        if ($attribute === null) {
36
            return;
37
        }
38
39
        $attribute = $attribute->newInstance();
40
41
        foreach ($classes as $class) {
42
            // If attribute is defined on class level and class has target attribute
43
            // then we can add it to the list of classes
44
            if (($attribute->flags & \Attribute::TARGET_CLASS)
45
                && $reader->firstClassMetadata($class, $target->getName())
46
            ) {
47
                yield $class->getName();
48
                continue;
49
            }
50
51
            // If attribute is defined on method level and class methods has target attribute
52
            // then we can add it to the list of classes
53
            if ($attribute->flags & \Attribute::TARGET_METHOD) {
54
                foreach ($class->getMethods() as $method) {
55
                    if ($reader->firstFunctionMetadata($method, $target->getName())) {
56
                        yield $class->getName();
57
                        continue 2;
58
                    }
59
                }
60
            }
61
62
            // If attribute is defined on property level and class properties has target attribute
63
            // then we can add it to the list of classes
64
            if ($attribute->flags & \Attribute::TARGET_PROPERTY) {
65
                foreach ($class->getProperties() as $property) {
66
                    if ($reader->firstPropertyMetadata($property, $target->getName())) {
67
                        yield $class->getName();
68
                        continue 2;
69
                    }
70
                }
71
            }
72
73
74
            // If attribute is defined on constant level and class constants has target attribute
75
            // then we can add it to the list of classes
76
            if ($attribute->flags & \Attribute::TARGET_CLASS_CONSTANT) {
77
                foreach ($class->getReflectionConstants() as $constant) {
78
                    if ($reader->firstConstantMetadata($constant, $target->getName())) {
79
                        yield $class->getName();
80
                        continue 2;
81
                    }
82
                }
83
            }
84
85
86
            // If attribute is defined on method parameters level and class method parameter has target attribute
87
            // then we can add it to the list of classes
88
            if ($attribute->flags & \Attribute::TARGET_PARAMETER) {
89
                foreach ($class->getMethods() as $method) {
90
                    foreach ($method->getParameters() as $parameter) {
91
                        if ($reader->firstParameterMetadata($parameter, $target->getName())) {
92
                            yield $class->getName();
93
                            continue 3;
94
                        }
95
                    }
96
                }
97
            }
98
        }
99
    }
100
101
    public function getScope(): ?string
102
    {
103
        return $this->scope;
104
    }
105
106
    public function getCacheKey(): string
107
    {
108
        return \md5($this->class . $this->scope);
109
    }
110
}
111