Passed
Pull Request — master (#32)
by Aleksei
14:34
created

ResolvingState::getDataToTemplate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 6
ccs 0
cts 0
cp 0
crap 6
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Injector;
6
7
use Generator;
8
use ReflectionFunctionAbstract;
9
10
/**
11
 * Intermediate arguments resolving data to pass around until resolving is finished.
12
 * @internal
13
 */
14
final class ResolvingState
15
{
16
    private ReflectionFunctionAbstract $reflection;
17
    /** @var array<int, object> */
18
    private array $numericArguments = [];
19
    /** @var array<string, mixed> */
20
    private array $namedArguments = [];
21
    private bool $shouldPushTrailingArguments;
22
    private array $resolvedValues = [];
23
    /** @var null|array<string, string> */
24
    private ?array $templateData = null;
25
26
    /**
27
     * @param ReflectionFunctionAbstract $reflection Function reflection.
28 45
     * @param array $arguments User arguments.
29
     */
30 45
    public function __construct(ReflectionFunctionAbstract $reflection, array $arguments)
31 45
    {
32 45
        $this->reflection = $reflection;
33 42
        $this->shouldPushTrailingArguments = !$reflection->isInternal();
34
        $this->sortArguments($arguments);
35 3
    }
36
37 3
    public function hasNamedArgument(string $name): bool
38
    {
39
        return array_key_exists($name, $this->namedArguments);
40
    }
41
42
    /**
43 38
     * @param bool $condition If true then trailing arguments will not be passed.
44
     */
45 38
    public function disablePushTrailingArguments(bool $condition): void
46 38
    {
47
        $this->shouldPushTrailingArguments = $this->shouldPushTrailingArguments && !$condition;
48 30
    }
49
50 30
    public function addResolvedValue(&$value): void
51 30
    {
52 38
        $this->resolvedValues[] = &$value;
53
    }
54 38
    public function resolveParameterByName(string $name, bool $variadic): bool
55 32
    {
56
        if (!array_key_exists($name, $this->namedArguments)) {
57 12
            return false;
58 2
        }
59
        if ($variadic && is_array($this->namedArguments[$name])) {
60 11
            array_walk($this->namedArguments[$name], [$this, 'addResolvedValue']);
61
        } else {
62 12
            $this->addResolvedValue($this->namedArguments[$name]);
63
        }
64 24
        return true;
65
    }
66 24
    public function resolveParameterByClass(?string $className, bool $variadic): bool
67 24
    {
68 20
        $generator = $this->pullNumericArgument($className);
69 17
        if (!$variadic) {
70
            if (!$generator->valid()) {
71 7
                return false;
72 7
            }
73 7
            $value = $generator->current();
74
            $this->addResolvedValue($value);
75 7
            return true;
76 5
        }
77
        foreach ($generator as &$value) {
78 7
            $this->addResolvedValue($value);
79
        }
80
        return true;
81 35
    }
82
83 35
    public function getResolvedValues(): array
84 23
    {
85 35
        return $this->shouldPushTrailingArguments
86
            ? [...$this->resolvedValues, ...$this->numericArguments]
87
            : $this->resolvedValues;
88
    }
89
90
    /**
91
     * @param null|string $className
92 24
     * @return Generator<void, object>
93
     */
94 24
    private function &pullNumericArgument(?string $className): Generator
95 11
    {
96 11
        foreach ($this->numericArguments as $key => &$value) {
97 11
            if ($className === null || $value instanceof $className) {
98
                unset($this->numericArguments[$key]);
99
                yield $value;
100 21
            }
101
        }
102
    }
103
    /**
104
     * @param array $arguments
105 45
     * @throws InvalidArgumentException
106
     */
107 45
    private function sortArguments(array $arguments): void
108 27
    {
109 20
        foreach ($arguments as $key => &$value) {
110 3
            if (is_int($key)) {
111
                if (!is_object($value)) {
112 17
                    throw new InvalidArgumentException($this->reflection, (string)$key);
113
                }
114 14
                $this->numericArguments[] = &$value;
115
            } else {
116
                $this->namedArguments[$key] = &$value;
117 42
            }
118
        }
119
    }
120
121
    /**
122
     * @return array<string, string>
123
     */
124
    public function getDataToTemplate(): array
125
    {
126
        if ($this->templateData === null) {
127
            $this->prepareDataToTemplate();
128
        }
129
        return $this->templateData;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->templateData could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
130
    }
131
132
    private function prepareDataToTemplate(): void
133
    {
134
        $class = $this->reflection instanceof \ReflectionMethod
135
            ? $this->reflection->getDeclaringClass()
0 ignored issues
show
Bug introduced by
The method getDeclaringClass() does not exist on ReflectionFunctionAbstract. It seems like you code against a sub-type of ReflectionFunctionAbstract such as ReflectionMethod. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

135
            ? $this->reflection->/** @scrutinizer ignore-call */ getDeclaringClass()
Loading history...
136
            : $this->reflection->getClosureScopeClass();
137
        $this->templateData = [
138
            Injector::TEMPLATE_METHOD => $this->reflection->getShortName(),
139
            Injector::TEMPLATE_CLASS => $class === null
140
                ? ''
141
                : $class->getName(),
142
            Injector::TEMPLATE_NAMESPACE => $class === null
143
                ? $this->reflection->getNamespaceName()
144
                : $class->getNamespaceName(),
145
        ];
146
    }
147
}
148