Test Failed
Pull Request — master (#878)
by butschster
09:10 queued 55s
created

TargetAttribute   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 99
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 22
eloc 38
c 2
b 0
f 0
dl 0
loc 99
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
D filter() 0 70 19
A getScope() 0 3 1
A __construct() 0 5 1
A getCacheKey() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Tokenizer\Attribute;
6
7
use Spiral\Attributes\AttributeReader;
8
use Spiral\Attributes\Factory;
9
use Spiral\Attributes\NamedArgumentConstructor;
10
use Spiral\Tokenizer\TokenizationListenerInterface;
11
12
/**
13
 * When applied to a listener, this attribute will instruct the tokenizer to listen for classes that use attributes of
14
 * the given class.
15
 * @see TokenizationListenerInterface
16
 */
17
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE), NamedArgumentConstructor]
18
final class TargetAttribute implements ListenerDefinitionInterface
19
{
20
    /**
21
     * @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...
22
     * @param non-empty-string|null $scope
23
     */
24
    public function __construct(
25
        public readonly string $class,
26
        public readonly ?string $scope = null,
27
        public readonly bool $useAnnotations = false,
28
    ) {
29
    }
30
31
    public function filter(array $classes): \Generator
32
    {
33
        $target = new \ReflectionClass($this->class);
34
        $attribute = $target->getAttributes(\Attribute::class)[0] ?? null;
35
36
        // If annotations are used, we need to use the annotation reader also
37
        // It will slow down the process a bit, but it will allow us to use annotations
38
        $reader = $this->useAnnotations
39
            ? (new Factory())->create()
40
            : new AttributeReader();
41
42
        if ($attribute === null) {
43
            return;
44
        }
45
46
        $attribute = $attribute->newInstance();
47
48
        foreach ($classes as $class) {
49
            // If attribute is defined on class level and class has target attribute
50
            // then we can add it to the list of classes
51
            if (($attribute->flags & \Attribute::TARGET_CLASS)
52
                && $reader->firstClassMetadata($class, $target->getName())
53
            ) {
54
                yield $class->getName();
55
                continue;
56
            }
57
58
            // If attribute is defined on method level and class methods has target attribute
59
            // then we can add it to the list of classes
60
            if ($attribute->flags & \Attribute::TARGET_METHOD) {
61
                foreach ($class->getMethods() as $method) {
62
                    if ($reader->firstFunctionMetadata($method, $target->getName())) {
63
                        yield $class->getName();
64
                        continue 2;
65
                    }
66
                }
67
            }
68
69
            // If attribute is defined on property level and class properties has target attribute
70
            // then we can add it to the list of classes
71
            if ($attribute->flags & \Attribute::TARGET_PROPERTY) {
72
                foreach ($class->getProperties() as $property) {
73
                    if ($reader->firstPropertyMetadata($property, $target->getName())) {
74
                        yield $class->getName();
75
                        continue 2;
76
                    }
77
                }
78
            }
79
80
81
            // If attribute is defined on constant level and class constants has target attribute
82
            // then we can add it to the list of classes
83
            if ($attribute->flags & \Attribute::TARGET_CLASS_CONSTANT) {
84
                foreach ($class->getReflectionConstants() as $constant) {
85
                    if ($reader->firstConstantMetadata($constant, $target->getName())) {
86
                        yield $class->getName();
87
                        continue 2;
88
                    }
89
                }
90
            }
91
92
93
            // If attribute is defined on method parameters level and class method parameter has target attribute
94
            // then we can add it to the list of classes
95
            if ($attribute->flags & \Attribute::TARGET_PARAMETER) {
96
                foreach ($class->getMethods() as $method) {
97
                    foreach ($method->getParameters() as $parameter) {
98
                        if ($reader->firstParameterMetadata($parameter, $target->getName())) {
99
                            yield $class->getName();
100
                            continue 3;
101
                        }
102
                    }
103
                }
104
            }
105
        }
106
    }
107
108
    public function getScope(): ?string
109
    {
110
        return $this->scope;
111
    }
112
113
    public function getCacheKey(): string
114
    {
115
        return \md5($this->class . $this->scope);
116
    }
117
}
118