TargetAttribute::filter()   F
last analyzed

Complexity

Conditions 25
Paths 292

Size

Total Lines 91
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 44
CRAP Score 25.0514

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 91
ccs 44
cts 46
cp 0.9565
rs 2.1833
c 0
b 0
f 0
cc 25
nc 292
nop 1
crap 25.0514

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Internal\Instantiator\NamedArgumentsInstantiator;
10
use Spiral\Attributes\NamedArgumentConstructor;
11
use Spiral\Tokenizer\TokenizationListenerInterface;
12
13
/**
14
 * When applied to {@see TokenizationListenerInterface}, this attribute will instruct the tokenizer to listen for
15
 * classes that use attributes of the given class.
16
 */
17
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE), NamedArgumentConstructor]
18
final class TargetAttribute extends AbstractTarget
19
{
20
    /**
21
     * @param class-string $attribute
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 Class locator scope
23
     * @param bool $namedArguments Whether to use named arguments when reading attributes
24
     * @param bool $scanParents Whether to scan parent classes/interfaces for attributes target of which is class
25
     */
26 397
    public function __construct(
27
        private readonly string $attribute,
28
        ?string $scope = null,
29
        private readonly bool $useAnnotations = false,
30
        private readonly bool $namedArguments = true,
31
        private readonly bool $scanParents = false,
32
    ) {
33 397
        parent::__construct($scope);
34
    }
35
36 396
    public function filter(array $classes): \Generator
37
    {
38 396
        $target = new \ReflectionClass($this->attribute);
39 396
        $attribute = $target->getAttributes(\Attribute::class)[0] ?? null;
40
41
        // If annotations are used, we need to use the annotation reader also
42
        // It will slow down the process a bit, but it will allow us to use annotations
43
        /** @psalm-suppress InternalClass */
44 396
        $reader = $this->useAnnotations
45 5
            ? (new Factory())->create()
46 391
            : new AttributeReader($this->namedArguments ? new NamedArgumentsInstantiator() : null);
47
48 396
        if ($attribute === null) {
49
            return;
50
        }
51
52 396
        $attribute = $attribute->newInstance();
53
54 396
        foreach ($classes as $class) {
55
            // If attribute is defined on class level and class has target attribute
56
            // then we can add it to the list of classes
57 396
            if ($attribute->flags & \Attribute::TARGET_CLASS) {
58 385
                if ($reader->firstClassMetadata($class, $target->getName())) {
59 364
                    yield $class->getName();
60 364
                    continue;
61
                }
62
63 381
                if ($this->scanParents) {
64
                    // Interfaces
65 2
                    foreach ($class->getInterfaces() as $interface) {
66 1
                        if ($reader->firstClassMetadata($interface, $target->getName())) {
67 1
                            yield $class->getName();
68 1
                            continue 2;
69
                        }
70
                    }
71
72
                    // Parents
73 1
                    $parent = $class->getParentClass();
74 1
                    while ($parent !== false) {
75 1
                        if ($reader->firstClassMetadata($parent, $target->getName())) {
76 1
                            yield $class->getName();
77 1
                            continue 2;
78
                        }
79
                        $parent = $parent->getParentClass();
80
                    }
81
                }
82
            }
83
84
            // If attribute is defined on method level and class methods has target attribute
85
            // then we can add it to the list of classes
86 390
            if ($attribute->flags & \Attribute::TARGET_METHOD) {
87 6
                foreach ($class->getMethods() as $method) {
88 6
                    if ($reader->firstFunctionMetadata($method, $target->getName())) {
89 6
                        yield $class->getName();
90 6
                        continue 2;
91
                    }
92
                }
93
            }
94
95
            // If attribute is defined on property level and class properties has target attribute
96
            // then we can add it to the list of classes
97 385
            if ($attribute->flags & \Attribute::TARGET_PROPERTY) {
98 1
                foreach ($class->getProperties() as $property) {
99 1
                    if ($reader->firstPropertyMetadata($property, $target->getName())) {
100 1
                        yield $class->getName();
101 1
                        continue 2;
102
                    }
103
                }
104
            }
105
106
107
            // If attribute is defined on constant level and class constants has target attribute
108
            // then we can add it to the list of classes
109 385
            if ($attribute->flags & \Attribute::TARGET_CLASS_CONSTANT) {
110 2
                foreach ($class->getReflectionConstants() as $constant) {
111 2
                    if ($reader->firstConstantMetadata($constant, $target->getName())) {
112 2
                        yield $class->getName();
113 2
                        continue 2;
114
                    }
115
                }
116
            }
117
118
119
            // If attribute is defined on method parameters level and class method parameter has target attribute
120
            // then we can add it to the list of classes
121 385
            if ($attribute->flags & \Attribute::TARGET_PARAMETER) {
122 2
                foreach ($class->getMethods() as $method) {
123 2
                    foreach ($method->getParameters() as $parameter) {
124 2
                        if ($reader->firstParameterMetadata($parameter, $target->getName())) {
125 2
                            yield $class->getName();
126 2
                            continue 3;
127
                        }
128
                    }
129
                }
130
            }
131
        }
132
    }
133
}
134