Completed
Push — 2.x ( 6946d0...9e5583 )
by Akihito
29s queued 15s
created

AssistedInjectInterceptor::invoke()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 3
nc 3
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\Di\Di\Inject;
10
use Ray\Di\Di\InjectInterface;
11
use Ray\Di\Di\Named;
12
use ReflectionAttribute;
13
use ReflectionNamedType;
14
use ReflectionParameter;
15
16
use function assert;
17
use function call_user_func_array;
18
use function get_class;
19
use function in_array;
20
use function is_callable;
21
22
/**
23
 * Assisted injection interceptor for #[Inject] attributed parameter
24
 */
25
final class AssistedInjectInterceptor implements MethodInterceptor
26
{
27
    /** @var InjectorInterface */
28
    private $injector;
29
30
    /** @var MethodInvocationProvider */
31
    private $methodInvocationProvider;
32
33
    public function __construct(InjectorInterface $injector, MethodInvocationProvider $methodInvocationProvider)
34
    {
35
        $this->injector = $injector;
36
        $this->methodInvocationProvider = $methodInvocationProvider;
37
    }
38
39
    /**
40
     * @return mixed
41
     */
42
    public function invoke(MethodInvocation $invocation)
43
    {
44
        $this->methodInvocationProvider->set($invocation);
45
        $params = $invocation->getMethod()->getParameters();
46
        $namedArguments = $this->getNamedArguments($invocation);
47
        foreach ($params as $param) {
48
            /** @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...
49
            $attributes = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
50
            if (isset($attributes[0])) {
51
                /** @psalm-suppress MixedAssignment */
52
                $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...
53
            }
54
        }
55
56
        $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...
57
        assert(is_callable($callable));
58
59
        return call_user_func_array($callable, $namedArguments); // @phpstan-ignore-line PHP8 named arguments
60
    }
61
62
    /**
63
     * @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...
64
     */
65
    private function getNamedArguments(MethodInvocation $invocation): array
66
    {
67
        $args = $invocation->getArguments();
68
        $params = $invocation->getMethod()->getParameters();
69
        $namedParams = [];
70
        foreach ($params as $param) {
71
            $pos = $param->getPosition();
72
            if (isset($args[$pos])) {
73
                /** @psalm-suppress MixedAssignment */
74
                $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...
75
            }
76
        }
77
78
        return $namedParams;
79
    }
80
81
    /**
82
     * @return mixed
83
     */
84
    private function getDependency(ReflectionParameter $param)
85
    {
86
        $named = $this->getName($param);
87
        $type = $param->getType();
88
        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...
89
        $typeName = $type ? $type->getName() : '';
90
        $interface = in_array($typeName, Argument::UNBOUND_TYPE) ? '' : $typeName;
91
92
        return $this->injector->getInstance($interface, $named);
93
    }
94
95
    private function getName(ReflectionParameter $param): string
96
    {
97
        /** @var list<ReflectionAttribute> $nameds */
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...
98
        $nameds = $param->getAttributes(Named::class);
99
        if (isset($nameds[0])) {
100
            $named = $nameds[0]->newInstance();
101
            assert($named instanceof Named);
102
103
            return $named->value;
104
        }
105
106
        if ($param->getAttributes(Inject::class)) {
107
            return '';
108
        }
109
110
        return $this->getCustomeInject($param);
111
    }
112
113
    /**
114
     * @return class-string
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" at position 0. (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...
115
     */
116
    private function getCustomeInject(ReflectionParameter $param): string
117
    {
118
        /** @var list<ReflectionAttribute> $injects */
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...
119
        $injects = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
120
        $inject = $injects[0]->newInstance();
121
        assert($inject instanceof InjectInterface);
122
123
        return get_class($inject);
124
    }
125
}
126