Completed
Pull Request — 2.x (#228)
by Akihito
02:15 queued 01:08
created

AssistedInjectInterceptor   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 83
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 5
dl 0
loc 83
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A invoke() 0 19 3
A getNamedArguments() 0 15 3
A getDependency() 0 9 3
A getName() 0 13 2
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\Di\Di\Inject;
10
use Ray\Di\Di\Named;
11
use ReflectionAttribute;
12
use ReflectionNamedType;
13
use ReflectionParameter;
14
15
use function assert;
16
use function call_user_func_array;
17
use function is_callable;
18
19
/**
20
 * Assisted injection interceptor for #[Inject] attributed parameter
21
 */
22
final class AssistedInjectInterceptor implements MethodInterceptor
23
{
24
    /** @var InjectorInterface */
25
    private $injector;
26
27
    /** @var MethodInvocationProvider */
28
    private $methodInvocationProvider;
29
30
    public function __construct(InjectorInterface $injector, MethodInvocationProvider $methodInvocationProvider)
31
    {
32
        $this->injector = $injector;
33
        $this->methodInvocationProvider = $methodInvocationProvider;
34
    }
35
36
    /**
37
     * @return mixed
38
     */
39
    public function invoke(MethodInvocation $invocation)
40
    {
41
        $this->methodInvocationProvider->set($invocation);
42
        $params = $invocation->getMethod()->getParameters();
43
        $namedArguments = $this->getNamedArguments($invocation);
44
        foreach ($params as $param) {
45
            /** @var list<ReflectionAttribute> $attributes */
0 ignored issues
show
Documentation introduced by
The doc-type list<ReflectionAttribute> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
46
            $attributes = $param->getAttributes(Inject::class);
47
            if (isset($attributes[0])) {
48
                /** @psalm-suppress MixedAssignment */
49
                $namedArguments[$param->getName()] = $this->getDependency($param);
0 ignored issues
show
Bug introduced by
Consider using $param->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
50
            }
51
        }
52
53
        $callable = [$invocation->getThis(), $invocation->getMethod()->getName()];
0 ignored issues
show
Bug introduced by
Consider using $invocation->getMethod()->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
54
        assert(is_callable($callable));
55
56
        return call_user_func_array($callable, $namedArguments); // @phpstan-ignore-line PHP8 named arguments
57
    }
58
59
    /**
60
     * @return array<string, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
61
     */
62
    private function getNamedArguments(MethodInvocation $invocation): array
63
    {
64
        $args = $invocation->getArguments();
65
        $params = $invocation->getMethod()->getParameters();
66
        $namedParams = [];
67
        foreach ($params as $param) {
68
            $pos = $param->getPosition();
69
            if (isset($args[$pos])) {
70
                /** @psalm-suppress MixedAssignment */
71
                $namedParams[$param->getName()] = $args[$pos];
0 ignored issues
show
Bug introduced by
Consider using $param->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
72
            }
73
        }
74
75
        return $namedParams;
76
    }
77
78
    /**
79
     * @return mixed
80
     */
81
    private function getDependency(ReflectionParameter $param)
82
    {
83
        $named = $this->getName($param);
84
        $type = $param->getType();
85
        assert($type instanceof ReflectionNamedType || $type === null);
0 ignored issues
show
Bug introduced by
The class ReflectionNamedType does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
86
        $interface = $type ? $type->getName() : '';
87
88
        return $this->injector->getInstance($interface, $named);
89
    }
90
91
    private function getName(ReflectionParameter $param): string
92
    {
93
        /** @var list<ReflectionAttribute> $attributes */
0 ignored issues
show
Documentation introduced by
The doc-type list<ReflectionAttribute> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
94
        $attributes = $param->getAttributes(Named::class);
95
        if (isset($attributes[0])) {
96
            $named = $attributes[0]->newInstance();
97
            assert($named instanceof Named);
98
99
            return $named->value;
100
        }
101
102
        return '';
103
    }
104
}
105