Collector::getInstances()   B
last analyzed

Complexity

Conditions 8
Paths 5

Size

Total Lines 53
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 33
dl 0
loc 53
rs 8.1475
c 0
b 0
f 0
cc 8
nc 5
nop 2

How to fix   Long Method   

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
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Attribute\Collector;
15
16
use Closure;
17
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use ReflectionAttribute;
19
use ReflectionClass;
20
use ReflectionClassConstant;
21
use ReflectionException;
22
use ReflectionFunction;
23
use ReflectionMethod;
24
use ReflectionParameter;
25
use ReflectionProperty;
26
use Reflector;
27
use Valkyrja\Attribute\Collector\Contract\CollectorContract as Contract;
28
use Valkyrja\Attribute\Throwable\Exception\RuntimeException;
29
use Valkyrja\Dispatch\Data\CallableDispatch;
30
use Valkyrja\Dispatch\Data\ClassDispatch;
31
use Valkyrja\Dispatch\Data\ConstantDispatch;
32
use Valkyrja\Dispatch\Data\MethodDispatch;
33
use Valkyrja\Dispatch\Data\PropertyDispatch;
34
use Valkyrja\Reflection\Reflector\Contract\ReflectorContract;
35
use Valkyrja\Reflection\Reflector\Reflector as ReflectorReflector;
36
37
use function is_callable;
38
39
class Collector implements Contract
40
{
41
    /**
42
     * Default flags for the getAttributes() method.
43
     *
44
     * @var int
45
     */
46
    protected static int $defaultFlags = ReflectionAttribute::IS_INSTANCEOF;
47
48
    public function __construct(
49
        protected ReflectorContract $reflection = new ReflectorReflector(),
50
    ) {
51
    }
52
53
    /**
54
     * @inheritDoc
55
     *
56
     * @throws ReflectionException
57
     */
58
    #[Override]
59
    public function forClass(string $class, string|null $attribute = null, int|null $flags = null): array
60
    {
61
        $reflection = $this->reflection->forClass($class);
62
63
        return $this->getInstances(
64
            $reflection,
65
            ...$reflection->getAttributes($attribute, $flags ?? static::$defaultFlags)
66
        );
67
    }
68
69
    /**
70
     * @inheritDoc
71
     *
72
     * @throws ReflectionException
73
     */
74
    #[Override]
75
    public function forClassMembers(string $class, string|null $attribute = null, int|null $flags = null): array
76
    {
77
        return [
78
            ...$this->forConstants($class, $attribute, $flags),
79
            ...$this->forProperties($class, $attribute, $flags),
80
            ...$this->forMethods($class, $attribute, $flags),
81
        ];
82
    }
83
84
    /**
85
     * @inheritDoc
86
     *
87
     * @throws ReflectionException
88
     */
89
    #[Override]
90
    public function forClassAndMembers(string $class, string|null $attribute = null, int|null $flags = null): array
91
    {
92
        return [
93
            ...$this->forClass($class, $attribute, $flags),
94
            ...$this->forClassMembers($class, $attribute, $flags),
95
        ];
96
    }
97
98
    /**
99
     * @inheritDoc
100
     *
101
     * @throws ReflectionException
102
     */
103
    #[Override]
104
    public function forConstant(
105
        string $class,
106
        string $constant,
107
        string|null $attribute = null,
108
        int|null $flags = null
109
    ): array {
110
        return $this->forClassMember($attribute, $flags, $this->reflection->forClassConstant($class, $constant));
111
    }
112
113
    /**
114
     * @inheritDoc
115
     *
116
     * @throws ReflectionException
117
     */
118
    #[Override]
119
    public function forConstants(string $class, string|null $attribute = null, int|null $flags = null): array
120
    {
121
        return $this->forClassMember(
122
            $attribute,
123
            $flags,
124
            ...$this->reflection->forClass($class)->getReflectionConstants()
125
        );
126
    }
127
128
    /**
129
     * @inheritDoc
130
     *
131
     * @throws ReflectionException
132
     */
133
    #[Override]
134
    public function forProperty(
135
        string $class,
136
        string $property,
137
        string|null $attribute = null,
138
        int|null $flags = null
139
    ): array {
140
        return $this->forClassMember($attribute, $flags, $this->reflection->forClassProperty($class, $property));
141
    }
142
143
    /**
144
     * @inheritDoc
145
     *
146
     * @throws ReflectionException
147
     */
148
    #[Override]
149
    public function forProperties(string $class, string|null $attribute = null, int|null $flags = null): array
150
    {
151
        return $this->forClassMember(
152
            $attribute,
153
            $flags,
154
            ...$this->reflection->forClass($class)->getProperties()
155
        );
156
    }
157
158
    /**
159
     * @inheritDoc
160
     *
161
     * @throws ReflectionException
162
     */
163
    #[Override]
164
    public function forMethod(
165
        string $class,
166
        string $method,
167
        string|null $attribute = null,
168
        int|null $flags = null
169
    ): array {
170
        return $this->forClassMember($attribute, $flags, $this->reflection->forClassMethod($class, $method));
171
    }
172
173
    /**
174
     * @inheritDoc
175
     *
176
     * @throws ReflectionException
177
     */
178
    #[Override]
179
    public function forMethodParameters(
180
        string $class,
181
        string $method,
182
        string|null $attribute = null,
183
        int|null $flags = null
184
    ): array {
185
        return $this->forParameter(
186
            $attribute,
187
            $flags,
188
            ...$this->reflection->forClassMethod($class, $method)->getParameters()
189
        );
190
    }
191
192
    /**
193
     * @inheritDoc
194
     *
195
     * @throws ReflectionException
196
     */
197
    #[Override]
198
    public function forMethodParameter(
199
        string $class,
200
        string $method,
201
        string $parameter,
202
        string|null $attribute = null,
203
        int|null $flags = null
204
    ): array {
205
        $parameters = $this->reflection->forClassMethod($class, $method)->getParameters();
206
207
        foreach ($parameters as $reflectionParameter) {
208
            if ($reflectionParameter->getName() === $parameter) {
209
                return $this->forParameter(
210
                    $attribute,
211
                    $flags,
212
                    $reflectionParameter
213
                );
214
            }
215
        }
216
217
        return [];
218
    }
219
220
    /**
221
     * @inheritDoc
222
     *
223
     * @throws ReflectionException
224
     */
225
    #[Override]
226
    public function forMethods(string $class, string|null $attribute = null, int|null $flags = null): array
227
    {
228
        return $this->forClassMember(
229
            $attribute,
230
            $flags,
231
            ...$this->reflection->forClass($class)->getMethods()
232
        );
233
    }
234
235
    /**
236
     * @inheritDoc
237
     *
238
     * @throws ReflectionException
239
     */
240
    #[Override]
241
    public function forFunction(string $function, string|null $attribute = null, int|null $flags = null): array
242
    {
243
        $reflection = $this->reflection->forFunction($function);
244
245
        return $this->getInstances(
246
            $reflection,
247
            ...$reflection->getAttributes($attribute, $flags ?? static::$defaultFlags)
248
        );
249
    }
250
251
    /**
252
     * @inheritDoc
253
     *
254
     * @throws ReflectionException
255
     */
256
    #[Override]
257
    public function forFunctionParameters(string $function, string|null $attribute = null, int|null $flags = null): array
258
    {
259
        return $this->forParameter(
260
            $attribute,
261
            $flags,
262
            ...$this->reflection->forFunction($function)->getParameters()
263
        );
264
    }
265
266
    /**
267
     * @inheritDoc
268
     *
269
     * @throws ReflectionException
270
     */
271
    #[Override]
272
    public function forClosure(Closure $closure, string|null $attribute = null, int|null $flags = null): array
273
    {
274
        $reflection = $this->reflection->forClosure($closure);
275
276
        return $this->getInstances(
277
            $reflection,
278
            ...$reflection->getAttributes($attribute, $flags ?? static::$defaultFlags)
279
        );
280
    }
281
282
    /**
283
     * @inheritDoc
284
     *
285
     * @throws ReflectionException
286
     */
287
    #[Override]
288
    public function forClosureParameters(Closure $closure, string|null $attribute = null, int|null $flags = null): array
289
    {
290
        return $this->forParameter(
291
            $attribute,
292
            $flags,
293
            ...$this->reflection->forClosure($closure)->getParameters()
294
        );
295
    }
296
297
    /**
298
     * Get a class' members' attributes.
299
     *
300
     * @param class-string|null                                           $attribute  [optional] The attribute to return
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
301
     * @param ReflectionClassConstant|ReflectionMethod|ReflectionProperty ...$members [optional] The members
302
     *
303
     * @return object[]
304
     */
305
    protected function forClassMember(
306
        string|null $attribute = null,
307
        int|null $flags = null,
308
        ReflectionClassConstant|ReflectionMethod|ReflectionProperty ...$members
309
    ): array {
310
        $instances = [];
311
312
        foreach ($members as $member) {
313
            /** @psalm-suppress PossiblyInvalidArgument $member is absolutely a Reflector */
314
            $instances = [
315
                ...$instances,
316
                ...$this->getInstances(
317
                    $member,
318
                    ...$member->getAttributes($attribute, $flags ?? static::$defaultFlags)
319
                ),
320
            ];
321
        }
322
323
        return $instances;
324
    }
325
326
    /**
327
     * Get a parameter' attributes.
328
     *
329
     * @param class-string|null   $attribute     [optional] The attribute to return
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
330
     * @param ReflectionParameter ...$parameters [optional] The parameters
331
     *
332
     * @return object[]
333
     */
334
    protected function forParameter(
335
        string|null $attribute = null,
336
        int|null $flags = null,
337
        ReflectionParameter ...$parameters
338
    ): array {
339
        $instances = [];
340
341
        foreach ($parameters as $parameter) {
342
            $instances = [
343
                ...$instances,
344
                ...$this->getInstances(
345
                    $parameter,
346
                    ...$parameter->getAttributes($attribute, $flags ?? static::$defaultFlags)
347
                ),
348
            ];
349
        }
350
351
        return $instances;
352
    }
353
354
    /**
355
     * Set the base annotation model values.
356
     *
357
     * @param ReflectionAttribute ...$reflectionAttributes The reflection attributes
358
     *
359
     * @return object[]
360
     */
361
    protected function getInstances(Reflector $reflection, ReflectionAttribute ...$reflectionAttributes): array
362
    {
363
        $instances = [];
364
365
        foreach ($reflectionAttributes as $reflectionAttribute) {
366
            $instance = $reflectionAttribute->newInstance();
367
368
            if (method_exists($instance, 'setReflection')) {
369
                $instance->setReflection($reflection);
370
            }
371
372
            if (
373
                method_exists($instance, 'withDispatch')
374
                && method_exists($instance, 'getDispatch')
375
                && method_exists($reflection, 'getName')
376
            ) {
377
                /** @var non-empty-string $name */
378
                $name = $reflection->getName();
379
                /** @var object $instance */
380
                $instance = $instance->withDispatch(
381
                    match (true) {
382
                        $reflection instanceof ReflectionMethod        => new MethodDispatch(
383
                            class: $reflection->getDeclaringClass()->getName(),
0 ignored issues
show
Bug introduced by
The method getDeclaringClass() does not exist on Reflector. It seems like you code against a sub-type of Reflector such as ReflectionProperty or ReflectionClassConstant or ReflectionParameter or ReflectionMethod. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

383
                            class: $reflection->/** @scrutinizer ignore-call */ getDeclaringClass()->getName(),
Loading history...
384
                            method: $name,
385
                            isStatic: $reflection->isStatic()
0 ignored issues
show
Bug introduced by
The method isStatic() does not exist on Reflector. It seems like you code against a sub-type of Reflector such as ReflectionProperty or ReflectionMethod. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

385
                            isStatic: $reflection->/** @scrutinizer ignore-call */ isStatic()
Loading history...
386
                        ),
387
                        $reflection instanceof ReflectionProperty      => new PropertyDispatch(
388
                            class: $reflection->getDeclaringClass()->getName(),
389
                            property: $name,
390
                            isStatic: $reflection->isStatic()
391
                        ),
392
                        $reflection instanceof ReflectionClass         => new ClassDispatch(
393
                            class: $reflection->getName(),
394
                        ),
395
                        $reflection instanceof ReflectionClassConstant => new ConstantDispatch(
396
                            constant: $name,
397
                            class: $reflection->getDeclaringClass()->getName()
398
                        ),
399
                        $reflection instanceof ReflectionFunction
400
                        && ! $reflection->isClosure()                  => new CallableDispatch(
401
                            callable: is_callable($name)
402
                                ? $name
403
                                : throw new RuntimeException('ReflectionFunction has no valid callable'),
404
                        ),
405
                        default                                        => $instance->getDispatch(),
406
                    }
407
                );
408
            }
409
410
            $instances[] = $instance;
411
        }
412
413
        return $instances;
414
    }
415
}
416