AST   F
last analyzed

Complexity

Total Complexity 104

Size/Duplication

Total Lines 547
Duplicated Lines 0 %

Test Coverage

Coverage 91.67%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 104
eloc 215
c 1
b 0
f 0
dl 0
loc 547
ccs 198
cts 216
cp 0.9167
rs 2

8 Methods

Rating   Name   Duplication   Size   Complexity  
A toArray() 0 3 1
B fromArray() 0 29 10
D valueFromAST() 0 148 31
A typeFromAST() 0 17 6
B getOperation() 0 15 7
D astFromValue() 0 133 34
C valueFromASTUntyped() 0 44 12
A isMissingVariable() 0 4 3

How to fix   Complexity   

Complex Class

Complex classes like AST often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AST, and based on these observations, apply Extract Interface, too.

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|null
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());
0 ignored issues
show
Bug introduced by
$type->getWrappedType() of type GraphQL\Type\Definition\Type is incompatible with the type GraphQL\Type\Definition\InputType expected by parameter $type of GraphQL\Utils\AST::astFromValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

159
            $astValue = self::astFromValue($value, /** @scrutinizer ignore-type */ $type->getWrappedType());
Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type ArrayAccess and GraphQL\Type\Definition\Type and GraphQL\Type\Definition\Type&ArrayAccess; however, parameter $search of array_key_exists() does only seem to accept array, 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 ignore-type  annotation

214
                    $fieldExists = array_key_exists($fieldName, /** @scrutinizer ignore-type */ $value);
Loading history...
215
                } elseif ($isArrayLike) {
216
                    $fieldExists = $value->offsetExists($fieldName);
0 ignored issues
show
Bug introduced by
The method offsetExists() does not exist on GraphQL\Type\Definition\Type. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

216
                    /** @scrutinizer ignore-call */ 
217
                    $fieldExists = $value->offsetExists($fieldName);

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.

Loading history...
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 107
    public static function valueFromAST(?ValueNode $valueNode, Type $type, ?array $variables = null)
320
    {
321 107
        $undefined = Utils::undefined();
322
323 107
        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 106
        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 106
        if ($valueNode instanceof NullValueNode) {
339
            // This is explicitly returning the value null.
340 9
            return null;
341
        }
342
343 104
        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 95
        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)) {
0 ignored issues
show
Bug introduced by
$itemNode of type GraphQL\Language\AST\Node is incompatible with the type GraphQL\Language\AST\ValueNode expected by parameter $valueNode of GraphQL\Utils\AST::isMissingVariable(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

369
                    if (self::isMissingVariable(/** @scrutinizer ignore-type */ $itemNode, $variables)) {
Loading history...
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);
0 ignored issues
show
Bug introduced by
$itemNode of type GraphQL\Language\AST\Node is incompatible with the type GraphQL\Language\AST\ValueNode|null expected by parameter $valueNode of GraphQL\Utils\AST::valueFromAST(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

378
                        $itemValue = self::valueFromAST(/** @scrutinizer ignore-type */ $itemNode, $itemType, $variables);
Loading history...
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 94
        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
Bug introduced by
The property value does not seem to exist on GraphQL\Language\AST\ObjectValueNode.
Loading history...
Bug introduced by
$fieldNode->value of type boolean|string is incompatible with the type GraphQL\Language\AST\ValueNode expected by parameter $valueNode of GraphQL\Utils\AST::isMissingVariable(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

417
                if ($fieldNode === null || self::isMissingVariable(/** @scrutinizer ignore-type */ $fieldNode->value, $variables)) {
Loading history...
Bug introduced by
The property value does not exist on GraphQL\Language\AST\ListValueNode. Did you mean values?
Loading history...
Bug introduced by
The property value does not seem to exist on GraphQL\Language\AST\NullValueNode.
Loading history...
Bug introduced by
The property value does not seem to exist on GraphQL\Language\AST\VariableNode.
Loading history...
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,
0 ignored issues
show
Bug introduced by
It seems like $fieldNode !== null ? $fieldNode->value : null can also be of type boolean and string; however, parameter $valueNode of GraphQL\Utils\AST::valueFromAST() does only seem to accept GraphQL\Language\AST\ValueNode|null, 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 ignore-type  annotation

428
                    /** @scrutinizer ignore-type */ $fieldNode !== null ? $fieldNode->value : null,
Loading history...
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 94
        if ($type instanceof EnumType) {
444 9
            if (! $valueNode instanceof EnumValueNode) {
445 1
                return $undefined;
446
            }
447 9
            $enumValue = $type->getValue($valueNode->value);
448 9
            if (! $enumValue) {
449
                return $undefined;
450
            }
451
452 9
            return $enumValue->value;
453
        }
454
455 87
        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 87
                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) {
0 ignored issues
show
introduced by
$inputTypeNode is always a sub-type of GraphQL\Language\AST\NamedTypeNode.
Loading history...
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|string
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