This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Date: 03.11.16 |
||
4 | * |
||
5 | * @author Portey Vasil <[email protected]> |
||
6 | */ |
||
7 | |||
8 | namespace Youshido\GraphQL\Execution; |
||
9 | |||
10 | |||
11 | use Youshido\GraphQL\Exception\ResolveException; |
||
12 | use Youshido\GraphQL\Execution\Container\Container; |
||
13 | use Youshido\GraphQL\Execution\Context\ExecutionContext; |
||
14 | use Youshido\GraphQL\Execution\Visitor\MaxComplexityQueryVisitor; |
||
15 | use Youshido\GraphQL\Field\Field; |
||
16 | use Youshido\GraphQL\Field\FieldInterface; |
||
17 | use Youshido\GraphQL\Field\InputField; |
||
18 | use Youshido\GraphQL\Parser\Ast\ArgumentValue\InputList as AstInputList; |
||
19 | use Youshido\GraphQL\Parser\Ast\ArgumentValue\InputObject as AstInputObject; |
||
20 | use Youshido\GraphQL\Parser\Ast\ArgumentValue\Literal as AstLiteral; |
||
21 | use Youshido\GraphQL\Parser\Ast\ArgumentValue\VariableReference; |
||
22 | use Youshido\GraphQL\Parser\Ast\Field as AstField; |
||
23 | use Youshido\GraphQL\Parser\Ast\FragmentReference; |
||
24 | use Youshido\GraphQL\Parser\Ast\Interfaces\FieldInterface as AstFieldInterface; |
||
25 | use Youshido\GraphQL\Parser\Ast\Mutation as AstMutation; |
||
26 | use Youshido\GraphQL\Parser\Ast\Query as AstQuery; |
||
27 | use Youshido\GraphQL\Parser\Ast\TypedFragmentReference; |
||
28 | use Youshido\GraphQL\Parser\Parser; |
||
29 | use Youshido\GraphQL\Schema\AbstractSchema; |
||
30 | use Youshido\GraphQL\Type\AbstractType; |
||
31 | use Youshido\GraphQL\Type\Enum\AbstractEnumType; |
||
32 | use Youshido\GraphQL\Type\InputObject\AbstractInputObjectType; |
||
33 | use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType; |
||
34 | use Youshido\GraphQL\Type\ListType\AbstractListType; |
||
35 | use Youshido\GraphQL\Type\Object\AbstractObjectType; |
||
36 | use Youshido\GraphQL\Type\Scalar\AbstractScalarType; |
||
37 | use Youshido\GraphQL\Type\TypeMap; |
||
38 | use Youshido\GraphQL\Type\Union\AbstractUnionType; |
||
39 | use Youshido\GraphQL\Validator\RequestValidator\RequestValidator; |
||
40 | use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidator; |
||
41 | use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidatorInterface; |
||
42 | |||
43 | class Processor |
||
44 | { |
||
45 | |||
46 | const TYPE_NAME_QUERY = '__typename'; |
||
47 | |||
48 | /** @var ExecutionContext */ |
||
49 | protected $executionContext; |
||
50 | |||
51 | /** @var ResolveValidatorInterface */ |
||
52 | protected $resolveValidator; |
||
53 | |||
54 | /** @var array */ |
||
55 | protected $data; |
||
56 | |||
57 | /** @var int */ |
||
58 | protected $maxComplexity; |
||
59 | |||
60 | /** @var array DeferredResult[] */ |
||
61 | protected $deferredResultsLeaf = []; |
||
62 | |||
63 | /** @var array DeferredResult[] */ |
||
64 | protected $deferredResultsComplex = []; |
||
65 | |||
66 | 71 | public function __construct(AbstractSchema $schema) |
|
67 | { |
||
68 | 71 | if (empty($this->executionContext)) { |
|
69 | 71 | $this->executionContext = new ExecutionContext($schema); |
|
70 | 71 | $this->executionContext->setContainer(new Container()); |
|
71 | 71 | } |
|
72 | |||
73 | 71 | $this->resolveValidator = new ResolveValidator($this->executionContext); |
|
74 | 71 | } |
|
75 | |||
76 | 69 | public function processPayload($payload, $variables = [], $reducers = []) |
|
77 | 1 | { |
|
78 | 69 | $this->data = []; |
|
79 | |||
80 | try { |
||
81 | 69 | $this->parseAndCreateRequest($payload, $variables); |
|
82 | |||
83 | 68 | if ($this->maxComplexity) { |
|
84 | 1 | $reducers[] = new MaxComplexityQueryVisitor($this->maxComplexity); |
|
85 | 1 | } |
|
86 | |||
87 | 68 | if ($reducers) { |
|
0 ignored issues
–
show
|
|||
88 | 2 | $reducer = new Reducer(); |
|
89 | 2 | $reducer->reduceQuery($this->executionContext, $reducers); |
|
90 | 2 | } |
|
91 | |||
92 | 68 | foreach ($this->executionContext->getRequest()->getAllOperations() as $query) { |
|
93 | 68 | if ($operationResult = $this->resolveQuery($query)) { |
|
94 | 68 | $this->data = array_replace_recursive($this->data, $operationResult); |
|
95 | 68 | }; |
|
96 | 68 | } |
|
97 | |||
98 | // If the processor found any deferred results, resolve them now. |
||
99 | 68 | if (!empty($this->data) && (!empty($this->deferredResultsLeaf) || !empty($this->deferredResultsComplex))) { |
|
100 | try { |
||
101 | 4 | while ($deferredResolver = array_shift($this->deferredResultsComplex)) { |
|
102 | 4 | $deferredResolver->resolve(); |
|
103 | 4 | } |
|
104 | |||
105 | // Deferred scalar and enum fields should be resolved last to |
||
106 | // pick up as many as possible for a single batch. |
||
107 | 4 | while ($deferredResolver = array_shift($this->deferredResultsLeaf)) { |
|
108 | $deferredResolver->resolve(); |
||
109 | } |
||
110 | 4 | } catch (\Exception $e) { |
|
111 | $this->executionContext->addError($e); |
||
112 | 4 | } finally { |
|
113 | 4 | $this->data = static::unpackDeferredResults($this->data); |
|
114 | 4 | } |
|
115 | 4 | } |
|
116 | |||
117 | 69 | } catch (\Exception $e) { |
|
118 | 5 | $this->executionContext->addError($e); |
|
119 | } |
||
120 | |||
121 | 69 | return $this; |
|
122 | } |
||
123 | |||
124 | /** |
||
125 | * Unpack results stored inside deferred resolvers. |
||
126 | * |
||
127 | * @param mixed $result |
||
128 | * The result ree. |
||
129 | * |
||
130 | * @return mixed |
||
131 | * The unpacked result. |
||
132 | */ |
||
133 | 4 | public static function unpackDeferredResults($result) |
|
134 | { |
||
135 | 4 | while ($result instanceof DeferredResult) { |
|
136 | 4 | $result = $result->result; |
|
137 | 4 | } |
|
138 | |||
139 | 4 | if (is_array($result)) { |
|
140 | 4 | foreach ($result as $key => $value) { |
|
141 | 4 | $result[$key] = static::unpackDeferredResults($value); |
|
142 | 4 | } |
|
143 | 4 | } |
|
144 | |||
145 | 4 | return $result; |
|
146 | } |
||
147 | |||
148 | 68 | protected function resolveQuery(AstQuery $query) |
|
149 | { |
||
150 | 68 | $schema = $this->executionContext->getSchema(); |
|
151 | 68 | $type = $query instanceof AstMutation ? $schema->getMutationType() : $schema->getQueryType(); |
|
152 | 68 | $field = new Field([ |
|
153 | 68 | 'name' => $query instanceof AstMutation ? 'mutation' : 'query', |
|
154 | 'type' => $type |
||
155 | 68 | ]); |
|
156 | |||
157 | 68 | if (self::TYPE_NAME_QUERY == $query->getName()) { |
|
158 | 1 | return [$this->getAlias($query) => $type->getName()]; |
|
159 | } |
||
160 | |||
161 | 68 | $this->resolveValidator->assetTypeHasField($type, $query); |
|
162 | 68 | $value = $this->resolveField($field, $query); |
|
163 | |||
164 | 68 | return [$this->getAlias($query) => $value]; |
|
165 | } |
||
166 | |||
167 | 68 | protected function resolveField(FieldInterface $field, AstFieldInterface $ast, $parentValue = null, $fromObject = false) |
|
168 | { |
||
169 | try { |
||
170 | /** @var AbstractObjectType $type */ |
||
171 | 68 | $type = $field->getType(); |
|
172 | 68 | $nonNullType = $type->getNullableType(); |
|
173 | |||
174 | 68 | if (self::TYPE_NAME_QUERY == $ast->getName()) { |
|
175 | 2 | return $nonNullType->getName(); |
|
176 | } |
||
177 | |||
178 | 68 | $this->resolveValidator->assetTypeHasField($nonNullType, $ast); |
|
179 | |||
180 | 68 | $targetField = $this->executionContext->getField($nonNullType, $ast->getName()); |
|
0 ignored issues
–
show
$nonNullType of type object<Youshido\GraphQL\Type\AbstractType> is not a sub-type of object<Youshido\GraphQL\...ect\AbstractObjectType> . It seems like you assume a child class of the class Youshido\GraphQL\Type\AbstractType to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
181 | |||
182 | 68 | $this->prepareAstArguments($targetField, $ast, $this->executionContext->getRequest()); |
|
183 | 67 | $this->resolveValidator->assertValidArguments($targetField, $ast, $this->executionContext->getRequest()); |
|
184 | |||
185 | 62 | switch ($kind = $targetField->getType()->getNullableType()->getKind()) { |
|
186 | 62 | case TypeMap::KIND_ENUM: |
|
187 | 62 | case TypeMap::KIND_SCALAR: |
|
188 | 54 | if ($ast instanceof AstQuery && $ast->hasFields()) { |
|
189 | 2 | throw new ResolveException(sprintf('You can\'t specify fields for scalar type "%s"', $targetField->getType()->getNullableType()->getName()), $ast->getLocation()); |
|
190 | } |
||
191 | |||
192 | 54 | return $this->resolveScalar($targetField, $ast, $parentValue); |
|
193 | |||
194 | 45 | View Code Duplication | case TypeMap::KIND_OBJECT: |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
195 | /** @var $type AbstractObjectType */ |
||
196 | 31 | if (!$ast instanceof AstQuery) { |
|
197 | 1 | throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation()); |
|
198 | } |
||
199 | |||
200 | 31 | return $this->resolveObject($targetField, $ast, $parentValue); |
|
201 | |||
202 | 31 | case TypeMap::KIND_LIST: |
|
203 | 28 | return $this->resolveList($targetField, $ast, $parentValue); |
|
204 | |||
205 | 6 | case TypeMap::KIND_UNION: |
|
206 | 6 | View Code Duplication | case TypeMap::KIND_INTERFACE: |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
207 | 6 | if (!$ast instanceof AstQuery) { |
|
208 | throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation()); |
||
209 | } |
||
210 | |||
211 | 6 | return $this->resolveComposite($targetField, $ast, $parentValue); |
|
212 | |||
213 | default: |
||
214 | throw new ResolveException(sprintf('Resolving type with kind "%s" not supported', $kind)); |
||
215 | } |
||
216 | 17 | } catch (\Exception $e) { |
|
217 | 17 | $this->executionContext->addError($e); |
|
218 | |||
219 | 17 | if ($fromObject) { |
|
220 | 4 | throw $e; |
|
221 | } |
||
222 | |||
223 | 15 | return null; |
|
224 | } |
||
225 | } |
||
226 | |||
227 | 68 | private function prepareAstArguments(FieldInterface $field, AstFieldInterface $query, Request $request) |
|
228 | { |
||
229 | 68 | foreach ($query->getArguments() as $astArgument) { |
|
230 | 38 | if ($field->hasArgument($astArgument->getName())) { |
|
231 | 38 | $argumentType = $field->getArgument($astArgument->getName())->getType()->getNullableType(); |
|
232 | |||
233 | 38 | $astArgument->setValue($this->prepareArgumentValue($astArgument->getValue(), $argumentType, $request)); |
|
234 | 37 | } |
|
235 | 67 | } |
|
236 | 67 | } |
|
237 | |||
238 | 38 | private function prepareArgumentValue($argumentValue, AbstractType $argumentType, Request $request) |
|
239 | { |
||
240 | 38 | switch ($argumentType->getKind()) { |
|
241 | 38 | case TypeMap::KIND_LIST: |
|
242 | /** @var $argumentType AbstractListType */ |
||
243 | 12 | $result = []; |
|
244 | 12 | if ($argumentValue instanceof AstInputList || is_array($argumentValue)) { |
|
245 | 9 | $list = is_array($argumentValue) ? $argumentValue : $argumentValue->getValue(); |
|
246 | 9 | foreach ($list as $item) { |
|
247 | 9 | $result[] = $this->prepareArgumentValue($item, $argumentType->getItemType()->getNullableType(), $request); |
|
248 | 9 | } |
|
249 | 9 | } else { |
|
250 | 3 | if ($argumentValue instanceof VariableReference) { |
|
251 | 3 | return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request); |
|
252 | } |
||
253 | } |
||
254 | |||
255 | 9 | return $result; |
|
256 | |||
257 | 37 | case TypeMap::KIND_INPUT_OBJECT: |
|
258 | /** @var $argumentType AbstractInputObjectType */ |
||
259 | 6 | $result = []; |
|
260 | 6 | if ($argumentValue instanceof AstInputObject) { |
|
261 | 5 | foreach ($argumentType->getFields() as $field) { |
|
262 | /** @var $field Field */ |
||
263 | 5 | if ($field->getConfig()->has('defaultValue')) { |
|
264 | 1 | $result[$field->getName()] = $field->getType()->getNullableType()->parseInputValue($field->getConfig()->get('defaultValue')); |
|
265 | 1 | } |
|
266 | 5 | } |
|
267 | 5 | foreach ($argumentValue->getValue() as $key => $item) { |
|
268 | 5 | if ($argumentType->hasField($key)) { |
|
269 | 5 | $result[$key] = $this->prepareArgumentValue($item, $argumentType->getField($key)->getType()->getNullableType(), $request); |
|
270 | 5 | } else { |
|
271 | $result[$key] = $item; |
||
272 | } |
||
273 | 5 | } |
|
274 | 5 | } else { |
|
275 | 2 | if ($argumentValue instanceof VariableReference) { |
|
276 | return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request); |
||
277 | } else { |
||
278 | 2 | if (is_array($argumentValue)) { |
|
279 | 1 | return $argumentValue; |
|
280 | } |
||
281 | } |
||
282 | } |
||
283 | |||
284 | 6 | return $result; |
|
285 | |||
286 | 36 | case TypeMap::KIND_SCALAR: |
|
287 | 36 | case TypeMap::KIND_ENUM: |
|
288 | /** @var $argumentValue AstLiteral|VariableReference */ |
||
289 | 36 | if ($argumentValue instanceof VariableReference) { |
|
290 | 7 | return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request); |
|
291 | } else { |
||
292 | 31 | if ($argumentValue instanceof AstLiteral) { |
|
293 | 19 | return $argumentValue->getValue(); |
|
294 | } else { |
||
295 | 13 | return $argumentValue; |
|
296 | } |
||
297 | } |
||
298 | } |
||
299 | |||
300 | throw new ResolveException('Argument type not supported'); |
||
301 | } |
||
302 | |||
303 | 9 | private function getVariableReferenceArgumentValue(VariableReference $variableReference, AbstractType $argumentType, Request $request) |
|
304 | { |
||
305 | 9 | $variable = $variableReference->getVariable(); |
|
306 | 9 | if ($argumentType->getKind() === TypeMap::KIND_LIST) { |
|
307 | if ( |
||
308 | 3 | (!$variable->isArray() && !is_array($variable->getValue())) || |
|
309 | 3 | ($variable->getTypeName() !== $argumentType->getNamedType()->getNullableType()->getName()) || |
|
310 | 3 | ($argumentType->getNamedType()->getKind() === TypeMap::KIND_NON_NULL && $variable->isArrayElementNullable()) |
|
311 | 3 | ) { |
|
312 | 1 | throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getNamedType()->getNullableType()->getName()), $variable->getLocation()); |
|
313 | } |
||
314 | 3 | } else { |
|
315 | 7 | if ($variable->getTypeName() !== $argumentType->getName()) { |
|
316 | 1 | throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getName()), $variable->getLocation()); |
|
317 | } |
||
318 | } |
||
319 | |||
320 | 8 | $requestValue = $request->getVariable($variable->getName()); |
|
321 | 8 | if ((null === $requestValue && $variable->isNullable()) && !$request->hasVariable($variable->getName())) { |
|
322 | throw new ResolveException(sprintf('Variable "%s" does not exist in request', $variable->getName()), $variable->getLocation()); |
||
323 | } |
||
324 | |||
325 | 8 | return $requestValue; |
|
326 | } |
||
327 | |||
328 | |||
329 | /** |
||
330 | * @param FieldInterface $field |
||
331 | * @param AbstractObjectType $type |
||
332 | * @param AstFieldInterface $ast |
||
333 | * @param $resolvedValue |
||
334 | * @return array |
||
335 | */ |
||
336 | 40 | private function collectResult(FieldInterface $field, AbstractObjectType $type, $ast, $resolvedValue) |
|
337 | { |
||
338 | 40 | $result = []; |
|
339 | |||
340 | 40 | foreach ($ast->getFields() as $astField) { |
|
341 | 40 | switch (true) { |
|
342 | 40 | case $astField instanceof TypedFragmentReference: |
|
343 | 2 | $astName = $astField->getTypeName(); |
|
344 | 2 | $typeName = $type->getName(); |
|
345 | |||
346 | 2 | View Code Duplication | if ($typeName !== $astName) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
347 | 2 | foreach ($type->getInterfaces() as $interface) { |
|
348 | 1 | if ($interface->getName() === $astName) { |
|
349 | $result = array_replace_recursive($result, $this->collectResult($field, $type, $astField, $resolvedValue)); |
||
0 ignored issues
–
show
$astField is of type object<Youshido\GraphQL\...TypedFragmentReference> , but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
350 | |||
351 | break; |
||
352 | } |
||
353 | 2 | } |
|
354 | |||
355 | 2 | continue 2; |
|
356 | } |
||
357 | |||
358 | 2 | $result = array_replace_recursive($result, $this->collectResult($field, $type, $astField, $resolvedValue)); |
|
0 ignored issues
–
show
$astField is of type object<Youshido\GraphQL\...TypedFragmentReference> , but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
359 | |||
360 | 2 | break; |
|
361 | |||
362 | 40 | case $astField instanceof FragmentReference: |
|
363 | 8 | $astFragment = $this->executionContext->getRequest()->getFragment($astField->getName()); |
|
364 | 8 | $astFragmentModel = $astFragment->getModel(); |
|
365 | 8 | $typeName = $type->getName(); |
|
366 | |||
367 | 8 | View Code Duplication | if ($typeName !== $astFragmentModel) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
368 | 2 | foreach ($type->getInterfaces() as $interface) { |
|
369 | 1 | if ($interface->getName() === $astFragmentModel) { |
|
370 | 1 | $result = array_replace_recursive($result, $this->collectResult($field, $type, $astFragment, $resolvedValue)); |
|
0 ignored issues
–
show
$astFragment is of type object<Youshido\GraphQL\Parser\Ast\Fragment>|null , but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
371 | |||
372 | 1 | break; |
|
373 | } |
||
374 | 2 | } |
|
375 | |||
376 | 2 | continue 2; |
|
377 | } |
||
378 | |||
379 | 7 | $result = array_replace_recursive($result, $this->collectResult($field, $type, $astFragment, $resolvedValue)); |
|
0 ignored issues
–
show
$astFragment is of type object<Youshido\GraphQL\Parser\Ast\Fragment>|null , but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
380 | |||
381 | 7 | break; |
|
382 | |||
383 | 39 | default: |
|
384 | 39 | $result = array_replace_recursive($result, [$this->getAlias($astField) => $this->resolveField($field, $astField, $resolvedValue, true)]); |
|
385 | 39 | } |
|
386 | 40 | } |
|
387 | |||
388 | 40 | return $result; |
|
389 | } |
||
390 | |||
391 | /** |
||
392 | * Apply post-process callbacks to all deferred resolvers. |
||
393 | */ |
||
394 | 62 | protected function deferredResolve($resolvedValue, FieldInterface $field, callable $callback) { |
|
395 | 62 | if ($resolvedValue instanceof DeferredResolverInterface) { |
|
396 | $deferredResult = new DeferredResult($resolvedValue, function ($resolvedValue) use ($field, $callback) { |
||
397 | // Allow nested deferred resolvers. |
||
398 | 4 | return $this->deferredResolve($resolvedValue, $field, $callback); |
|
399 | 4 | }); |
|
400 | |||
401 | // Whenever we stumble upon a deferred resolver, add it to the queue |
||
402 | // to be resolved later. |
||
403 | 4 | $type = $field->getType()->getNamedType(); |
|
404 | 4 | if ($type instanceof AbstractScalarType || $type instanceof AbstractEnumType) { |
|
405 | array_push($this->deferredResultsLeaf, $deferredResult); |
||
406 | } else { |
||
407 | 4 | array_push($this->deferredResultsComplex, $deferredResult); |
|
408 | } |
||
409 | |||
410 | 4 | return $deferredResult; |
|
411 | } |
||
412 | // For simple values, invoke the callback immediately. |
||
413 | 62 | return $callback($resolvedValue); |
|
414 | } |
||
415 | |||
416 | 55 | protected function resolveScalar(FieldInterface $field, AstFieldInterface $ast, $parentValue) |
|
417 | { |
||
418 | 55 | $resolvedValue = $this->doResolve($field, $ast, $parentValue); |
|
419 | return $this->deferredResolve($resolvedValue, $field, function($resolvedValue) use ($field, $ast, $parentValue) { |
||
420 | 55 | $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue); |
|
421 | |||
422 | /** @var AbstractScalarType $type */ |
||
423 | 54 | $type = $field->getType()->getNullableType(); |
|
424 | |||
425 | 54 | return $type->serialize($resolvedValue); |
|
426 | 55 | }); |
|
427 | } |
||
428 | |||
429 | 28 | protected function resolveList(FieldInterface $field, AstFieldInterface $ast, $parentValue) |
|
430 | { |
||
431 | /** @var AstQuery $ast */ |
||
432 | 28 | $resolvedValue = $this->doResolve($field, $ast, $parentValue); |
|
433 | |||
434 | return $this->deferredResolve($resolvedValue, $field, function ($resolvedValue) use ($field, $ast, $parentValue) { |
||
435 | 28 | $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue); |
|
436 | |||
437 | 26 | if (null === $resolvedValue) { |
|
438 | 8 | return null; |
|
439 | } |
||
440 | |||
441 | /** @var AbstractListType $type */ |
||
442 | 25 | $type = $field->getType()->getNullableType(); |
|
443 | 25 | $itemType = $type->getNamedType(); |
|
444 | |||
445 | 25 | $fakeAst = clone $ast; |
|
446 | 25 | if ($fakeAst instanceof AstQuery) { |
|
447 | 24 | $fakeAst->setArguments([]); |
|
448 | 24 | } |
|
449 | |||
450 | 25 | $fakeField = new Field([ |
|
451 | 25 | 'name' => $field->getName(), |
|
452 | 25 | 'type' => $itemType, |
|
453 | 25 | 'args' => $field->getArguments(), |
|
454 | 25 | ]); |
|
455 | |||
456 | 25 | $result = []; |
|
457 | 25 | foreach ($resolvedValue as $resolvedValueItem) { |
|
458 | try { |
||
459 | $fakeField->getConfig()->set('resolve', function () use ($resolvedValueItem) { |
||
460 | 24 | return $resolvedValueItem; |
|
461 | 24 | }); |
|
462 | |||
463 | 24 | switch ($itemType->getNullableType()->getKind()) { |
|
464 | 24 | case TypeMap::KIND_ENUM: |
|
465 | 24 | case TypeMap::KIND_SCALAR: |
|
466 | 6 | $value = $this->resolveScalar($fakeField, $fakeAst, $resolvedValueItem); |
|
467 | |||
468 | 5 | break; |
|
469 | |||
470 | |||
471 | 20 | case TypeMap::KIND_OBJECT: |
|
472 | 17 | $value = $this->resolveObject($fakeField, $fakeAst, $resolvedValueItem); |
|
473 | |||
474 | 17 | break; |
|
475 | |||
476 | 4 | case TypeMap::KIND_UNION: |
|
477 | 4 | case TypeMap::KIND_INTERFACE: |
|
478 | 4 | $value = $this->resolveComposite($fakeField, $fakeAst, $resolvedValueItem); |
|
479 | |||
480 | 4 | break; |
|
481 | |||
482 | default: |
||
483 | $value = null; |
||
484 | 23 | } |
|
485 | 24 | } catch (\Exception $e) { |
|
486 | 1 | $this->executionContext->addError($e); |
|
487 | |||
488 | 1 | $value = null; |
|
489 | } |
||
490 | |||
491 | 24 | $result[] = $value; |
|
492 | 25 | } |
|
493 | |||
494 | 25 | return $result; |
|
495 | 28 | }); |
|
496 | } |
||
497 | |||
498 | 41 | protected function resolveObject(FieldInterface $field, AstFieldInterface $ast, $parentValue, $fromUnion = false) |
|
499 | { |
||
500 | 41 | $resolvedValue = $parentValue; |
|
501 | 41 | if (!$fromUnion) { |
|
502 | 35 | $resolvedValue = $this->doResolve($field, $ast, $parentValue); |
|
503 | 35 | } |
|
504 | |||
505 | return $this->deferredResolve($resolvedValue, $field, function ($resolvedValue) use ($field, $ast, $parentValue) { |
||
506 | 41 | $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue); |
|
507 | |||
508 | 41 | if (null === $resolvedValue) { |
|
509 | 8 | return null; |
|
510 | } |
||
511 | /** @var AbstractObjectType $type */ |
||
512 | 40 | $type = $field->getType()->getNullableType(); |
|
513 | |||
514 | try { |
||
515 | 40 | return $this->collectResult($field, $type, $ast, $resolvedValue); |
|
516 | 4 | } catch (\Exception $e) { |
|
517 | 4 | return null; |
|
518 | } |
||
519 | 41 | }); |
|
520 | } |
||
521 | |||
522 | 8 | protected function resolveComposite(FieldInterface $field, AstFieldInterface $ast, $parentValue) |
|
523 | { |
||
524 | /** @var AstQuery $ast */ |
||
525 | 8 | $resolvedValue = $this->doResolve($field, $ast, $parentValue); |
|
526 | 8 | return $this->deferredResolve($resolvedValue, $field, function ($resolvedValue) use ($field, $ast, $parentValue) { |
|
527 | 8 | $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue); |
|
528 | |||
529 | 8 | if (null === $resolvedValue) { |
|
530 | return null; |
||
531 | } |
||
532 | |||
533 | /** @var AbstractUnionType $type */ |
||
534 | 8 | $type = $field->getType()->getNullableType(); |
|
535 | 8 | $resolveInfo = new ResolveInfo( |
|
536 | 8 | $field, |
|
537 | 8 | $ast instanceof AstQuery ? $ast->getFields() : [], |
|
538 | 8 | $this->executionContext |
|
539 | 8 | ); |
|
540 | 8 | $resolvedType = $type->resolveType($resolvedValue, $resolveInfo); |
|
0 ignored issues
–
show
The call to
AbstractUnionType::resolveType() has too many arguments starting with $resolveInfo .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
541 | |||
542 | 8 | if (!$resolvedType) { |
|
543 | throw new ResolveException('Resolving function must return type'); |
||
544 | } |
||
545 | |||
546 | 8 | if ($type instanceof AbstractInterfaceType) { |
|
547 | 6 | $this->resolveValidator->assertTypeImplementsInterface($resolvedType, $type); |
|
548 | 6 | } else { |
|
549 | 2 | $this->resolveValidator->assertTypeInUnionTypes($resolvedType, $type); |
|
550 | } |
||
551 | |||
552 | 8 | $fakeField = new Field([ |
|
553 | 8 | 'name' => $field->getName(), |
|
554 | 8 | 'type' => $resolvedType, |
|
555 | 8 | 'args' => $field->getArguments(), |
|
556 | 8 | ]); |
|
557 | |||
558 | 8 | return $this->resolveObject($fakeField, $ast, $resolvedValue, true); |
|
559 | 8 | }); |
|
560 | } |
||
561 | |||
562 | 69 | protected function parseAndCreateRequest($payload, $variables = []) |
|
563 | { |
||
564 | 69 | if (empty($payload)) { |
|
565 | 1 | throw new \InvalidArgumentException('Must provide an operation.'); |
|
566 | } |
||
567 | |||
568 | 69 | $parser = new Parser(); |
|
569 | 69 | $request = new Request($parser->parse($payload), $variables); |
|
570 | |||
571 | 69 | (new RequestValidator())->validate($request); |
|
572 | |||
573 | 68 | $this->executionContext->setRequest($request); |
|
574 | 68 | } |
|
575 | |||
576 | 62 | protected function doResolve(FieldInterface $field, AstFieldInterface $ast, $parentValue = null) |
|
577 | { |
||
578 | /** @var AstQuery|AstField $ast */ |
||
579 | 62 | $arguments = $this->parseArgumentsValues($field, $ast); |
|
580 | 62 | $astFields = $ast instanceof AstQuery ? $ast->getFields() : []; |
|
581 | |||
582 | 62 | return $field->resolve($parentValue, $arguments, $this->createResolveInfo($field, $astFields)); |
|
583 | } |
||
584 | |||
585 | 62 | protected function parseArgumentsValues(FieldInterface $field, AstFieldInterface $ast) |
|
586 | { |
||
587 | 62 | $values = []; |
|
588 | 62 | $defaults = []; |
|
589 | |||
590 | 62 | foreach ($field->getArguments() as $argument) { |
|
591 | /** @var $argument InputField */ |
||
592 | 45 | if ($argument->getConfig()->has('defaultValue')) { |
|
593 | 9 | $defaults[$argument->getName()] = $argument->getConfig()->getDefaultValue(); |
|
594 | 9 | } |
|
595 | 62 | } |
|
596 | |||
597 | 62 | foreach ($ast->getArguments() as $astArgument) { |
|
598 | 34 | $argument = $field->getArgument($astArgument->getName()); |
|
599 | 34 | $argumentType = $argument->getType()->getNullableType(); |
|
600 | |||
601 | 34 | $values[$argument->getName()] = $argumentType->parseValue($astArgument->getValue()); |
|
602 | |||
603 | 34 | if (isset($defaults[$argument->getName()])) { |
|
604 | 3 | unset($defaults[$argument->getName()]); |
|
605 | 3 | } |
|
606 | 62 | } |
|
607 | |||
608 | 62 | return array_merge($values, $defaults); |
|
609 | } |
||
610 | |||
611 | 68 | private function getAlias(AstFieldInterface $ast) |
|
612 | { |
||
613 | 68 | return $ast->getAlias() ?: $ast->getName(); |
|
614 | } |
||
615 | |||
616 | 62 | protected function createResolveInfo(FieldInterface $field, array $astFields) |
|
617 | { |
||
618 | 62 | return new ResolveInfo($field, $astFields, $this->executionContext); |
|
619 | } |
||
620 | |||
621 | |||
622 | /** |
||
623 | * You can access ExecutionContext to check errors and inject dependencies |
||
624 | * |
||
625 | * @return ExecutionContext |
||
626 | */ |
||
627 | 11 | public function getExecutionContext() |
|
628 | { |
||
629 | 11 | return $this->executionContext; |
|
630 | } |
||
631 | |||
632 | 68 | public function getResponseData() |
|
633 | { |
||
634 | 68 | $result = []; |
|
635 | |||
636 | 68 | if (!empty($this->data)) { |
|
637 | 67 | $result['data'] = $this->data; |
|
638 | 67 | } |
|
639 | |||
640 | 68 | if ($this->executionContext->hasErrors()) { |
|
641 | 19 | $result['errors'] = $this->executionContext->getErrorsArray(); |
|
642 | 19 | } |
|
643 | |||
644 | 68 | return $result; |
|
645 | } |
||
646 | |||
647 | /** |
||
648 | * @return int |
||
649 | */ |
||
650 | public function getMaxComplexity() |
||
651 | { |
||
652 | return $this->maxComplexity; |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * @param int $maxComplexity |
||
657 | */ |
||
658 | 1 | public function setMaxComplexity($maxComplexity) |
|
659 | { |
||
660 | 1 | $this->maxComplexity = $maxComplexity; |
|
661 | 1 | } |
|
662 | |||
663 | } |
||
664 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.