1 | <?php |
||||||
2 | |||||||
3 | namespace Digia\GraphQL\Execution; |
||||||
4 | |||||||
5 | use Digia\GraphQL\Error\GraphQLException; |
||||||
6 | use Digia\GraphQL\Error\InvalidTypeException; |
||||||
7 | use Digia\GraphQL\Error\InvariantException; |
||||||
8 | use Digia\GraphQL\Language\Node\ArgumentNode; |
||||||
9 | use Digia\GraphQL\Language\Node\ArgumentsAwareInterface; |
||||||
10 | use Digia\GraphQL\Language\Node\NameAwareInterface; |
||||||
11 | use Digia\GraphQL\Language\Node\NodeInterface; |
||||||
12 | use Digia\GraphQL\Language\Node\NullValueNode; |
||||||
13 | use Digia\GraphQL\Language\Node\VariableDefinitionNode; |
||||||
14 | use Digia\GraphQL\Language\Node\VariableNode; |
||||||
15 | use Digia\GraphQL\Schema\Schema; |
||||||
16 | use Digia\GraphQL\Type\Coercer\CoercingException; |
||||||
17 | use Digia\GraphQL\Type\Definition\Directive; |
||||||
18 | use Digia\GraphQL\Type\Definition\EnumType; |
||||||
19 | use Digia\GraphQL\Type\Definition\EnumValue; |
||||||
20 | use Digia\GraphQL\Type\Definition\Field; |
||||||
21 | use Digia\GraphQL\Type\Definition\InputObjectType; |
||||||
22 | use Digia\GraphQL\Type\Definition\ListType; |
||||||
23 | use Digia\GraphQL\Type\Definition\NonNullType; |
||||||
24 | use Digia\GraphQL\Type\Definition\ScalarType; |
||||||
25 | use Digia\GraphQL\Type\Definition\TypeInterface; |
||||||
26 | use Digia\GraphQL\Type\Definition\WrappingTypeInterface; |
||||||
27 | use Digia\GraphQL\Util\ConversionException; |
||||||
28 | use Digia\GraphQL\Util\TypeASTConverter; |
||||||
29 | use Digia\GraphQL\Util\ValueASTConverter; |
||||||
30 | use function Digia\GraphQL\Test\jsonEncode; |
||||||
31 | use function Digia\GraphQL\Util\find; |
||||||
32 | use function Digia\GraphQL\Util\invariant; |
||||||
33 | use function Digia\GraphQL\Util\keyMap; |
||||||
34 | use function Digia\GraphQL\Util\suggestionList; |
||||||
35 | |||||||
36 | class ValuesResolver |
||||||
37 | { |
||||||
38 | /** |
||||||
39 | * Prepares an object map of argument values given a list of argument |
||||||
40 | * definitions and list of argument AST nodes. |
||||||
41 | * |
||||||
42 | * @see https://facebook.github.io/graphql/October2016/#CoerceArgumentValues() |
||||||
43 | * |
||||||
44 | * @param Field|Directive $definition |
||||||
45 | * @param ArgumentsAwareInterface $node |
||||||
46 | * @param array $variableValues |
||||||
47 | * @return array |
||||||
48 | * @throws ExecutionException |
||||||
49 | * @throws InvariantException |
||||||
50 | */ |
||||||
51 | public static function coerceArgumentValues( |
||||||
52 | $definition, |
||||||
53 | ArgumentsAwareInterface $node, |
||||||
54 | array $variableValues = [] |
||||||
55 | ): array { |
||||||
56 | $coercedValues = []; |
||||||
57 | $argumentDefinitions = $definition->getArguments(); |
||||||
58 | $argumentNodes = $node->getArguments(); |
||||||
59 | |||||||
60 | if (empty($argumentDefinitions)) { |
||||||
61 | return $coercedValues; |
||||||
62 | } |
||||||
63 | |||||||
64 | /** @var ArgumentNode[] $argumentNodeMap */ |
||||||
65 | $argumentNodeMap = keyMap($argumentNodes, function (ArgumentNode $value) { |
||||||
66 | return $value->getNameValue(); |
||||||
67 | }); |
||||||
68 | |||||||
69 | foreach ($argumentDefinitions as $argumentDefinition) { |
||||||
70 | $argumentName = $argumentDefinition->getName(); |
||||||
71 | $argumentType = $argumentDefinition->getType(); |
||||||
72 | $argumentNode = $argumentNodeMap[$argumentName] ?? null; |
||||||
73 | $defaultValue = $argumentDefinition->getDefaultValue(); |
||||||
74 | $argumentValue = null !== $argumentNode ? $argumentNode->getValue() : null; |
||||||
75 | |||||||
76 | if (null !== $argumentNode && $argumentValue instanceof VariableNode) { |
||||||
77 | $variableName = $argumentValue->getNameValue(); |
||||||
78 | $hasValue = !empty($variableValues) && \array_key_exists($variableName, $variableValues); |
||||||
79 | $isNull = $hasValue && null === $variableValues[$variableName]; |
||||||
80 | } else { |
||||||
81 | $hasValue = null !== $argumentNode; |
||||||
82 | $isNull = $hasValue && $argumentValue instanceof NullValueNode; |
||||||
83 | } |
||||||
84 | |||||||
85 | if (!$hasValue && null !== $defaultValue) { |
||||||
86 | // If no argument was provided where the definition has a default value, |
||||||
87 | // use the default value. |
||||||
88 | $coercedValues[$argumentName] = $defaultValue; |
||||||
89 | } elseif ((!$hasValue || $isNull) && $argumentType instanceof NonNullType) { |
||||||
90 | // If no argument or a null value was provided to an argument with a |
||||||
91 | // non-null type (required), produce a field error. |
||||||
92 | if ($isNull) { |
||||||
93 | throw new ExecutionException( |
||||||
94 | \sprintf( |
||||||
95 | 'Argument "%s" of non-null type "%s" must not be null.', |
||||||
96 | $argumentName, |
||||||
97 | $argumentType |
||||||
98 | ), |
||||||
99 | [$argumentValue] |
||||||
100 | ); |
||||||
101 | } elseif (null !== $argumentNode && $argumentValue instanceof VariableNode) { |
||||||
102 | $variableName = $argumentValue->getNameValue(); |
||||||
103 | throw new ExecutionException( |
||||||
104 | \sprintf( |
||||||
105 | 'Argument "%s" of required type "%s" was provided the variable "$%s" ' |
||||||
106 | . 'which was not provided a runtime value.', |
||||||
107 | $argumentName, |
||||||
108 | $argumentType, |
||||||
109 | $variableName |
||||||
110 | ), |
||||||
111 | [$argumentValue] |
||||||
112 | ); |
||||||
113 | } else { |
||||||
114 | throw new ExecutionException( |
||||||
115 | \sprintf( |
||||||
116 | 'Argument "%s" of required type "%s" was not provided.', |
||||||
117 | $argumentName, |
||||||
118 | $argumentType |
||||||
119 | ), |
||||||
120 | [$node] |
||||||
121 | ); |
||||||
122 | } |
||||||
123 | } elseif ($hasValue) { |
||||||
124 | if ($argumentValue instanceof NullValueNode) { |
||||||
125 | // If the explicit value `null` was provided, an entry in the coerced |
||||||
126 | // values must exist as the value `null`. |
||||||
127 | $coercedValues[$argumentName] = null; |
||||||
128 | } elseif ($argumentValue instanceof VariableNode) { |
||||||
129 | $variableName = $argumentValue->getNameValue(); |
||||||
130 | invariant(!empty($variableValues), 'Must exist for hasValue to be true.'); |
||||||
131 | // Note: This does no further checking that this variable is correct. |
||||||
132 | // This assumes that this query has been validated and the variable |
||||||
133 | // usage here is of the correct type. |
||||||
134 | $coercedValues[$argumentName] = $variableValues[$variableName]; |
||||||
135 | } else { |
||||||
136 | $valueNode = $argumentNode->getValue(); |
||||||
0 ignored issues
–
show
|
|||||||
137 | try { |
||||||
138 | // Value nodes that cannot be resolved should be treated as invalid values |
||||||
139 | // because there is no undefined value in PHP so that we throw an exception |
||||||
140 | $coercedValue = ValueASTConverter::convert($valueNode, $argumentType, $variableValues); |
||||||
0 ignored issues
–
show
It seems like
$argumentType can also be of type null ; however, parameter $type of Digia\GraphQL\Util\ValueASTConverter::convert() does only seem to accept Digia\GraphQL\Type\Definition\TypeInterface , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() It seems like
$valueNode can also be of type null ; however, parameter $node of Digia\GraphQL\Util\ValueASTConverter::convert() does only seem to accept Digia\GraphQL\Language\Node\NodeInterface , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
141 | } catch (\Exception $ex) { |
||||||
142 | // Note: ValuesOfCorrectType validation should catch this before |
||||||
143 | // execution. This is a runtime check to ensure execution does not |
||||||
144 | // continue with an invalid argument value. |
||||||
145 | throw new ExecutionException( |
||||||
146 | \sprintf( |
||||||
147 | 'Argument "%s" has invalid value %s.', |
||||||
148 | $argumentName, |
||||||
149 | (string)$argumentValue |
||||||
150 | ), |
||||||
151 | [$argumentValue], |
||||||
152 | null, |
||||||
153 | null, |
||||||
154 | null, |
||||||
155 | null, |
||||||
156 | $ex |
||||||
157 | ); |
||||||
158 | } |
||||||
159 | $coercedValues[$argumentName] = $coercedValue; |
||||||
160 | } |
||||||
161 | } |
||||||
162 | } |
||||||
163 | |||||||
164 | return $coercedValues; |
||||||
165 | } |
||||||
166 | |||||||
167 | /** |
||||||
168 | * Prepares an object map of argument values given a directive definition |
||||||
169 | * and a AST node which may contain directives. Optionally also accepts a map |
||||||
170 | * of variable values. |
||||||
171 | * |
||||||
172 | * If the directive does not exist on the node, returns null. |
||||||
173 | * |
||||||
174 | * @param Directive $directive |
||||||
175 | * @param mixed $node |
||||||
176 | * @param array $variableValues |
||||||
177 | * @return array|null |
||||||
178 | * @throws ExecutionException |
||||||
179 | * @throws InvariantException |
||||||
180 | */ |
||||||
181 | public static function coerceDirectiveValues( |
||||||
182 | Directive $directive, |
||||||
183 | $node, |
||||||
184 | array $variableValues = [] |
||||||
185 | ): ?array { |
||||||
186 | $directiveNode = $node->hasDirectives() |
||||||
187 | ? find($node->getDirectives(), function (NameAwareInterface $value) use ($directive) { |
||||||
188 | return $value->getNameValue() === $directive->getName(); |
||||||
189 | }) : null; |
||||||
190 | |||||||
191 | if (null !== $directiveNode) { |
||||||
192 | return static::coerceArgumentValues($directive, $directiveNode, $variableValues); |
||||||
193 | } |
||||||
194 | |||||||
195 | return null; |
||||||
196 | } |
||||||
197 | |||||||
198 | /** |
||||||
199 | * Prepares an object map of variableValues of the correct type based on the |
||||||
200 | * provided variable definitions and arbitrary input. If the input cannot be |
||||||
201 | * parsed to match the variable definitions, a GraphQLError will be thrown. |
||||||
202 | * |
||||||
203 | * @param Schema $schema |
||||||
204 | * @param array|VariableDefinitionNode[] $variableDefinitionNodes |
||||||
205 | * @param array $inputs |
||||||
206 | * @return CoercedValue |
||||||
207 | * @throws GraphQLException |
||||||
208 | * @throws InvariantException |
||||||
209 | * @throws ConversionException |
||||||
210 | */ |
||||||
211 | public static function coerceVariableValues( |
||||||
212 | Schema $schema, |
||||||
213 | array $variableDefinitionNodes, |
||||||
214 | array $inputs |
||||||
215 | ): CoercedValue { |
||||||
216 | $coercedValues = []; |
||||||
217 | $errors = []; |
||||||
218 | |||||||
219 | foreach ($variableDefinitionNodes as $variableDefinitionNode) { |
||||||
220 | $variableName = $variableDefinitionNode->getVariable()->getNameValue(); |
||||||
221 | $variableType = TypeASTConverter::convert($schema, $variableDefinitionNode->getType()); |
||||||
222 | $variableTypeName = (string)$variableType; |
||||||
223 | |||||||
224 | if ($variableTypeName === '') { |
||||||
225 | $variableTypeName = (string)$variableDefinitionNode; |
||||||
226 | } |
||||||
227 | |||||||
228 | if (!static::isInputType($variableType)) { |
||||||
229 | // Must use input types for variables. This should be caught during |
||||||
230 | // validation, however is checked again here for safety. |
||||||
231 | $errors[] = static::buildCoerceException( |
||||||
232 | \sprintf( |
||||||
233 | 'Variable "$%s" expected value of type "%s" which cannot be used as an input type', |
||||||
234 | $variableName, |
||||||
235 | $variableTypeName |
||||||
236 | ), |
||||||
237 | $variableDefinitionNode, |
||||||
238 | null |
||||||
239 | ); |
||||||
240 | } else { |
||||||
241 | $hasValue = \array_key_exists($variableName, $inputs); |
||||||
242 | $value = $hasValue ? $inputs[$variableName] : null; |
||||||
243 | if (!$hasValue && $variableDefinitionNode->hasDefaultValue()) { |
||||||
244 | // If no value was provided to a variable with a default value, |
||||||
245 | // use the default value. |
||||||
246 | $coercedValues[$variableName] = ValueASTConverter::convert( |
||||||
247 | $variableDefinitionNode->getDefaultValue(), |
||||||
248 | $variableType |
||||||
249 | ); |
||||||
250 | } elseif ((!$hasValue || null === $value) && $variableType instanceof NonNullType) { |
||||||
251 | // If no value or a nullish value was provided to a variable with a |
||||||
252 | // non-null type (required), produce an error. |
||||||
253 | $errors[] = static::buildCoerceException( |
||||||
254 | \sprintf( |
||||||
255 | $value |
||||||
256 | ? 'Variable "$%s" of non-null type "%s" must not be null' |
||||||
257 | : 'Variable "$%s" of required type "%s" was not provided', |
||||||
258 | $variableName, |
||||||
259 | $variableTypeName |
||||||
260 | ), |
||||||
261 | $variableDefinitionNode, |
||||||
262 | null |
||||||
263 | ); |
||||||
264 | } elseif ($hasValue) { |
||||||
265 | if (null === $value) { |
||||||
266 | // If the explicit value `null` was provided, an entry in the coerced |
||||||
267 | // values must exist as the value `null`. |
||||||
268 | $coercedValues[$variableName] = null; |
||||||
269 | } else { |
||||||
270 | // Otherwise, a non-null value was provided, coerce it to the expected |
||||||
271 | // type or report an error if coercion fails. |
||||||
272 | $coercedValue = static::coerceValue($value, $variableType, $variableDefinitionNode); |
||||||
273 | if ($coercedValue->hasErrors()) { |
||||||
274 | $message = \sprintf( |
||||||
275 | 'Variable "$%s" got invalid value %s', |
||||||
276 | $variableName, |
||||||
277 | jsonEncode($value) |
||||||
278 | ); |
||||||
279 | foreach ($coercedValue->getErrors() as $error) { |
||||||
280 | $errors[] = static::buildCoerceException( |
||||||
281 | $message, |
||||||
282 | $variableDefinitionNode, |
||||||
283 | null, |
||||||
284 | $error->getMessage(), |
||||||
285 | $error |
||||||
286 | ); |
||||||
287 | } |
||||||
288 | } else { |
||||||
289 | $coercedValues[$variableName] = $coercedValue->getValue(); |
||||||
290 | } |
||||||
291 | } |
||||||
292 | } |
||||||
293 | } |
||||||
294 | } |
||||||
295 | |||||||
296 | return new CoercedValue($coercedValues, $errors); |
||||||
297 | } |
||||||
298 | |||||||
299 | /** |
||||||
300 | * @param TypeInterface|null $type |
||||||
301 | * @return bool |
||||||
302 | */ |
||||||
303 | protected static function isInputType(?TypeInterface $type) |
||||||
304 | { |
||||||
305 | return ($type instanceof ScalarType) || |
||||||
306 | ($type instanceof EnumType) || |
||||||
307 | ($type instanceof InputObjectType) || |
||||||
308 | (($type instanceof WrappingTypeInterface) && static::isInputType($type->getOfType())); |
||||||
309 | } |
||||||
310 | |||||||
311 | /** |
||||||
312 | * Returns either a value which is valid for the provided type or a list of |
||||||
313 | * encountered coercion errors. |
||||||
314 | * |
||||||
315 | * @param mixed|array $value |
||||||
316 | * @param mixed $type |
||||||
317 | * @param NodeInterface $blameNode |
||||||
318 | * @param Path|null $path |
||||||
319 | * @return CoercedValue |
||||||
320 | * @throws GraphQLException |
||||||
321 | * @throws InvariantException |
||||||
322 | */ |
||||||
323 | protected static function coerceValue($value, $type, $blameNode, ?Path $path = null): CoercedValue |
||||||
324 | { |
||||||
325 | if ($type instanceof NonNullType) { |
||||||
326 | return static::coerceValueForNonNullType($value, $type, $blameNode, $path); |
||||||
327 | } |
||||||
328 | |||||||
329 | if (null === $value) { |
||||||
330 | return new CoercedValue(null); |
||||||
331 | } |
||||||
332 | |||||||
333 | if ($type instanceof ScalarType) { |
||||||
334 | return static::coerceValueForScalarType($value, $type, $blameNode, $path); |
||||||
335 | } |
||||||
336 | |||||||
337 | if ($type instanceof EnumType) { |
||||||
338 | return static::coerceValueForEnumType($value, $type, $blameNode, $path); |
||||||
339 | } |
||||||
340 | |||||||
341 | if ($type instanceof ListType) { |
||||||
342 | return static::coerceValueForListType($value, $type, $blameNode, $path); |
||||||
343 | } |
||||||
344 | |||||||
345 | if ($type instanceof InputObjectType) { |
||||||
346 | return static::coerceValueForInputObjectType($value, $type, $blameNode, $path); |
||||||
347 | } |
||||||
348 | |||||||
349 | throw new GraphQLException('Unexpected type.'); |
||||||
350 | } |
||||||
351 | |||||||
352 | /** |
||||||
353 | * @param mixed $value |
||||||
354 | * @param NonNullType $type |
||||||
355 | * @param NodeInterface $blameNode |
||||||
356 | * @param Path|null $path |
||||||
357 | * @return CoercedValue |
||||||
358 | * @throws GraphQLException |
||||||
359 | * @throws InvariantException |
||||||
360 | */ |
||||||
361 | protected static function coerceValueForNonNullType( |
||||||
362 | $value, |
||||||
363 | NonNullType $type, |
||||||
364 | NodeInterface $blameNode, |
||||||
365 | ?Path $path |
||||||
366 | ): CoercedValue { |
||||||
367 | if (null === $value) { |
||||||
368 | return new CoercedValue(null, [ |
||||||
369 | static::buildCoerceException( |
||||||
370 | \sprintf('Expected non-nullable type %s not to be null', (string)$type), |
||||||
371 | $blameNode, |
||||||
372 | $path |
||||||
373 | ) |
||||||
374 | ]); |
||||||
375 | } |
||||||
376 | return static::coerceValue($value, $type->getOfType(), $blameNode, $path); |
||||||
377 | } |
||||||
378 | |||||||
379 | /** |
||||||
380 | * Scalars determine if a value is valid via parseValue(), which can |
||||||
381 | * throw to indicate failure. If it throws, maintain a reference to |
||||||
382 | * the original error. |
||||||
383 | * |
||||||
384 | * @param mixed $value |
||||||
385 | * @param ScalarType $type |
||||||
386 | * @param NodeInterface $blameNode |
||||||
387 | * @param Path|null $path |
||||||
388 | * @return CoercedValue |
||||||
389 | */ |
||||||
390 | protected static function coerceValueForScalarType( |
||||||
391 | $value, |
||||||
392 | ScalarType $type, |
||||||
393 | NodeInterface $blameNode, |
||||||
394 | ?Path $path |
||||||
395 | ): CoercedValue { |
||||||
396 | try { |
||||||
397 | $parseResult = $type->parseValue($value); |
||||||
398 | if (null === $parseResult) { |
||||||
399 | return new CoercedValue(null, [ |
||||||
400 | new GraphQLException(\sprintf('Expected type %s', (string)$type)) |
||||||
401 | ]); |
||||||
402 | } |
||||||
403 | return new CoercedValue($parseResult); |
||||||
404 | } /** @noinspection PhpRedundantCatchClauseInspection */ catch (InvalidTypeException|CoercingException $ex) { |
||||||
405 | return new CoercedValue(null, [ |
||||||
406 | static::buildCoerceException( |
||||||
407 | \sprintf('Expected type %s', (string)$type), |
||||||
408 | $blameNode, |
||||||
409 | $path, |
||||||
410 | $ex->getMessage(), |
||||||
411 | $ex |
||||||
412 | ) |
||||||
413 | ]); |
||||||
414 | } |
||||||
415 | } |
||||||
416 | |||||||
417 | /** |
||||||
418 | * @param mixed $value |
||||||
419 | * @param EnumType $type |
||||||
420 | * @param NodeInterface $blameNode |
||||||
421 | * @param Path|null $path |
||||||
422 | * @return CoercedValue |
||||||
423 | * @throws InvariantException |
||||||
424 | */ |
||||||
425 | protected static function coerceValueForEnumType( |
||||||
426 | $value, |
||||||
427 | EnumType $type, |
||||||
428 | NodeInterface $blameNode, |
||||||
429 | ?Path $path |
||||||
430 | ): CoercedValue { |
||||||
431 | if (\is_string($value) && null !== ($enumValue = $type->getValue($value))) { |
||||||
432 | return new CoercedValue($enumValue); |
||||||
433 | } |
||||||
434 | |||||||
435 | $suggestions = suggestionList((string)$value, \array_map(function (EnumValue $enumValue) { |
||||||
436 | return $enumValue->getName(); |
||||||
437 | }, $type->getValues())); |
||||||
438 | |||||||
439 | $didYouMean = (!empty($suggestions)) |
||||||
440 | ? 'did you mean' . \implode(',', $suggestions) |
||||||
441 | : null; |
||||||
442 | |||||||
443 | return new CoercedValue(null, [ |
||||||
444 | static::buildCoerceException(\sprintf('Expected type %s', $type->getName()), $blameNode, $path, $didYouMean) |
||||||
445 | ]); |
||||||
446 | } |
||||||
447 | |||||||
448 | /** |
||||||
449 | * @param mixed $value |
||||||
450 | * @param InputObjectType $type |
||||||
451 | * @param NodeInterface $blameNode |
||||||
452 | * @param Path|null $path |
||||||
453 | * @return CoercedValue |
||||||
454 | * @throws InvariantException |
||||||
455 | * @throws GraphQLException |
||||||
456 | */ |
||||||
457 | protected static function coerceValueForInputObjectType( |
||||||
458 | $value, |
||||||
459 | InputObjectType $type, |
||||||
460 | NodeInterface $blameNode, |
||||||
461 | ?Path $path |
||||||
462 | ): CoercedValue { |
||||||
463 | $errors = []; |
||||||
464 | $coercedValues = []; |
||||||
465 | $fields = $type->getFields(); |
||||||
466 | |||||||
467 | // Ensure every defined field is valid. |
||||||
468 | foreach ($fields as $field) { |
||||||
469 | $fieldType = $field->getType(); |
||||||
470 | |||||||
471 | if (!isset($value[$field->getName()])) { |
||||||
472 | if (!empty($field->getDefaultValue())) { |
||||||
473 | $coercedValue[$field->getName()] = $field->getDefaultValue(); |
||||||
474 | } elseif ($fieldType instanceof NonNullType) { |
||||||
475 | $errors[] = new GraphQLException( |
||||||
476 | \sprintf( |
||||||
477 | "Field %s of required type %s! was not provided.", |
||||||
478 | static::printPath(new Path($path, $field->getName())), |
||||||
479 | (string)$fieldType->getOfType() |
||||||
480 | ) |
||||||
481 | ); |
||||||
482 | } |
||||||
483 | } else { |
||||||
484 | $fieldValue = $value[$field->getName()]; |
||||||
485 | $coercedValue = static::coerceValue( |
||||||
486 | $fieldValue, |
||||||
487 | $fieldType, |
||||||
488 | $blameNode, |
||||||
489 | new Path($path, $field->getName()) |
||||||
490 | ); |
||||||
491 | |||||||
492 | if ($coercedValue->hasErrors()) { |
||||||
493 | $errors = \array_merge($errors, $coercedValue->getErrors()); |
||||||
494 | } elseif (empty($errors)) { |
||||||
495 | $coercedValues[$field->getName()] = $coercedValue->getValue(); |
||||||
496 | } |
||||||
497 | } |
||||||
498 | } |
||||||
499 | |||||||
500 | // Ensure every provided field is defined. |
||||||
501 | foreach ($value as $fieldName => $fieldValue) { |
||||||
502 | if (!isset($fields[$fieldName])) { |
||||||
503 | $suggestions = suggestionList($fieldName, \array_keys($fields)); |
||||||
504 | $didYouMean = (!empty($suggestions)) |
||||||
505 | ? 'did you mean' . \implode(',', $suggestions) |
||||||
506 | : null; |
||||||
507 | |||||||
508 | $errors[] = static::buildCoerceException( |
||||||
509 | \sprintf('Field "%s" is not defined by type %s', $fieldName, $type->getName()), |
||||||
510 | $blameNode, |
||||||
511 | $path, |
||||||
512 | $didYouMean |
||||||
513 | ); |
||||||
514 | } |
||||||
515 | } |
||||||
516 | |||||||
517 | return new CoercedValue($coercedValues, $errors); |
||||||
518 | } |
||||||
519 | |||||||
520 | /** |
||||||
521 | * @param mixed $value |
||||||
522 | * @param ListType $type |
||||||
523 | * @param NodeInterface $blameNode |
||||||
524 | * @param Path|null $path |
||||||
525 | * @return CoercedValue |
||||||
526 | * @throws GraphQLException |
||||||
527 | * @throws InvariantException |
||||||
528 | */ |
||||||
529 | protected static function coerceValueForListType( |
||||||
530 | $value, |
||||||
531 | ListType $type, |
||||||
532 | NodeInterface $blameNode, |
||||||
533 | ?Path $path |
||||||
534 | ): CoercedValue { |
||||||
535 | $itemType = $type->getOfType(); |
||||||
536 | |||||||
537 | if (\is_array($value) || $value instanceof \Traversable) { |
||||||
538 | $errors = []; |
||||||
539 | $coercedValues = []; |
||||||
540 | |||||||
541 | foreach ($value as $index => $itemValue) { |
||||||
542 | $coercedValue = static::coerceValue($itemValue, $itemType, $blameNode, new Path($path, $index)); |
||||||
543 | |||||||
544 | if ($coercedValue->hasErrors()) { |
||||||
545 | $errors = \array_merge($errors, $coercedValue->getErrors()); |
||||||
546 | } else { |
||||||
547 | $coercedValues[] = $coercedValue->getValue(); |
||||||
548 | } |
||||||
549 | } |
||||||
550 | |||||||
551 | return new CoercedValue($coercedValues, $errors); |
||||||
552 | } |
||||||
553 | |||||||
554 | // Lists accept a non-list value as a list of one. |
||||||
555 | $coercedValue = static::coerceValue($value, $itemType, $blameNode); |
||||||
556 | |||||||
557 | return new CoercedValue([$coercedValue->getValue()], $coercedValue->getErrors()); |
||||||
558 | } |
||||||
559 | |||||||
560 | /** |
||||||
561 | * @param string $message |
||||||
562 | * @param NodeInterface $blameNode |
||||||
563 | * @param Path|null $path |
||||||
564 | * @param null|string $subMessage |
||||||
565 | * @param GraphQLException|null $originalException |
||||||
566 | * @return GraphQLException |
||||||
567 | */ |
||||||
568 | protected static function buildCoerceException( |
||||||
569 | string $message, |
||||||
570 | NodeInterface $blameNode, |
||||||
571 | ?Path $path, |
||||||
572 | ?string $subMessage = null, |
||||||
573 | ?GraphQLException $originalException = null |
||||||
574 | ) { |
||||||
575 | $stringPath = static::printPath($path); |
||||||
576 | |||||||
577 | return new CoercingException( |
||||||
578 | $message . |
||||||
579 | (($stringPath !== '') ? ' at ' . $stringPath : $stringPath) . |
||||||
580 | (($subMessage !== null) ? '; ' . $subMessage : '.'), |
||||||
581 | [$blameNode], |
||||||
582 | null, |
||||||
583 | null, |
||||||
584 | null, |
||||||
585 | null, |
||||||
586 | $originalException |
||||||
587 | ); |
||||||
588 | } |
||||||
589 | |||||||
590 | /** |
||||||
591 | * @param Path|null $path |
||||||
592 | * @return string |
||||||
593 | */ |
||||||
594 | protected static function printPath(?Path $path) |
||||||
595 | { |
||||||
596 | $stringPath = ''; |
||||||
597 | $currentPath = $path; |
||||||
598 | |||||||
599 | while ($currentPath !== null) { |
||||||
600 | $stringPath = \is_string($currentPath->getKey()) |
||||||
601 | ? '.' . $currentPath->getKey() . $stringPath |
||||||
602 | : '[' . (string)$currentPath->getKey() . ']' . $stringPath; |
||||||
603 | |||||||
604 | $currentPath = $currentPath->getPrevious(); |
||||||
605 | } |
||||||
606 | |||||||
607 | return !empty($stringPath) ? 'value' . $stringPath : ''; |
||||||
608 | } |
||||||
609 | } |
||||||
610 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.