Attributes::forConstants()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

390
                            class: $reflection->/** @scrutinizer ignore-call */ getDeclaringClass()->getName(),
Loading history...
391
                            method: $name,
392
                            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

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