Completed
Push — master ( 8c5203...c6433c )
by Gerrit
23:20
created

ArgumentCompiler::buildRouteArguments()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.0047

Importance

Changes 0
Metric Value
dl 0
loc 35
c 0
b 0
f 0
ccs 14
cts 15
cp 0.9333
rs 9.36
cc 4
nc 5
nop 2
crap 4.0047
1
<?php
2
/**
3
 * Copyright (C) 2018 Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 *
8
 * @license GPL-3.0
9
 *
10
 * @author Gerrit Addiks <[email protected]>
11
 */
12
13
namespace Addiks\SymfonyGenerics\Services;
14
15
use Addiks\SymfonyGenerics\Services\ArgumentCompilerInterface;
16
use Psr\Container\ContainerInterface;
17
use ErrorException;
18
use ReflectionParameter;
19
use ReflectionType;
20
use ReflectionMethod;
21
use Symfony\Component\HttpFoundation\Request;
22
use Webmozart\Assert\Assert;
23
use Addiks\SymfonyGenerics\Services\EntityRepositoryInterface;
24
use ReflectionClass;
25
26
final class ArgumentCompiler implements ArgumentCompilerInterface
27
{
28
29
    /**
30
     * @var ContainerInterface
31
     */
32
    private $container;
33
34
    /**
35
     * @var EntityRepositoryInterface
36
     */
37
    private $entityRepository;
38
39 4
    public function __construct(
40
        ContainerInterface $container,
41
        EntityRepositoryInterface $entityRepository
42
    ) {
43 4
        $this->container = $container;
44 4
        $this->entityRepository = $entityRepository;
45 4
    }
46
47 2
    public function buildRouteArguments(
48
        array $argumentsConfiguration,
49
        Request $request
50
    ): array {
51
        /** @var array<int, mixed> $routeArguments */
52 2
        $routeArguments = array();
53
54 2
        foreach ($argumentsConfiguration as $key => $argumentConfiguration) {
55
            /** @var array|string $argumentConfiguration */
56
57
            /** @var bool $doSet */
58 2
            $doSet = true;
59
60
            /** @var string|null $parameterTypeName */
61 2
            $parameterTypeName = null;
62
63 2
            if (isset($argumentConfiguration['entity-class'])) {
64
                $parameterTypeName = $argumentConfiguration['entity-class'];
65
            }
66
67
            /** @var mixed $argumentValue */
68 2
            $argumentValue = $this->resolveArgumentConfiguration(
69 2
                $argumentConfiguration,
70 2
                $request,
71 2
                $parameterTypeName,
72 2
                $doSet
73
            );
74
75 2
            if ($doSet) {
76 2
                $routeArguments[$key] = $argumentValue;
77
            }
78
        }
79
80 1
        return $routeArguments;
81
    }
82
83 3
    public function buildCallArguments(
84
        ReflectionMethod $methodReflection,
85
        array $argumentsConfiguration,
86
        Request $request
87
    ): array {
88
        /** @var array<int, mixed> $callArguments */
89 3
        $callArguments = array();
90
91 3
        foreach ($methodReflection->getParameters() as $index => $parameterReflection) {
92
            /** @var ReflectionParameter $parameterReflection */
93
94
            /** @var string $parameterName */
95 2
            $parameterName = $parameterReflection->getName();
0 ignored issues
show
Bug introduced by
Consider using $parameterReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
96
97
            /** @var mixed $requestValue */
98 2
            $requestValue = $request->get($parameterName);
99
100 2
            if (isset($argumentsConfiguration[$parameterName])) {
101
                /** @var array|string $argumentConfiguration */
102 2
                $argumentConfiguration = $argumentsConfiguration[$parameterName];
103
104 2
                Assert::true(is_string($argumentConfiguration) || is_array($argumentConfiguration));
105
106
                /** @var bool $doSet */
107 1
                $doSet = true;
108
109
                /** @var string|null $parameterTypeName */
110 1
                $parameterTypeName = null;
111
112 1
                if ($parameterReflection->hasType()) {
113
                    /** @var ReflectionType|null $parameterType */
114 1
                    $parameterType = $parameterReflection->getType();
115
116 1
                    if ($parameterType instanceof ReflectionType) {
0 ignored issues
show
Bug introduced by
The class ReflectionType 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...
117 1
                        $parameterTypeName = $parameterType->__toString();
118
                    }
119
                }
120
121
                /** @var mixed $argumentValue */
122 1
                $argumentValue = $this->resolveArgumentConfiguration(
123 1
                    $argumentConfiguration,
124 1
                    $request,
125 1
                    $parameterTypeName,
126 1
                    $doSet
127
                );
128
129 1
                if ($doSet) {
130 1
                    $callArguments[$index] = $argumentValue;
131
                }
132
133 1
            } elseif (!is_null($requestValue)) {
134 1
                $callArguments[$index] = $requestValue;
135
            }
136
        }
137
138 2
        return $callArguments;
139
    }
140
141
    /**
142
     * @param array|string $argumentConfiguration
143
     *
144
     * @return mixed
145
     */
146 3
    private function resolveArgumentConfiguration(
147
        $argumentConfiguration,
148
        Request $request,
149
        ?string $parameterTypeName,
150
        bool &$doSet
0 ignored issues
show
Unused Code introduced by
The parameter $doSet is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
151
    ) {
152
        /** @var mixed $argumentValue */
153 3
        $argumentValue = null;
154
155 3
        if (is_array($argumentConfiguration)) {
156
            if (isset($argumentConfiguration['id'])) {
157
                $argumentValue = $this->container->get($argumentConfiguration['id']);
158
            }
159
160
        } else {
161 3
            if (is_int(strpos($argumentConfiguration, '::'))) {
162 2
                [$factoryClass, $factoryMethod] = explode('::', $argumentConfiguration);
0 ignored issues
show
Bug introduced by
The variable $factoryClass does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $factoryMethod does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
163
164 2
                if (!empty($factoryClass)) {
165 2
                    if ($factoryClass[0] == '@') {
166
                        /** @var string $factoryServiceId */
167 2
                        $factoryServiceId = substr($factoryClass, 1);
168
169
                        /** @var object|null $factoryObject */
170 2
                        $factoryObject = $this->container->get($factoryServiceId);
171
172 2
                        Assert::methodExists($factoryObject, $factoryMethod, sprintf(
173 2
                            "Did not find service with id '%s' that has a method '%s'!",
174 2
                            $factoryServiceId,
175 2
                            $factoryMethod
176
                        ));
177
178 1
                        $factoryReflection = new ReflectionClass($factoryObject);
179
180
                        /** @var ReflectionMethod $methodReflection */
181 1
                        $methodReflection = $factoryReflection->getMethod($factoryMethod);
182
183 1
                        $callArguments = $this->buildCallArguments(
184 1
                            $methodReflection,
185 1
                            [], # TODO
186 1
                            $request
187
                        );
188
189
                        # Create by factory-service-object
190 1
                        $argumentValue = call_user_func_array([$factoryObject, $factoryMethod], $callArguments);
191
192
                    } else {
193
                        # Create by static factory-method of other class
194 1
                        $argumentValue = call_user_func_array($argumentConfiguration, []);
195
                    }
196
                }
197
198 3
            } elseif ($argumentConfiguration[0] == '$') {
199 3
                $argumentValue = $request->get(substr($argumentConfiguration, 1));
200
201 1
            } elseif ($argumentConfiguration[0] == '@') {
202 1
                $argumentValue = $this->container->get(substr($argumentConfiguration, 1));
203
            }
204
        }
205
206 3
        if (!empty($parameterTypeName)) {
207 1
            if (class_exists($parameterTypeName)) {
208 1
                $argumentValue = $this->entityRepository->findEntity($parameterTypeName, $argumentValue);
209
                # TODO: error handling "not an entty", "entity not found", ...
210
            }
211
        }
212
213 3
        return $argumentValue;
214
    }
215
216
}
217