AssistedInterceptor::getInterface()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
rs 9.6111
cc 5
nc 4
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use Ray\Aop\MethodInterceptor;
8
use Ray\Aop\MethodInvocation;
9
use Ray\Aop\ReflectionMethod;
10
use Ray\Di\Di\Assisted;
11
use Ray\Di\Di\Named;
12
use ReflectionNamedType;
13
use ReflectionParameter;
14
use ReflectionType;
15
16
use function assert;
17
use function class_exists;
18
use function in_array;
19
use function interface_exists;
20
use function is_string;
21
use function parse_str;
22
23
/**
24
 * Assisted injection interceptor for @ Assisted annotated parameter
25
 */
26
final class AssistedInterceptor implements MethodInterceptor
27
{
28
    /** @var InjectorInterface */
29
    private $injector;
30
31
    /** @var MethodInvocationProvider */
32
    private $methodInvocationProvider;
33
34
    public function __construct(InjectorInterface $injector, MethodInvocationProvider $methodInvocationProvider)
35
    {
36
        $this->injector = $injector;
37
        $this->methodInvocationProvider = $methodInvocationProvider;
38
    }
39
40
    /**
41
     * Intercepts any method and injects instances of the missing arguments
42
     * when they are type hinted
43
     *
44
     * @return mixed
45
     */
46
    public function invoke(MethodInvocation $invocation)
47
    {
48
        $method = $invocation->getMethod();
49
        $assisted = $method->getAnnotation(Assisted::class);
50
        $parameters = $method->getParameters();
51
        $arguments = $invocation->getArguments()->getArrayCopy();
52
        if ($assisted instanceof Assisted) {
53
            $this->methodInvocationProvider->set($invocation);
54
            $arguments = $this->injectAssistedParameters($method, $assisted, $parameters, $arguments);
55
        }
56
57
        $invocation->getArguments()->exchangeArray($arguments);
58
59
        return $invocation->proceed();
60
    }
61
62
    /**
63
     * @param array<ReflectionParameter> $parameters
64
     * @param array<int, mixed>          $arguments
65
     *
66
     * @return array<int, mixed>
67
     *
68
     * @internal param int $cntArgs
69
     */
70
    public function injectAssistedParameters(ReflectionMethod $method, Assisted $assisted, array $parameters, array $arguments): array
71
    {
72
        foreach ($parameters as $parameter) {
73
            if (! in_array($parameter->getName(), $assisted->values, true)) {
74
                continue;
75
            }
76
77
            $type = $parameter->getType();
78
            $interface = $this->getInterface($type);
79
            $name = $this->getName($method, $parameter);
80
            $pos = $parameter->getPosition();
81
            /** @psalm-suppress MixedAssignment */
82
            $arguments[$pos] = $this->injector->getInstance($interface, $name);
83
        }
84
85
        return $arguments;
86
    }
87
88
    /**
89
     * Return dependency "name"
90
     */
91
    private function getName(ReflectionMethod $method, ReflectionParameter $parameter): string
92
    {
93
        $named = $method->getAnnotation(Named::class);
94
        if (! $named instanceof Named) {
95
            return Name::ANY;
96
        }
97
98
        parse_str($named->value, $names);
99
        $paramName = $parameter->getName();
100
        if (isset($names[$paramName]) && is_string($names[$paramName])) {
101
            return $names[$paramName];
102
        }
103
104
        return Name::ANY;
105
    }
106
107
    /**
108
     * @return ''|class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment ''|class-string at position 0 could not be parsed: Unknown type name '''' at position 0 in ''|class-string.
Loading history...
109
     */
110
    private function getInterface(?ReflectionType $type)
111
    {
112
        $interface = $type instanceof ReflectionNamedType && ! in_array($type->getName(), Argument::UNBOUND_TYPE, true) ? $type->getName() : '';
113
        assert($interface === '' || interface_exists($interface) || class_exists($interface));
114
115
        return $interface;
116
    }
117
}
118