Completed
Push — 2.x ( 7cc030...4a0149 )
by Akihito
16s queued 13s
created

AssistedInterceptor   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 13
eloc 31
c 0
b 0
f 0
dl 0
loc 80
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A invoke() 0 14 2
B injectAssistedParameters() 0 17 7
A getName() 0 14 3
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
15
use function assert;
16
use function class_exists;
17
use function in_array;
18
use function interface_exists;
19
use function parse_str;
20
21
/**
22
 * Assisted injection interceptor for @ Assisted annotated parameter
23
 */
24
final class AssistedInterceptor implements MethodInterceptor
25
{
26
    /** @var InjectorInterface */
27
    private $injector;
28
29
    /** @var MethodInvocationProvider */
30
    private $methodInvocationProvider;
31
32
    public function __construct(InjectorInterface $injector, MethodInvocationProvider $methodInvocationProvider)
33
    {
34
        $this->injector = $injector;
35
        $this->methodInvocationProvider = $methodInvocationProvider;
36
    }
37
38
    /**
39
     * Intercepts any method and injects instances of the missing arguments
40
     * when they are type hinted
41
     *
42
     * @return mixed
43
     */
44
    public function invoke(MethodInvocation $invocation)
45
    {
46
        $method = $invocation->getMethod();
47
        $assisted = $method->getAnnotation(Assisted::class);
48
        $parameters = $method->getParameters();
49
        $arguments = $invocation->getArguments()->getArrayCopy();
50
        if ($assisted instanceof Assisted) {
51
            $this->methodInvocationProvider->set($invocation);
52
            $arguments = $this->injectAssistedParameters($method, $assisted, $parameters, $arguments);
53
        }
54
55
        $invocation->getArguments()->exchangeArray($arguments);
56
57
        return $invocation->proceed();
58
    }
59
60
    /**
61
     * @param array<ReflectionParameter> $parameters
62
     * @param array<int, mixed>          $arguments
63
     *
64
     * @return array<int, mixed>
65
     *
66
     * @internal param int $cntArgs
67
     */
68
    public function injectAssistedParameters(ReflectionMethod $method, Assisted $assisted, array $parameters, array $arguments): array
69
    {
70
        foreach ($parameters as $parameter) {
71
            if (! in_array($parameter->getName(), $assisted->values, true)) {
72
                continue;
73
            }
74
75
            $type = $parameter->getType();
76
            $interface = $type instanceof ReflectionNamedType && ! in_array($type->getName(), Argument::UNBOUND_TYPE, true) ? $type->getName() : '';
77
            $name = $this->getName($method, $parameter);
78
            $pos = $parameter->getPosition();
79
            assert(class_exists($interface) || interface_exists($interface) || $interface === '');
80
            /** @psalm-suppress MixedAssignment */
81
            $arguments[$pos] = $this->injector->getInstance($interface, $name);
82
        }
83
84
        return $arguments;
85
    }
86
87
    /**
88
     * Return dependency "name"
89
     */
90
    private function getName(ReflectionMethod $method, ReflectionParameter $parameter): string
91
    {
92
        $named = $method->getAnnotation(Named::class);
93
        if (! $named instanceof Named) {
94
            return Name::ANY;
95
        }
96
97
        parse_str($named->value, $names);
98
        $paramName = $parameter->getName();
99
        if (isset($names[$paramName])) {
100
            return (string) $names[$paramName];
101
        }
102
103
        return Name::ANY;
104
    }
105
}
106