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\RDMBundle\Mapping; |
||
14 | |||
15 | use Addiks\RDMBundle\Mapping\CallDefinitionInterface; |
||
16 | use Addiks\RDMBundle\Mapping\MappingInterface; |
||
17 | use BackedEnum; |
||
0 ignored issues
–
show
|
|||
18 | use Webmozart\Assert\Assert; |
||
19 | use Addiks\RDMBundle\Hydration\HydrationContextInterface; |
||
20 | use Symfony\Component\DependencyInjection\ContainerInterface; |
||
21 | use Doctrine\ORM\Exception\ORMException; |
||
22 | use Doctrine\Common\Util\ClassUtils; |
||
23 | use ArgumentCountError; |
||
24 | |||
25 | final class CallDefinition implements CallDefinitionInterface |
||
26 | { |
||
27 | |||
28 | /** |
||
29 | * @var ContainerInterface |
||
30 | */ |
||
31 | private $container; |
||
32 | |||
33 | /** |
||
34 | * @var string|null |
||
35 | */ |
||
36 | private $objectReference; |
||
37 | |||
38 | /** |
||
39 | * @var string |
||
40 | */ |
||
41 | private $routineName; |
||
42 | |||
43 | /** |
||
44 | * @var array<MappingInterface> |
||
45 | */ |
||
46 | private $argumentMappings = array(); |
||
47 | |||
48 | /** |
||
49 | * @var bool |
||
50 | */ |
||
51 | private $isStaticCall; |
||
52 | |||
53 | /** |
||
54 | * @var string |
||
55 | */ |
||
56 | private $origin; |
||
57 | 13 | ||
58 | public function __construct( |
||
59 | ContainerInterface $container, |
||
60 | string $routineName, |
||
61 | string $objectReference = null, |
||
62 | array $argumentMappings = array(), |
||
63 | bool $isStaticCall = false, |
||
64 | string $origin = "unknown" |
||
65 | 13 | ) { |
|
66 | 13 | $this->routineName = $routineName; |
|
67 | 13 | $this->objectReference = $objectReference; |
|
68 | 13 | $this->isStaticCall = $isStaticCall; |
|
69 | 13 | $this->container = $container; |
|
70 | $this->origin = $origin; |
||
71 | 13 | ||
72 | foreach ($argumentMappings as $argumentMapping) { |
||
73 | /** @var MappingInterface $argumentMapping */ |
||
74 | 13 | ||
75 | Assert::isInstanceOf($argumentMapping, MappingInterface::class); |
||
76 | 13 | ||
77 | $this->argumentMappings[] = $argumentMapping; |
||
78 | } |
||
79 | } |
||
80 | 1 | ||
81 | public function __sleep(): array |
||
82 | { |
||
83 | 1 | return [ |
|
84 | 'objectReference', |
||
85 | 'routineName', |
||
86 | 'argumentMappings', |
||
87 | 'isStaticCall', |
||
88 | 'origin', |
||
89 | ]; |
||
90 | } |
||
91 | 7 | ||
92 | public function execute( |
||
93 | HydrationContextInterface $context, |
||
94 | array $dataFromAdditionalColumns |
||
95 | ) { |
||
96 | 7 | /** @var mixed $result */ |
|
97 | $result = null; |
||
98 | |||
99 | 7 | /** @var null|object|string $callee */ |
|
100 | $callee = $this->resolveCallee((string)$this->objectReference, $context); |
||
101 | 7 | ||
102 | if ($this->isStaticCall && is_object($callee)) { |
||
103 | $callee = get_class($callee); |
||
104 | } |
||
105 | |||
106 | 7 | /** @var array<mixed> $arguments */ |
|
107 | $arguments = $this->resolveArguments( |
||
108 | $context, |
||
109 | $dataFromAdditionalColumns |
||
110 | ); |
||
111 | |||
112 | 7 | try { |
|
113 | if (is_null($callee) && !empty($this->objectReference)) { |
||
114 | $result = null; |
||
115 | 7 | ||
116 | 1 | } elseif (is_null($callee)) { |
|
117 | $result = call_user_func_array($this->routineName, $arguments); |
||
118 | 6 | ||
119 | 1 | } elseif (is_string($callee)) { |
|
120 | $result = call_user_func_array("{$callee}::{$this->routineName}", $arguments); |
||
121 | |||
122 | 7 | } elseif (property_exists($callee, $this->routineName) && !method_exists($callee, $this->routineName)) { |
|
123 | $result = $callee->{$this->routineName}; |
||
124 | |||
125 | } elseif ($callee instanceof BackedEnum && $this->routineName === '__toString') { |
||
126 | $result = $callee->value; |
||
127 | |||
128 | } else { |
||
129 | $result = call_user_func_array([$callee, $this->routineName], $arguments); |
||
130 | } |
||
131 | |||
132 | } catch (ArgumentCountError $exception) { |
||
133 | /** @var string $calleeDescription */ |
||
134 | $calleeDescription = ""; |
||
135 | |||
136 | if (is_object($callee)) { |
||
137 | $calleeDescription = get_class($callee); |
||
138 | |||
139 | if (class_exists(ClassUtils::class)) { |
||
140 | $calleeDescription = ClassUtils::getRealClass($calleeDescription); |
||
141 | } |
||
142 | |||
143 | } elseif (is_string($callee)) { |
||
144 | $calleeDescription = $callee; |
||
145 | } |
||
146 | |||
147 | if (!empty($calleeDescription)) { |
||
148 | $calleeDescription .= $this->isStaticCall ?'::' :'->'; |
||
149 | } |
||
150 | |||
151 | throw new ORMException(sprintf( |
||
152 | "Wrong number of arguments passed to routine '%s%s' in %s: %s", |
||
153 | 7 | $calleeDescription, |
|
154 | $this->routineName, |
||
155 | $this->origin, |
||
156 | 1 | $exception->getMessage() |
|
157 | ), 0, $exception); |
||
158 | 1 | } |
|
159 | |||
160 | return $result; |
||
161 | 1 | } |
|
162 | |||
163 | 1 | public function getObjectReference(): ?string |
|
164 | { |
||
165 | return $this->objectReference; |
||
166 | 1 | } |
|
167 | |||
168 | 1 | public function getRoutineName(): string |
|
169 | { |
||
170 | return $this->routineName; |
||
171 | 1 | } |
|
172 | |||
173 | 1 | public function getArgumentMappings(): array |
|
174 | { |
||
175 | return $this->argumentMappings; |
||
176 | } |
||
177 | |||
178 | public function isStaticCall(): bool |
||
179 | { |
||
180 | return $this->isStaticCall; |
||
181 | 7 | } |
|
182 | |||
183 | /** |
||
184 | * (This return type should be nullable, but there seems to be a bug in current version psalm preventing it.) |
||
185 | * |
||
186 | 7 | * @return object|string|null |
|
187 | */ |
||
188 | 7 | private function resolveCallee( |
|
189 | string $objectReference, |
||
190 | 6 | HydrationContextInterface $context |
|
191 | ) { |
||
192 | 6 | /** @var object|string $callee */ |
|
193 | 1 | $callee = null; |
|
194 | |||
195 | if (!empty($objectReference)) { |
||
196 | 6 | /** @var array<mixed> $hydrationStack */ |
|
197 | $hydrationStack = $context->getObjectHydrationStack(); |
||
198 | |||
199 | 6 | if ($objectReference[0] === '$') { |
|
200 | 2 | $objectReference = substr($objectReference, 1); |
|
201 | } |
||
202 | 4 | ||
203 | 1 | if (in_array($objectReference, ['root', 'entity'])) { |
|
204 | $callee = $context->getEntity(); |
||
205 | 3 | ||
206 | } elseif (in_array($objectReference, ['self', 'this'])) { |
||
207 | 2 | $callee = $hydrationStack[count($hydrationStack) - 1]; |
|
208 | |||
209 | 2 | } elseif (in_array($objectReference, ['parent'])) { |
|
210 | $callee = $hydrationStack[count($hydrationStack) - 2]; |
||
211 | 1 | ||
212 | 1 | } elseif ($objectReference[0] === '@') { |
|
213 | /** @var string $serviceId */ |
||
214 | $serviceId = substr($objectReference, 1); |
||
215 | |||
216 | $callee = $this->container->get($serviceId); |
||
217 | |||
218 | } elseif (class_exists($objectReference)) { |
||
219 | 7 | $callee = $objectReference; |
|
220 | |||
221 | } elseif ($context->hasRegisteredValue($objectReference)) { |
||
222 | $callee = $context->getRegisteredValue($objectReference); |
||
223 | } |
||
224 | } |
||
225 | |||
226 | return $callee; |
||
227 | 7 | } |
|
228 | |||
229 | /** |
||
230 | * @param array<scalar> $dataFromAdditionalColumns |
||
231 | * |
||
232 | 7 | * @return array<mixed> |
|
233 | */ |
||
234 | 7 | private function resolveArguments( |
|
235 | HydrationContextInterface $context, |
||
236 | array $dataFromAdditionalColumns |
||
237 | ): array { |
||
238 | /** @var array<mixed> $arguments */ |
||
239 | 7 | $arguments = array(); |
|
240 | |||
241 | if (array_key_exists('', $dataFromAdditionalColumns)) { |
||
242 | 7 | $arguments[] = $dataFromAdditionalColumns['']; |
|
243 | unset($dataFromAdditionalColumns['']); |
||
244 | } |
||
245 | |||
246 | foreach ($this->argumentMappings as $argumentMapping) { |
||
247 | /** @var MappingInterface $argumentMapping */ |
||
248 | 7 | ||
249 | $arguments[] = $argumentMapping->resolveValue( |
||
250 | $context, |
||
251 | 1 | $dataFromAdditionalColumns |
|
252 | 1 | ); |
|
253 | } |
||
254 | |||
255 | return $arguments; |
||
256 | } |
||
257 | |||
258 | public function wakeUpCall(ContainerInterface $container): void { |
||
259 | $this->container = $container; |
||
260 | } |
||
261 | } |
||
262 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths