Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
23 / 23
40.00% covered (danger)
40.00%
4 / 10
42.86% covered (danger)
42.86%
3 / 7
100.00% covered (success)
100.00%
3 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
AttributesResolver
100.00% covered (success)
100.00%
23 / 23
40.00% covered (danger)
40.00%
4 / 10
42.86% covered (danger)
42.86%
3 / 7
100.00% covered (success)
100.00%
3 / 3
12.72
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 newAttributeInstance
100.00% covered (success)
100.00%
11 / 11
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 4
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Celemas\Router;
6
7use Celemas\Wire\Call;
8use Celemas\Wire\CallableResolver;
9use Celemas\Wire\Creator;
10use Psr\Container\ContainerInterface as Container;
11use ReflectionAttribute;
12use ReflectionClass;
13use ReflectionFunctionAbstract;
14use ReflectionObject;
15
16/** @internal */
17final class AttributesResolver
18{
19    private readonly array $attributes;
20
21    /** @param list<ReflectionClass|ReflectionFunctionAbstract> $reflectors */
22    public function __construct(
23        array $reflectors,
24        protected readonly ?Container $container,
25    ) {
26        $reflectionAttributes = array_merge(
27            ...array_map(static fn($reflector) => $reflector->getAttributes(), $reflectors),
28        );
29
30        $this->attributes = array_map(
31            $this->newAttributeInstance(...),
32            $reflectionAttributes,
33        );
34    }
35
36    /** @param ?class-string $filter */
37    public function get(?string $filter = null): array
38    {
39        if ($filter) {
40            return array_values(
41                array_filter($this->attributes, static fn($attribute) => $attribute instanceof $filter),
42            );
43        }
44
45        return $this->attributes;
46    }
47
48    private function newAttributeInstance(ReflectionAttribute $attribute): object
49    {
50        $instance = $attribute->newInstance();
51        $callAttrs = new ReflectionObject($instance)->getAttributes(Call::class);
52
53        if (count($callAttrs) > 0) {
54            $resolver = new CallableResolver(new Creator($this->container));
55
56            // See if the attribute itself has one or more Call attributes. If so,
57            // resolve/autowire the arguments of the method it states and call it.
58            foreach ($callAttrs as $callAttr) {
59                $callAttr = $callAttr->newInstance();
60                $methodToResolve = $callAttr->method;
61
62                /** @var callable $callable */
63                $callable = [$instance, $methodToResolve];
64                $args = $resolver->resolve($callable, $callAttr->args);
65                $callable(...$args);
66            }
67        }
68
69        return $instance;
70    }
71}