Passed
Push — main ( d72029...35e57d )
by Thomas
03:10
created

Resolver   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 16
eloc 41
dl 0
loc 86
ccs 47
cts 47
cp 1
rs 10
c 1
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getParamInfo() 0 14 4
A autowire() 0 11 2
A __construct() 0 2 1
A resolveClosureArgs() 0 5 1
A resolveArgs() 0 12 3
A resolveParam() 0 25 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Conia\Chuck\Registry;
6
7
use Closure;
8
use Conia\Chuck\Exception\ContainerException;
9
use Conia\Chuck\Exception\NotFoundException;
10
use ReflectionClass;
11
use ReflectionFunction;
12
use ReflectionFunctionAbstract;
13
use ReflectionMethod;
14
use ReflectionNamedType;
15
use ReflectionParameter;
16
use Throwable;
17
18
class Resolver
19
{
20 125
    public function __construct(protected readonly Registry $registry)
21
    {
22 125
    }
23
24
    /** @psalm-param class-string $class */
25 64
    public function autowire(string $class): object
26
    {
27 64
        $rc = new ReflectionClass($class);
28 64
        $constructor = $rc->getConstructor();
29 64
        $args = $this->resolveArgs($constructor);
30
31
        try {
32 61
            return $rc->newInstance(...$args);
33 2
        } catch (Throwable $e) {
34 2
            throw new ContainerException(
35 2
                'Autowiring unresolvable: ' . $class . ' Details: ' . $e->getMessage()
36 2
            );
37
        }
38
    }
39
40 24
    public function resolveParam(ReflectionParameter $param): mixed
41
    {
42 24
        $type = $param->getType();
43
44 24
        if ($type instanceof ReflectionNamedType) {
45
            try {
46 20
                return $this->registry->getWithParamName($type->getName(), '$' . ltrim($param->getName(), '?'));
47 3
            } catch (NotFoundException $e) {
48 2
                if ($param->isDefaultValueAvailable()) {
49 1
                    return $param->getDefaultValue();
50
                }
51
52 1
                throw $e;
53
            }
54
        } else {
55 4
            if ($type) {
56 2
                throw new ContainerException(
57 2
                    "Autowiring does not support union or intersection types. Source: \n" .
58 2
                        $this->getParamInfo($param)
59 2
                );
60
            }
61
62 2
            throw new ContainerException(
63 2
                "Autowired entities need to have typed constructor parameters. Source: \n" .
64 2
                    $this->getParamInfo($param)
65 2
            );
66
        }
67
    }
68
69 6
    public function getParamInfo(ReflectionParameter $param): string
70
    {
71 6
        $type = $param->getType();
72 6
        $rf = $param->getDeclaringFunction();
73 6
        $rc = null;
74
75 6
        if ($rf instanceof ReflectionMethod) {
76 6
            $rc = $rf->getDeclaringClass();
77
        }
78
79 6
        return ($rc ? $rc->getName() . '::' : '') .
80 6
            ($rf->getName() . '(..., ') .
81 6
            ($type ? (string)$type . ' ' : '') .
82 6
            '$' . $param->getName() . ', ...)';
83
    }
84
85 8
    public function resolveClosureArgs(Closure $closure): array
86
    {
87 8
        $rf = new ReflectionFunction($closure);
88
89 8
        return $this->resolveArgs($rf);
90
    }
91
92 67
    protected function resolveArgs(?ReflectionFunctionAbstract $rf): array
93
    {
94 67
        $args = [];
95
96 67
        if ($rf) {
97 15
            foreach ($rf->getParameters() as $param) {
98
                /** @psalm-var list<mixed> */
99 9
                $args[] = $this->resolveParam($param);
100
            }
101
        }
102
103 64
        return $args;
104
    }
105
}
106