Passed
Push — master ( 5fb970...6cce67 )
by Vladimir
08:12 queued 11s
created

AST::astFromValue()   D

Complexity

Conditions 37
Paths 72

Size

Total Lines 139
Code Lines 75

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 61
CRAP Score 43.0828

Importance

Changes 0
Metric Value
eloc 75
dl 0
loc 139
ccs 61
cts 73
cp 0.8356
rs 4.1666
c 0
b 0
f 0
cc 37
nc 72
nop 2
crap 43.0828

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 27
    public static function astFromValue($value, InputType $type)
157
    {
158 27
        if ($type instanceof NonNull) {
159 4
            $astValue = self::astFromValue($value, $type->getWrappedType());
0 ignored issues
show
Bug introduced by
It seems like $type->getWrappedType() can also be of type GraphQL\Type\Definition\InterfaceType and GraphQL\Type\Definition\ObjectType and GraphQL\Type\Definition\UnionType; however, parameter $type of GraphQL\Utils\AST::astFromValue() does only seem to accept GraphQL\Type\Definition\InputType, 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

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 27
        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 25
        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' => $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 25
        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 GraphQL\Type\Definition\Type and ArrayAccess 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
                    /** @var ArrayAccess $value */
217
                    $fieldExists = $value->offsetExists($fieldName);
218
                } else {
219
                    $fieldExists = property_exists($value, $fieldName);
220
                }
221
222 2
                if (! $fieldExists) {
223 1
                    continue;
224
                }
225
226 2
                $fieldNode = self::astFromValue($fieldValue, $field->getType());
227
228 2
                if (! $fieldNode) {
229
                    continue;
230
                }
231
232 2
                $fieldNodes[] = new ObjectFieldNode([
233 2
                    'name'  => new NameNode(['value' => $fieldName]),
234 2
                    'value' => $fieldNode,
235
                ]);
236
            }
237
238 2
            return new ObjectValueNode(['fields' => $fieldNodes]);
239
        }
240
241 24
        if ($type instanceof ScalarType || $type instanceof EnumType) {
242
            // Since value is an internally represented value, it must be serialized
243
            // to an externally represented value before converting into an AST.
244
            try {
245 24
                $serialized = $type->serialize($value);
246 3
            } catch (Exception $error) {
247 3
                if ($error instanceof Error && $type instanceof EnumType) {
248 1
                    return null;
249
                }
250 2
                throw $error;
251
            } catch (Throwable $error) {
252
                if ($error instanceof Error && $type instanceof EnumType) {
253
                    return null;
254
                }
255
                throw $error;
256
            }
257
258
            // Others serialize based on their corresponding PHP scalar types.
259 22
            if (is_bool($serialized)) {
260 6
                return new BooleanValueNode(['value' => $serialized]);
261
            }
262 20
            if (is_int($serialized)) {
263 5
                return new IntValueNode(['value' => $serialized]);
264
            }
265 15
            if (is_float($serialized)) {
266
                // int cast with == used for performance reasons
267
                // phpcs:ignore
268 2
                if ((int) $serialized == $serialized) {
269 2
                    return new IntValueNode(['value' => $serialized]);
270
                }
271
272 1
                return new FloatValueNode(['value' => $serialized]);
273
            }
274 14
            if (is_string($serialized)) {
275
                // Enum types use Enum literals.
276 14
                if ($type instanceof EnumType) {
277 4
                    return new EnumValueNode(['value' => $serialized]);
278
                }
279
280
                // ID types can use Int literals.
281 12
                $asInt = (int) $serialized;
282 12
                if ($type instanceof IDType && (string) $asInt === $serialized) {
283 1
                    return new IntValueNode(['value' => $serialized]);
284
                }
285
286
                // Use json_encode, which uses the same string encoding as GraphQL,
287
                // then remove the quotes.
288 12
                return new StringValueNode(['value' => $serialized]);
289
            }
290
291
            throw new InvariantViolation('Cannot convert value to AST: ' . Utils::printSafe($serialized));
292
        }
293
294
        throw new Error('Unknown type: ' . Utils::printSafe($type) . '.');
295
    }
296
297
    /**
298
     * Produces a PHP value given a GraphQL Value AST.
299
     *
300
     * A GraphQL type must be provided, which will be used to interpret different
301
     * GraphQL Value literals.
302
     *
303
     * Returns `null` when the value could not be validly coerced according to
304
     * the provided type.
305
     *
306
     * | GraphQL Value        | PHP Value     |
307
     * | -------------------- | ------------- |
308
     * | Input Object         | Assoc Array   |
309
     * | List                 | Array         |
310
     * | Boolean              | Boolean       |
311
     * | String               | String        |
312
     * | Int / Float          | Int / Float   |
313
     * | Enum Value           | Mixed         |
314
     * | Null Value           | null          |
315
     *
316
     * @param ValueNode|null $valueNode
317
     * @param mixed[]|null   $variables
318
     *
319
     * @return mixed[]|stdClass|null
320
     *
321
     * @throws Exception
322
     *
323
     * @api
324
     */
325 83
    public static function valueFromAST($valueNode, InputType $type, ?array $variables = null)
326
    {
327 83
        $undefined = Utils::undefined();
328
329 83
        if ($valueNode === null) {
330
            // When there is no AST, then there is also no value.
331
            // Importantly, this is different from returning the GraphQL null value.
332 1
            return $undefined;
333
        }
334
335 82
        if ($type instanceof NonNull) {
336 37
            if ($valueNode instanceof NullValueNode) {
337
                // Invalid: intentionally return no value.
338 5
                return $undefined;
339
            }
340
341 36
            return self::valueFromAST($valueNode, $type->getWrappedType(), $variables);
0 ignored issues
show
Bug introduced by
It seems like $type->getWrappedType() can also be of type GraphQL\Type\Definition\InterfaceType and GraphQL\Type\Definition\ObjectType and GraphQL\Type\Definition\UnionType; however, parameter $type of GraphQL\Utils\AST::valueFromAST() does only seem to accept GraphQL\Type\Definition\InputType, 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

341
            return self::valueFromAST($valueNode, /** @scrutinizer ignore-type */ $type->getWrappedType(), $variables);
Loading history...
342
        }
343
344 82
        if ($valueNode instanceof NullValueNode) {
345
            // This is explicitly returning the value null.
346 8
            return null;
347
        }
348
349 80
        if ($valueNode instanceof VariableNode) {
350 3
            $variableName = $valueNode->name->value;
351
352 3
            if (! $variables || ! array_key_exists($variableName, $variables)) {
353
                // No valid return value.
354 1
                return $undefined;
355
            }
356
            // Note: we're not doing any checking that this variable is correct. We're
357
            // assuming that this query has been validated and the variable usage here
358
            // is of the correct type.
359 3
            return $variables[$variableName];
360
        }
361
362 79
        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 78
        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
                /** @var ValueNode $fieldNode */
414 4
                $fieldName = $field->name;
415 4
                $fieldNode = $fieldNodes[$fieldName] ?? null;
416
417 4
                if ($fieldNode === null || self::isMissingVariable($fieldNode->value, $variables)) {
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 78
        if ($type instanceof EnumType) {
444 6
            if (! $valueNode instanceof EnumValueNode) {
445 1
                return $undefined;
446
            }
447 6
            $enumValue = $type->getValue($valueNode->value);
448 6
            if (! $enumValue) {
449
                return $undefined;
450
            }
451
452 6
            return $enumValue->value;
453
        }
454
455 73
        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 73
                return $type->parseLiteral($valueNode, $variables);
461 8
            } catch (Exception $error) {
462 8
                return $undefined;
463
            } catch (Throwable $error) {
464
                return $undefined;
465
            }
466
        }
467
468
        throw new Error('Unknown type: ' . Utils::printSafe($type) . '.');
469
    }
470
471
    /**
472
     * Returns true if the provided valueNode is a variable which is not defined
473
     * in the set of variables.
474
     *
475
     * @param ValueNode $valueNode
476
     * @param mixed[]   $variables
477
     *
478
     * @return bool
479
     */
480 9
    private static function isMissingVariable($valueNode, $variables)
481
    {
482 9
        return $valueNode instanceof VariableNode &&
483 9
            (count($variables) === 0 || ! array_key_exists($valueNode->name->value, $variables));
484
    }
485
486
    /**
487
     * Produces a PHP value given a GraphQL Value AST.
488
     *
489
     * Unlike `valueFromAST()`, no type is provided. The resulting PHP value
490
     * will reflect the provided GraphQL value AST.
491
     *
492
     * | GraphQL Value        | PHP Value     |
493
     * | -------------------- | ------------- |
494
     * | Input Object         | Assoc Array   |
495
     * | List                 | Array         |
496
     * | Boolean              | Boolean       |
497
     * | String               | String        |
498
     * | Int / Float          | Int / Float   |
499
     * | Enum                 | Mixed         |
500
     * | Null                 | null          |
501
     *
502
     * @param Node         $valueNode
503
     * @param mixed[]|null $variables
504
     *
505
     * @return mixed
506
     *
507
     * @throws Exception
508
     *
509
     * @api
510
     */
511 6
    public static function valueFromASTUntyped($valueNode, ?array $variables = null)
512
    {
513
        switch (true) {
514 6
            case $valueNode instanceof NullValueNode:
515 2
                return null;
516 6
            case $valueNode instanceof IntValueNode:
517 3
                return intval($valueNode->value, 10);
518 5
            case $valueNode instanceof FloatValueNode:
519 2
                return floatval($valueNode->value);
520 5
            case $valueNode instanceof StringValueNode:
521 5
            case $valueNode instanceof EnumValueNode:
522 5
            case $valueNode instanceof BooleanValueNode:
523 4
                return $valueNode->value;
524 4
            case $valueNode instanceof ListValueNode:
525 4
                return array_map(
526
                    static function ($node) use ($variables) {
527 4
                        return self::valueFromASTUntyped($node, $variables);
528 4
                    },
529 4
                    iterator_to_array($valueNode->values)
530
                );
531 2
            case $valueNode instanceof ObjectValueNode:
532 2
                return array_combine(
533 2
                    array_map(
534
                        static function ($field) {
535 2
                            return $field->name->value;
536 2
                        },
537 2
                        iterator_to_array($valueNode->fields)
538
                    ),
539 2
                    array_map(
540
                        static function ($field) use ($variables) {
541 2
                            return self::valueFromASTUntyped($field->value, $variables);
542 2
                        },
543 2
                        iterator_to_array($valueNode->fields)
544
                    )
545
                );
546 1
            case $valueNode instanceof VariableNode:
547 1
                $variableName = $valueNode->name->value;
548
549 1
                return $variables && isset($variables[$variableName])
550 1
                    ? $variables[$variableName]
551 1
                    : null;
552
        }
553
554
        throw new Error('Unexpected value kind: ' . $valueNode->kind . '.');
555
    }
556
557
    /**
558
     * Returns type definition for given AST Type node
559
     *
560
     * @param NamedTypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode
561
     *
562
     * @return Type|null
563
     *
564
     * @throws Exception
565
     *
566
     * @api
567
     */
568 333
    public static function typeFromAST(Schema $schema, $inputTypeNode)
569
    {
570 333
        if ($inputTypeNode instanceof ListTypeNode) {
571 19
            $innerType = self::typeFromAST($schema, $inputTypeNode->type);
572
573 19
            return $innerType ? new ListOfType($innerType) : null;
574
        }
575 333
        if ($inputTypeNode instanceof NonNullTypeNode) {
576 44
            $innerType = self::typeFromAST($schema, $inputTypeNode->type);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $innerType is correct as self::typeFromAST($schema, $inputTypeNode->type) targeting GraphQL\Utils\AST::typeFromAST() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
It seems like $inputTypeNode->type can also be of type GraphQL\Language\AST\NameNode; however, parameter $inputTypeNode of GraphQL\Utils\AST::typeFromAST() does only seem to accept GraphQL\Language\AST\Lis...age\AST\NonNullTypeNode, 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

576
            $innerType = self::typeFromAST($schema, /** @scrutinizer ignore-type */ $inputTypeNode->type);
Loading history...
577
578 44
            return $innerType ? new NonNull($innerType) : null;
0 ignored issues
show
introduced by
$innerType is of type null, thus it always evaluated to false.
Loading history...
579
        }
580 333
        if ($inputTypeNode instanceof NamedTypeNode) {
0 ignored issues
show
introduced by
$inputTypeNode is always a sub-type of GraphQL\Language\AST\NamedTypeNode.
Loading history...
581 333
            return $schema->getType($inputTypeNode->name->value);
582
        }
583
584
        throw new Error('Unexpected type kind: ' . $inputTypeNode->kind . '.');
585
    }
586
587
    /**
588
     * Returns operation type ("query", "mutation" or "subscription") given a document and operation name
589
     *
590
     * @param string $operationName
591
     *
592
     * @return bool
593
     *
594
     * @api
595
     */
596 23
    public static function getOperation(DocumentNode $document, $operationName = null)
597
    {
598 23
        if ($document->definitions) {
599 23
            foreach ($document->definitions as $def) {
600 23
                if (! ($def instanceof OperationDefinitionNode)) {
601
                    continue;
602
                }
603
604 23
                if (! $operationName || (isset($def->name->value) && $def->name->value === $operationName)) {
605 23
                    return $def->operation;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $def->operation returns the type string which is incompatible with the documented return type boolean.
Loading history...
606
                }
607
            }
608
        }
609
610
        return false;
611
    }
612
}
613