1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace GraphQL\Utils; |
||
6 | |||
7 | use ArrayAccess; |
||
8 | use Exception; |
||
9 | use GraphQL\Error\Error; |
||
10 | use GraphQL\Error\InvariantViolation; |
||
11 | use GraphQL\Language\AST\BooleanValueNode; |
||
12 | use GraphQL\Language\AST\DocumentNode; |
||
13 | use GraphQL\Language\AST\EnumValueNode; |
||
14 | use GraphQL\Language\AST\FloatValueNode; |
||
15 | use GraphQL\Language\AST\IntValueNode; |
||
16 | use GraphQL\Language\AST\ListTypeNode; |
||
17 | use GraphQL\Language\AST\ListValueNode; |
||
18 | use GraphQL\Language\AST\Location; |
||
19 | use GraphQL\Language\AST\NamedTypeNode; |
||
20 | use GraphQL\Language\AST\NameNode; |
||
21 | use GraphQL\Language\AST\Node; |
||
22 | use GraphQL\Language\AST\NodeKind; |
||
23 | use GraphQL\Language\AST\NodeList; |
||
24 | use GraphQL\Language\AST\NonNullTypeNode; |
||
25 | use GraphQL\Language\AST\NullValueNode; |
||
26 | use GraphQL\Language\AST\ObjectFieldNode; |
||
27 | use GraphQL\Language\AST\ObjectValueNode; |
||
28 | use GraphQL\Language\AST\OperationDefinitionNode; |
||
29 | use GraphQL\Language\AST\StringValueNode; |
||
30 | use GraphQL\Language\AST\ValueNode; |
||
31 | use GraphQL\Language\AST\VariableNode; |
||
32 | use GraphQL\Type\Definition\EnumType; |
||
33 | use GraphQL\Type\Definition\IDType; |
||
34 | use GraphQL\Type\Definition\InputObjectType; |
||
35 | use GraphQL\Type\Definition\InputType; |
||
36 | use GraphQL\Type\Definition\ListOfType; |
||
37 | use GraphQL\Type\Definition\NonNull; |
||
38 | use GraphQL\Type\Definition\ScalarType; |
||
39 | use GraphQL\Type\Definition\Type; |
||
40 | use GraphQL\Type\Schema; |
||
41 | use stdClass; |
||
42 | use Throwable; |
||
43 | use Traversable; |
||
44 | use function array_combine; |
||
45 | use function array_key_exists; |
||
46 | use function array_map; |
||
47 | use function count; |
||
48 | use function floatval; |
||
49 | use function intval; |
||
50 | use function is_array; |
||
51 | use function is_bool; |
||
52 | use function is_float; |
||
53 | use function is_int; |
||
54 | use function is_object; |
||
55 | use function is_string; |
||
56 | use function iterator_to_array; |
||
57 | use function property_exists; |
||
58 | |||
59 | /** |
||
60 | * Various utilities dealing with AST |
||
61 | */ |
||
62 | class AST |
||
63 | { |
||
64 | /** |
||
65 | * Convert representation of AST as an associative array to instance of GraphQL\Language\AST\Node. |
||
66 | * |
||
67 | * For example: |
||
68 | * |
||
69 | * ```php |
||
70 | * AST::fromArray([ |
||
71 | * 'kind' => 'ListValue', |
||
72 | * 'values' => [ |
||
73 | * ['kind' => 'StringValue', 'value' => 'my str'], |
||
74 | * ['kind' => 'StringValue', 'value' => 'my other str'] |
||
75 | * ], |
||
76 | * 'loc' => ['start' => 21, 'end' => 25] |
||
77 | * ]); |
||
78 | * ``` |
||
79 | * |
||
80 | * Will produce instance of `ListValueNode` where `values` prop is a lazily-evaluated `NodeList` |
||
81 | * returning instances of `StringValueNode` on access. |
||
82 | * |
||
83 | * This is a reverse operation for AST::toArray($node) |
||
84 | * |
||
85 | * @param mixed[] $node |
||
86 | * |
||
87 | * @api |
||
88 | */ |
||
89 | 2 | public static function fromArray(array $node) : Node |
|
90 | { |
||
91 | 2 | if (! isset($node['kind']) || ! isset(NodeKind::$classMap[$node['kind']])) { |
|
92 | throw new InvariantViolation('Unexpected node structure: ' . Utils::printSafeJson($node)); |
||
93 | } |
||
94 | |||
95 | 2 | $kind = $node['kind'] ?? null; |
|
96 | 2 | $class = NodeKind::$classMap[$kind]; |
|
97 | 2 | $instance = new $class([]); |
|
98 | |||
99 | 2 | if (isset($node['loc'], $node['loc']['start'], $node['loc']['end'])) { |
|
100 | 1 | $instance->loc = Location::create($node['loc']['start'], $node['loc']['end']); |
|
101 | } |
||
102 | |||
103 | 2 | foreach ($node as $key => $value) { |
|
104 | 2 | if ($key === 'loc' || $key === 'kind') { |
|
105 | 2 | continue; |
|
106 | } |
||
107 | 2 | if (is_array($value)) { |
|
108 | 2 | if (isset($value[0]) || empty($value)) { |
|
109 | 2 | $value = new NodeList($value); |
|
110 | } else { |
||
111 | 2 | $value = self::fromArray($value); |
|
112 | } |
||
113 | } |
||
114 | 2 | $instance->{$key} = $value; |
|
115 | } |
||
116 | |||
117 | 2 | return $instance; |
|
118 | } |
||
119 | |||
120 | /** |
||
121 | * Convert AST node to serializable array |
||
122 | * |
||
123 | * @return mixed[] |
||
124 | * |
||
125 | * @api |
||
126 | */ |
||
127 | public static function toArray(Node $node) |
||
128 | { |
||
129 | return $node->toArray(true); |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Produces a GraphQL Value AST given a PHP value. |
||
134 | * |
||
135 | * Optionally, a GraphQL type may be provided, which will be used to |
||
136 | * disambiguate between value primitives. |
||
137 | * |
||
138 | * | PHP Value | GraphQL Value | |
||
139 | * | ------------- | -------------------- | |
||
140 | * | Object | Input Object | |
||
141 | * | Assoc Array | Input Object | |
||
142 | * | Array | List | |
||
143 | * | Boolean | Boolean | |
||
144 | * | String | String / Enum Value | |
||
145 | * | Int | Int | |
||
146 | * | Float | Int / Float | |
||
147 | * | Mixed | Enum Value | |
||
148 | * | null | NullValue | |
||
149 | * |
||
150 | * @param Type|mixed|null $value |
||
151 | * |
||
152 | * @return ObjectValueNode|ListValueNode|BooleanValueNode|IntValueNode|FloatValueNode|EnumValueNode|StringValueNode|NullValueNode |
||
153 | * |
||
154 | * @api |
||
155 | */ |
||
156 | 28 | public static function astFromValue($value, InputType $type) |
|
157 | { |
||
158 | 28 | if ($type instanceof NonNull) { |
|
159 | 4 | $astValue = self::astFromValue($value, $type->getWrappedType()); |
|
160 | 4 | if ($astValue instanceof NullValueNode) { |
|
161 | 4 | return null; |
|
162 | } |
||
163 | |||
164 | 1 | return $astValue; |
|
165 | } |
||
166 | |||
167 | 28 | if ($value === null) { |
|
168 | 7 | return new NullValueNode([]); |
|
169 | } |
||
170 | |||
171 | // Convert PHP array to GraphQL list. If the GraphQLType is a list, but |
||
172 | // the value is not an array, convert the value using the list's item type. |
||
173 | 26 | if ($type instanceof ListOfType) { |
|
174 | 2 | $itemType = $type->getWrappedType(); |
|
175 | 2 | if (is_array($value) || ($value instanceof Traversable)) { |
|
176 | 1 | $valuesNodes = []; |
|
177 | 1 | foreach ($value as $item) { |
|
178 | 1 | $itemNode = self::astFromValue($item, $itemType); |
|
179 | 1 | if (! $itemNode) { |
|
180 | continue; |
||
181 | } |
||
182 | |||
183 | 1 | $valuesNodes[] = $itemNode; |
|
184 | } |
||
185 | |||
186 | 1 | return new ListValueNode(['values' => new NodeList($valuesNodes)]); |
|
187 | } |
||
188 | |||
189 | 1 | return self::astFromValue($value, $itemType); |
|
190 | } |
||
191 | |||
192 | // Populate the fields of the input object by creating ASTs from each value |
||
193 | // in the PHP object according to the fields in the input type. |
||
194 | 26 | if ($type instanceof InputObjectType) { |
|
195 | 2 | $isArray = is_array($value); |
|
196 | 2 | $isArrayLike = $isArray || $value instanceof ArrayAccess; |
|
197 | 2 | if ($value === null || (! $isArrayLike && ! is_object($value))) { |
|
198 | return null; |
||
199 | } |
||
200 | 2 | $fields = $type->getFields(); |
|
201 | 2 | $fieldNodes = []; |
|
202 | 2 | foreach ($fields as $fieldName => $field) { |
|
203 | 2 | if ($isArrayLike) { |
|
204 | 2 | $fieldValue = $value[$fieldName] ?? null; |
|
205 | } else { |
||
206 | 1 | $fieldValue = $value->{$fieldName} ?? null; |
|
207 | } |
||
208 | |||
209 | // Have to check additionally if key exists, since we differentiate between |
||
210 | // "no key" and "value is null": |
||
211 | 2 | if ($fieldValue !== null) { |
|
212 | 1 | $fieldExists = true; |
|
213 | 1 | } elseif ($isArray) { |
|
214 | 1 | $fieldExists = array_key_exists($fieldName, $value); |
|
215 | } elseif ($isArrayLike) { |
||
216 | $fieldExists = $value->offsetExists($fieldName); |
||
0 ignored issues
–
show
|
|||
217 | } else { |
||
218 | $fieldExists = property_exists($value, $fieldName); |
||
219 | } |
||
220 | |||
221 | 2 | if (! $fieldExists) { |
|
222 | 1 | continue; |
|
223 | } |
||
224 | |||
225 | 2 | $fieldNode = self::astFromValue($fieldValue, $field->getType()); |
|
226 | |||
227 | 2 | if (! $fieldNode) { |
|
228 | continue; |
||
229 | } |
||
230 | |||
231 | 2 | $fieldNodes[] = new ObjectFieldNode([ |
|
232 | 2 | 'name' => new NameNode(['value' => $fieldName]), |
|
233 | 2 | 'value' => $fieldNode, |
|
234 | ]); |
||
235 | } |
||
236 | |||
237 | 2 | return new ObjectValueNode(['fields' => new NodeList($fieldNodes)]); |
|
238 | } |
||
239 | |||
240 | 25 | if ($type instanceof ScalarType || $type instanceof EnumType) { |
|
241 | // Since value is an internally represented value, it must be serialized |
||
242 | // to an externally represented value before converting into an AST. |
||
243 | try { |
||
244 | 25 | $serialized = $type->serialize($value); |
|
245 | 3 | } catch (Throwable $error) { |
|
246 | 3 | if ($error instanceof Error && $type instanceof EnumType) { |
|
247 | 1 | return null; |
|
248 | } |
||
249 | 2 | throw $error; |
|
250 | } |
||
251 | |||
252 | // Others serialize based on their corresponding PHP scalar types. |
||
253 | 23 | if (is_bool($serialized)) { |
|
254 | 7 | return new BooleanValueNode(['value' => $serialized]); |
|
255 | } |
||
256 | 21 | if (is_int($serialized)) { |
|
257 | 5 | return new IntValueNode(['value' => $serialized]); |
|
258 | } |
||
259 | 16 | if (is_float($serialized)) { |
|
260 | // int cast with == used for performance reasons |
||
261 | // phpcs:ignore |
||
262 | 2 | if ((int) $serialized == $serialized) { |
|
263 | 2 | return new IntValueNode(['value' => $serialized]); |
|
264 | } |
||
265 | |||
266 | 1 | return new FloatValueNode(['value' => $serialized]); |
|
267 | } |
||
268 | 15 | if (is_string($serialized)) { |
|
269 | // Enum types use Enum literals. |
||
270 | 15 | if ($type instanceof EnumType) { |
|
271 | 4 | return new EnumValueNode(['value' => $serialized]); |
|
272 | } |
||
273 | |||
274 | // ID types can use Int literals. |
||
275 | 13 | $asInt = (int) $serialized; |
|
276 | 13 | if ($type instanceof IDType && (string) $asInt === $serialized) { |
|
277 | 1 | return new IntValueNode(['value' => $serialized]); |
|
278 | } |
||
279 | |||
280 | // Use json_encode, which uses the same string encoding as GraphQL, |
||
281 | // then remove the quotes. |
||
282 | 13 | return new StringValueNode(['value' => $serialized]); |
|
283 | } |
||
284 | |||
285 | throw new InvariantViolation('Cannot convert value to AST: ' . Utils::printSafe($serialized)); |
||
286 | } |
||
287 | |||
288 | throw new Error('Unknown type: ' . Utils::printSafe($type) . '.'); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Produces a PHP value given a GraphQL Value AST. |
||
293 | * |
||
294 | * A GraphQL type must be provided, which will be used to interpret different |
||
295 | * GraphQL Value literals. |
||
296 | * |
||
297 | * Returns `null` when the value could not be validly coerced according to |
||
298 | * the provided type. |
||
299 | * |
||
300 | * | GraphQL Value | PHP Value | |
||
301 | * | -------------------- | ------------- | |
||
302 | * | Input Object | Assoc Array | |
||
303 | * | List | Array | |
||
304 | * | Boolean | Boolean | |
||
305 | * | String | String | |
||
306 | * | Int / Float | Int / Float | |
||
307 | * | Enum Value | Mixed | |
||
308 | * | Null Value | null | |
||
309 | * |
||
310 | * @param VariableNode|NullValueNode|IntValueNode|FloatValueNode|StringValueNode|BooleanValueNode|EnumValueNode|ListValueNode|ObjectValueNode|null $valueNode |
||
311 | * @param mixed[]|null $variables |
||
312 | * |
||
313 | * @return mixed[]|stdClass|null |
||
314 | * |
||
315 | * @throws Exception |
||
316 | * |
||
317 | * @api |
||
318 | */ |
||
319 | 106 | public static function valueFromAST(?ValueNode $valueNode, Type $type, ?array $variables = null) |
|
320 | { |
||
321 | 106 | $undefined = Utils::undefined(); |
|
322 | |||
323 | 106 | if ($valueNode === null) { |
|
324 | // When there is no AST, then there is also no value. |
||
325 | // Importantly, this is different from returning the GraphQL null value. |
||
326 | 1 | return $undefined; |
|
327 | } |
||
328 | |||
329 | 105 | if ($type instanceof NonNull) { |
|
330 | 39 | if ($valueNode instanceof NullValueNode) { |
|
331 | // Invalid: intentionally return no value. |
||
332 | 5 | return $undefined; |
|
333 | } |
||
334 | |||
335 | 38 | return self::valueFromAST($valueNode, $type->getWrappedType(), $variables); |
|
336 | } |
||
337 | |||
338 | 105 | if ($valueNode instanceof NullValueNode) { |
|
339 | // This is explicitly returning the value null. |
||
340 | 9 | return null; |
|
341 | } |
||
342 | |||
343 | 103 | if ($valueNode instanceof VariableNode) { |
|
344 | 11 | $variableName = $valueNode->name->value; |
|
345 | |||
346 | 11 | if (! $variables || ! array_key_exists($variableName, $variables)) { |
|
347 | // No valid return value. |
||
348 | 1 | return $undefined; |
|
349 | } |
||
350 | |||
351 | 11 | $variableValue = $variables[$variableName] ?? null; |
|
352 | 11 | if ($variableValue === null && $type instanceof NonNull) { |
|
353 | return $undefined; // Invalid: intentionally return no value. |
||
354 | } |
||
355 | |||
356 | // Note: This does no further checking that this variable is correct. |
||
357 | // This assumes that this query has been validated and the variable |
||
358 | // usage here is of the correct type. |
||
359 | 11 | return $variables[$variableName]; |
|
360 | } |
||
361 | |||
362 | 94 | if ($type instanceof ListOfType) { |
|
363 | 7 | $itemType = $type->getWrappedType(); |
|
364 | |||
365 | 7 | if ($valueNode instanceof ListValueNode) { |
|
366 | 7 | $coercedValues = []; |
|
367 | 7 | $itemNodes = $valueNode->values; |
|
368 | 7 | foreach ($itemNodes as $itemNode) { |
|
369 | 7 | if (self::isMissingVariable($itemNode, $variables)) { |
|
370 | // If an array contains a missing variable, it is either coerced to |
||
371 | // null or if the item type is non-null, it considered invalid. |
||
372 | 1 | if ($itemType instanceof NonNull) { |
|
373 | // Invalid: intentionally return no value. |
||
374 | 1 | return $undefined; |
|
375 | } |
||
376 | 1 | $coercedValues[] = null; |
|
377 | } else { |
||
378 | 7 | $itemValue = self::valueFromAST($itemNode, $itemType, $variables); |
|
379 | 7 | if ($undefined === $itemValue) { |
|
380 | // Invalid: intentionally return no value. |
||
381 | 4 | return $undefined; |
|
382 | } |
||
383 | 7 | $coercedValues[] = $itemValue; |
|
384 | } |
||
385 | } |
||
386 | |||
387 | 7 | return $coercedValues; |
|
388 | } |
||
389 | 5 | $coercedValue = self::valueFromAST($valueNode, $itemType, $variables); |
|
390 | 5 | if ($undefined === $coercedValue) { |
|
391 | // Invalid: intentionally return no value. |
||
392 | 4 | return $undefined; |
|
393 | } |
||
394 | |||
395 | 5 | return [$coercedValue]; |
|
396 | } |
||
397 | |||
398 | 93 | if ($type instanceof InputObjectType) { |
|
399 | 4 | if (! $valueNode instanceof ObjectValueNode) { |
|
400 | // Invalid: intentionally return no value. |
||
401 | 2 | return $undefined; |
|
402 | } |
||
403 | |||
404 | 4 | $coercedObj = []; |
|
405 | 4 | $fields = $type->getFields(); |
|
406 | 4 | $fieldNodes = Utils::keyMap( |
|
407 | 4 | $valueNode->fields, |
|
408 | static function ($field) { |
||
409 | 4 | return $field->name->value; |
|
410 | 4 | } |
|
411 | ); |
||
412 | 4 | foreach ($fields as $field) { |
|
413 | 4 | $fieldName = $field->name; |
|
414 | /** @var VariableNode|NullValueNode|IntValueNode|FloatValueNode|StringValueNode|BooleanValueNode|EnumValueNode|ListValueNode|ObjectValueNode $fieldNode */ |
||
415 | 4 | $fieldNode = $fieldNodes[$fieldName] ?? null; |
|
416 | |||
417 | 4 | if ($fieldNode === null || self::isMissingVariable($fieldNode->value, $variables)) { |
|
0 ignored issues
–
show
|
|||
418 | 4 | if ($field->defaultValueExists()) { |
|
419 | 2 | $coercedObj[$fieldName] = $field->defaultValue; |
|
420 | 4 | } elseif ($field->getType() instanceof NonNull) { |
|
421 | // Invalid: intentionally return no value. |
||
422 | 2 | return $undefined; |
|
423 | } |
||
424 | 4 | continue; |
|
425 | } |
||
426 | |||
427 | 4 | $fieldValue = self::valueFromAST( |
|
428 | 4 | $fieldNode !== null ? $fieldNode->value : null, |
|
429 | 4 | $field->getType(), |
|
430 | 4 | $variables |
|
431 | ); |
||
432 | |||
433 | 4 | if ($undefined === $fieldValue) { |
|
434 | // Invalid: intentionally return no value. |
||
435 | 1 | return $undefined; |
|
436 | } |
||
437 | 4 | $coercedObj[$fieldName] = $fieldValue; |
|
438 | } |
||
439 | |||
440 | 4 | return $coercedObj; |
|
441 | } |
||
442 | |||
443 | 93 | if ($type instanceof EnumType) { |
|
444 | 8 | if (! $valueNode instanceof EnumValueNode) { |
|
445 | 1 | return $undefined; |
|
446 | } |
||
447 | 8 | $enumValue = $type->getValue($valueNode->value); |
|
448 | 8 | if (! $enumValue) { |
|
449 | return $undefined; |
||
450 | } |
||
451 | |||
452 | 8 | return $enumValue->value; |
|
453 | } |
||
454 | |||
455 | 86 | if ($type instanceof ScalarType) { |
|
456 | // Scalars fulfill parsing a literal value via parseLiteral(). |
||
457 | // Invalid values represent a failure to parse correctly, in which case |
||
458 | // no value is returned. |
||
459 | try { |
||
460 | 86 | return $type->parseLiteral($valueNode, $variables); |
|
461 | 8 | } catch (Throwable $error) { |
|
462 | 8 | return $undefined; |
|
463 | } |
||
464 | } |
||
465 | |||
466 | throw new Error('Unknown type: ' . Utils::printSafe($type) . '.'); |
||
467 | } |
||
468 | |||
469 | /** |
||
470 | * Returns true if the provided valueNode is a variable which is not defined |
||
471 | * in the set of variables. |
||
472 | * |
||
473 | * @param VariableNode|NullValueNode|IntValueNode|FloatValueNode|StringValueNode|BooleanValueNode|EnumValueNode|ListValueNode|ObjectValueNode $valueNode |
||
474 | * @param mixed[] $variables |
||
475 | * |
||
476 | * @return bool |
||
477 | */ |
||
478 | 9 | private static function isMissingVariable(ValueNode $valueNode, $variables) |
|
479 | { |
||
480 | 9 | return $valueNode instanceof VariableNode && |
|
481 | 9 | (count($variables) === 0 || ! array_key_exists($valueNode->name->value, $variables)); |
|
482 | } |
||
483 | |||
484 | /** |
||
485 | * Produces a PHP value given a GraphQL Value AST. |
||
486 | * |
||
487 | * Unlike `valueFromAST()`, no type is provided. The resulting PHP value |
||
488 | * will reflect the provided GraphQL value AST. |
||
489 | * |
||
490 | * | GraphQL Value | PHP Value | |
||
491 | * | -------------------- | ------------- | |
||
492 | * | Input Object | Assoc Array | |
||
493 | * | List | Array | |
||
494 | * | Boolean | Boolean | |
||
495 | * | String | String | |
||
496 | * | Int / Float | Int / Float | |
||
497 | * | Enum | Mixed | |
||
498 | * | Null | null | |
||
499 | * |
||
500 | * @param Node $valueNode |
||
501 | * @param mixed[]|null $variables |
||
502 | * |
||
503 | * @return mixed |
||
504 | * |
||
505 | * @throws Exception |
||
506 | * |
||
507 | * @api |
||
508 | */ |
||
509 | 6 | public static function valueFromASTUntyped($valueNode, ?array $variables = null) |
|
510 | { |
||
511 | switch (true) { |
||
512 | 6 | case $valueNode instanceof NullValueNode: |
|
513 | 2 | return null; |
|
514 | 6 | case $valueNode instanceof IntValueNode: |
|
515 | 3 | return intval($valueNode->value, 10); |
|
516 | 5 | case $valueNode instanceof FloatValueNode: |
|
517 | 2 | return floatval($valueNode->value); |
|
518 | 5 | case $valueNode instanceof StringValueNode: |
|
519 | 5 | case $valueNode instanceof EnumValueNode: |
|
520 | 5 | case $valueNode instanceof BooleanValueNode: |
|
521 | 4 | return $valueNode->value; |
|
522 | 4 | case $valueNode instanceof ListValueNode: |
|
523 | 4 | return array_map( |
|
524 | static function ($node) use ($variables) { |
||
525 | 4 | return self::valueFromASTUntyped($node, $variables); |
|
526 | 4 | }, |
|
527 | 4 | iterator_to_array($valueNode->values) |
|
528 | ); |
||
529 | 2 | case $valueNode instanceof ObjectValueNode: |
|
530 | 2 | return array_combine( |
|
531 | 2 | array_map( |
|
532 | static function ($field) { |
||
533 | 2 | return $field->name->value; |
|
534 | 2 | }, |
|
535 | 2 | iterator_to_array($valueNode->fields) |
|
536 | ), |
||
537 | 2 | array_map( |
|
538 | static function ($field) use ($variables) { |
||
539 | 2 | return self::valueFromASTUntyped($field->value, $variables); |
|
540 | 2 | }, |
|
541 | 2 | iterator_to_array($valueNode->fields) |
|
542 | ) |
||
543 | ); |
||
544 | 1 | case $valueNode instanceof VariableNode: |
|
545 | 1 | $variableName = $valueNode->name->value; |
|
546 | |||
547 | 1 | return $variables && isset($variables[$variableName]) |
|
548 | 1 | ? $variables[$variableName] |
|
549 | 1 | : null; |
|
550 | } |
||
551 | |||
552 | throw new Error('Unexpected value kind: ' . $valueNode->kind . '.'); |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Returns type definition for given AST Type node |
||
557 | * |
||
558 | * @param NamedTypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode |
||
559 | * |
||
560 | * @return Type|null |
||
561 | * |
||
562 | * @throws Exception |
||
563 | * |
||
564 | * @api |
||
565 | */ |
||
566 | 349 | public static function typeFromAST(Schema $schema, $inputTypeNode) |
|
567 | { |
||
568 | 349 | if ($inputTypeNode instanceof ListTypeNode) { |
|
569 | 19 | $innerType = self::typeFromAST($schema, $inputTypeNode->type); |
|
570 | |||
571 | 19 | return $innerType ? new ListOfType($innerType) : null; |
|
572 | } |
||
573 | 349 | if ($inputTypeNode instanceof NonNullTypeNode) { |
|
574 | 43 | $innerType = self::typeFromAST($schema, $inputTypeNode->type); |
|
575 | |||
576 | 43 | return $innerType ? new NonNull($innerType) : null; |
|
577 | } |
||
578 | 349 | if ($inputTypeNode instanceof NamedTypeNode) { |
|
579 | 349 | return $schema->getType($inputTypeNode->name->value); |
|
580 | } |
||
581 | |||
582 | throw new Error('Unexpected type kind: ' . $inputTypeNode->kind . '.'); |
||
583 | } |
||
584 | |||
585 | /** |
||
586 | * Returns operation type ("query", "mutation" or "subscription") given a document and operation name |
||
587 | * |
||
588 | * @param string $operationName |
||
589 | * |
||
590 | * @return bool |
||
591 | * |
||
592 | * @api |
||
593 | */ |
||
594 | 23 | public static function getOperation(DocumentNode $document, $operationName = null) |
|
595 | { |
||
596 | 23 | if ($document->definitions) { |
|
597 | 23 | foreach ($document->definitions as $def) { |
|
598 | 23 | if (! ($def instanceof OperationDefinitionNode)) { |
|
599 | continue; |
||
600 | } |
||
601 | |||
602 | 23 | if (! $operationName || (isset($def->name->value) && $def->name->value === $operationName)) { |
|
603 | 23 | return $def->operation; |
|
604 | } |
||
605 | } |
||
606 | } |
||
607 | |||
608 | return false; |
||
609 | } |
||
610 | } |
||
611 |
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.