Completed
Pull Request — master (#349)
by Christoffer
04:04 queued 01:38
created

ValuesResolver::coerceVariableValues()   C

Complexity

Conditions 15
Paths 31

Size

Total Lines 86
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 53
nc 31
nop 3
dl 0
loc 86
rs 5.9166
c 1
b 0
f 0

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
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\NodeKindEnum;
13
use Digia\GraphQL\Language\Node\NullValueNode;
14
use Digia\GraphQL\Language\Node\VariableDefinitionNode;
15
use Digia\GraphQL\Language\Node\VariableNode;
16
use Digia\GraphQL\Schema\Schema;
17
use Digia\GraphQL\Type\Coercer\CoercingException;
18
use Digia\GraphQL\Type\Definition\Directive;
19
use Digia\GraphQL\Type\Definition\EnumType;
20
use Digia\GraphQL\Type\Definition\EnumValue;
21
use Digia\GraphQL\Type\Definition\Field;
22
use Digia\GraphQL\Type\Definition\InputObjectType;
23
use Digia\GraphQL\Type\Definition\ListType;
24
use Digia\GraphQL\Type\Definition\NonNullType;
25
use Digia\GraphQL\Type\Definition\ScalarType;
26
use Digia\GraphQL\Type\Definition\TypeInterface;
27
use Digia\GraphQL\Type\Definition\WrappingTypeInterface;
28
use Digia\GraphQL\Util\ConversionException;
29
use Digia\GraphQL\Util\TypeASTConverter;
30
use Digia\GraphQL\Util\ValueASTConverter;
31
use function Digia\GraphQL\printNode;
32
use function Digia\GraphQL\Test\jsonEncode;
33
use function Digia\GraphQL\Util\find;
34
use function Digia\GraphQL\Util\invariant;
35
use function Digia\GraphQL\Util\keyMap;
36
use function Digia\GraphQL\Util\suggestionList;
37
38
/**
39
 * TODO: Make this class static
40
 */
41
class ValuesResolver
42
{
43
    /**
44
     * Prepares an object map of argument values given a list of argument
45
     * definitions and list of argument AST nodes.
46
     *
47
     * @see https://facebook.github.io/graphql/October2016/#CoerceArgumentValues()
48
     *
49
     * @param Field|Directive         $definition
50
     * @param ArgumentsAwareInterface $node
51
     * @param array                   $variableValues
52
     * @return array
53
     * @throws ExecutionException
54
     * @throws InvalidTypeException
55
     * @throws InvariantException
56
     */
57
    public function coerceArgumentValues($definition, ArgumentsAwareInterface $node, array $variableValues = []): array
58
    {
59
        $coercedValues       = [];
60
        $argumentDefinitions = $definition->getArguments();
61
        $argumentNodes       = $node->getArguments();
62
63
        if (empty($argumentDefinitions)) {
64
            return $coercedValues;
65
        }
66
67
        /** @var ArgumentNode[] $argumentNodeMap */
68
        $argumentNodeMap = keyMap($argumentNodes, function (ArgumentNode $value) {
69
            return $value->getNameValue();
70
        });
71
72
        foreach ($argumentDefinitions as $argumentDefinition) {
73
            $argumentName  = $argumentDefinition->getName();
74
            $argumentType  = $argumentDefinition->getType();
75
            $argumentNode  = $argumentNodeMap[$argumentName] ?? null;
76
            $defaultValue  = $argumentDefinition->getDefaultValue();
77
            $argumentValue = null !== $argumentNode ? $argumentNode->getValue() : null;
78
79
            if (null !== $argumentNode && $argumentValue instanceof VariableNode) {
80
                $variableName = $argumentValue->getNameValue();
81
                $hasValue     = !empty($variableValues) && \array_key_exists($variableName, $variableValues);
82
                $isNull       = $hasValue && null === $variableValues[$variableName];
83
            } else {
84
                $hasValue = null !== $argumentNode;
85
                $isNull   = $hasValue && $argumentValue instanceof NullValueNode;
86
            }
87
88
            if (!$hasValue && null !== $defaultValue) {
89
                // If no argument was provided where the definition has a default value,
90
                // use the default value.
91
                $coercedValues[$argumentName] = $defaultValue;
92
            } elseif ((!$hasValue || $isNull) && $argumentType instanceof NonNullType) {
93
                // If no argument or a null value was provided to an argument with a
94
                // non-null type (required), produce a field error.
95
                if ($isNull) {
96
                    throw new ExecutionException(
97
                        \sprintf(
98
                            'Argument "%s" of non-null type "%s" must not be null.',
99
                            $argumentName,
100
                            $argumentType
101
                        ),
102
                        [$argumentValue]
103
                    );
104
                } elseif (null !== $argumentNode && $argumentValue instanceof VariableNode) {
105
                    $variableName = $argumentValue->getNameValue();
106
                    throw new ExecutionException(
107
                        \sprintf(
108
                            'Argument "%s" of required type "%s" was provided the variable "$%s" '
109
                            . 'which was not provided a runtime value.',
110
                            $argumentName,
111
                            $argumentType,
112
                            $variableName
113
                        ),
114
                        [$argumentValue]
115
                    );
116
                } else {
117
                    throw new ExecutionException(
118
                        \sprintf(
119
                            'Argument "%s" of required type "%s" was not provided.',
120
                            $argumentName,
121
                            $argumentType
122
                        ),
123
                        [$node]
124
                    );
125
                }
126
            } elseif ($hasValue) {
127
                if ($argumentValue instanceof NullValueNode) {
128
                    // If the explicit value `null` was provided, an entry in the coerced
129
                    // values must exist as the value `null`.
130
                    $coercedValues[$argumentName] = null;
131
                } elseif ($argumentValue instanceof VariableNode) {
132
                    $variableName = $argumentValue->getNameValue();
133
                    invariant(!empty($variableValues), 'Must exist for hasValue to be true.');
134
                    // Note: This does no further checking that this variable is correct.
135
                    // This assumes that this query has been validated and the variable
136
                    // usage here is of the correct type.
137
                    $coercedValues[$argumentName] = $variableValues[$variableName];
138
                } else {
139
                    $valueNode = $argumentNode->getValue();
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on null. ( Ignorable by Annotation )

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

139
                    /** @scrutinizer ignore-call */ 
140
                    $valueNode = $argumentNode->getValue();

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...
140
                    try {
141
                        // Value nodes that cannot be resolved should be treated as invalid values
142
                        // because there is no undefined value in PHP so that we throw an exception
143
                        $coercedValue = ValueASTConverter::convert($valueNode, $argumentType, $variableValues);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

143
                        $coercedValue = ValueASTConverter::convert($valueNode, /** @scrutinizer ignore-type */ $argumentType, $variableValues);
Loading history...
144
                    } catch (\Exception $ex) {
145
                        // Note: ValuesOfCorrectType validation should catch this before
146
                        // execution. This is a runtime check to ensure execution does not
147
                        // continue with an invalid argument value.
148
                        throw new ExecutionException(
149
                            \sprintf(
150
                                'Argument "%s" has invalid value %s.',
151
                                $argumentName,
152
                                (string)$argumentValue
153
                            ),
154
                            [$argumentValue],
155
                            null,
156
                            null,
157
                            null,
158
                            null,
159
                            $ex
160
                        );
161
                    }
162
                    $coercedValues[$argumentName] = $coercedValue;
163
                }
164
            }
165
        }
166
167
        return $coercedValues;
168
    }
169
170
    /**
171
     * Prepares an object map of argument values given a directive definition
172
     * and a AST node which may contain directives. Optionally also accepts a map
173
     * of variable values.
174
     *
175
     * If the directive does not exist on the node, returns null.
176
     *
177
     * @param Directive $directive
178
     * @param mixed     $node
179
     * @param array     $variableValues
180
     * @return array|null
181
     * @throws ExecutionException
182
     * @throws InvalidTypeException
183
     * @throws InvariantException
184
     */
185
    public function coerceDirectiveValues(
186
        Directive $directive,
187
        $node,
188
        array $variableValues = []
189
    ): ?array {
190
        $directiveNode = $node->hasDirectives()
191
            ? find($node->getDirectives(), function (NameAwareInterface $value) use ($directive) {
192
                return $value->getNameValue() === $directive->getName();
193
            }) : null;
194
195
        if (null !== $directiveNode) {
196
            return $this->coerceArgumentValues($directive, $directiveNode, $variableValues);
197
        }
198
199
        return null;
200
    }
201
202
    /**
203
     * Prepares an object map of variableValues of the correct type based on the
204
     * provided variable definitions and arbitrary input. If the input cannot be
205
     * parsed to match the variable definitions, a GraphQLError will be thrown.
206
     *
207
     * @param Schema                         $schema
208
     * @param array|VariableDefinitionNode[] $variableDefinitionNodes
209
     * @param array                          $inputs
210
     * @return CoercedValue
211
     * @throws GraphQLException
212
     * @throws InvariantException
213
     * @throws ConversionException
214
     */
215
    public function coerceVariableValues(
216
        Schema $schema,
217
        array $variableDefinitionNodes,
218
        array $inputs
219
    ): CoercedValue {
220
        $coercedValues = [];
221
        $errors        = [];
222
223
        foreach ($variableDefinitionNodes as $variableDefinitionNode) {
224
            $variableName     = $variableDefinitionNode->getVariable()->getNameValue();
225
            $variableType     = TypeASTConverter::convert($schema, $variableDefinitionNode->getType());
226
            $variableTypeName = (string)$variableType;
227
228
            if ($variableTypeName === '') {
229
                $variableTypeName = (string)$variableDefinitionNode;
230
            }
231
232
            if (!$this->isInputType($variableType)) {
233
                // Must use input types for variables. This should be caught during
234
                // validation, however is checked again here for safety.
235
                $errors[] = $this->buildCoerceException(
236
                    \sprintf(
237
                        'Variable "$%s" expected value of type "%s" which cannot be used as an input type',
238
                        $variableName,
239
                        $variableTypeName
240
                    ),
241
                    $variableDefinitionNode,
242
                    null
243
                );
244
            } else {
245
                $hasValue = \array_key_exists($variableName, $inputs);
246
                $value    = $hasValue ? $inputs[$variableName] : null;
247
                if (!$hasValue && $variableDefinitionNode->hasDefaultValue()) {
248
                    // If no value was provided to a variable with a default value,
249
                    // use the default value.
250
                    $coercedValues[$variableName] = ValueASTConverter::convert(
251
                        $variableDefinitionNode->getDefaultValue(),
252
                        $variableType
253
                    );
254
                } elseif ((!$hasValue || null === $value) && $variableType instanceof NonNullType) {
255
                    // If no value or a nullish value was provided to a variable with a
256
                    // non-null type (required), produce an error.
257
                    $errors[] = $this->buildCoerceException(
258
                        \sprintf(
259
                            $value
260
                                ? 'Variable "$%s" of non-null type "%s" must not be null'
261
                                : 'Variable "$%s" of required type "%s" was not provided',
262
                            $variableName,
263
                            $variableTypeName
264
                        ),
265
                        $variableDefinitionNode,
266
                        null
267
                    );
268
                } elseif ($hasValue) {
269
                    if (null === $value) {
270
                        // If the explicit value `null` was provided, an entry in the coerced
271
                        // values must exist as the value `null`.
272
                        $coercedValues[$variableName] = null;
273
                    } else {
274
                        // Otherwise, a non-null value was provided, coerce it to the expected
275
                        // type or report an error if coercion fails.
276
                        $coercedValue = $this->coerceValue($value, $variableType, $variableDefinitionNode);
277
                        if ($coercedValue->hasErrors()) {
278
                            $message = \sprintf(
279
                                'Variable "$%s" got invalid value %s',
280
                                $variableName,
281
                                jsonEncode($value)
282
                            );
283
                            foreach ($coercedValue->getErrors() as $error) {
284
                                $errors[] = $this->buildCoerceException(
285
                                    $message,
286
                                    $variableDefinitionNode,
287
                                    null,
288
                                    $error->getMessage(),
289
                                    $error
290
                                );
291
                            }
292
                        } else {
293
                            $coercedValues[$variableName] = $coercedValue->getValue();
294
                        }
295
                    }
296
                }
297
            }
298
        }
299
300
        return new CoercedValue($coercedValues, $errors);
301
    }
302
303
304
    /**
305
     * @param TypeInterface|null $type
306
     * @return bool
307
     */
308
    protected
309
    function isInputType(
310
        ?TypeInterface $type
311
    ) {
312
        return ($type instanceof ScalarType) ||
313
            ($type instanceof EnumType) ||
314
            ($type instanceof InputObjectType) ||
315
            (($type instanceof WrappingTypeInterface) && $this->isInputType($type->getOfType()));
316
    }
317
318
    /**
319
     * Returns either a value which is valid for the provided type or a list of
320
     * encountered coercion errors.
321
     *
322
     * @param mixed|array   $value
323
     * @param mixed         $type
324
     * @param NodeInterface $blameNode
325
     * @param Path|null     $path
326
     * @return CoercedValue
327
     * @throws GraphQLException
328
     * @throws InvariantException
329
     */
330
    private
331
    function coerceValue(
332
        $value,
333
        $type,
334
        $blameNode,
335
        ?Path $path = null
336
    ): CoercedValue {
337
        if ($type instanceof NonNullType) {
338
            return $this->coerceValueForNonNullType($value, $type, $blameNode, $path);
339
        }
340
341
        if (null === $value) {
342
            return new CoercedValue(null);
343
        }
344
345
        if ($type instanceof ScalarType) {
346
            return $this->coerceValueForScalarType($value, $type, $blameNode, $path);
347
        }
348
349
        if ($type instanceof EnumType) {
350
            return $this->coerceValueForEnumType($value, $type, $blameNode, $path);
351
        }
352
353
        if ($type instanceof ListType) {
354
            return $this->coerceValueForListType($value, $type, $blameNode, $path);
355
        }
356
357
        if ($type instanceof InputObjectType) {
358
            return $this->coerceValueForInputObjectType($value, $type, $blameNode, $path);
359
        }
360
361
        throw new GraphQLException('Unexpected type.');
362
    }
363
364
    /**
365
     * @param mixed         $value
366
     * @param NonNullType   $type
367
     * @param NodeInterface $blameNode
368
     * @param Path|null     $path
369
     * @return CoercedValue
370
     * @throws GraphQLException
371
     * @throws InvariantException
372
     */
373
    protected
374
    function coerceValueForNonNullType(
375
        $value,
376
        NonNullType $type,
377
        NodeInterface $blameNode,
378
        ?Path $path
379
    ): CoercedValue {
380
        if (null === $value) {
381
            return new CoercedValue(null, [
382
                $this->buildCoerceException(
383
                    sprintf('Expected non-nullable type %s not to be null', (string)$type),
384
                    $blameNode,
385
                    $path
386
                )
387
            ]);
388
        }
389
        return $this->coerceValue($value, $type->getOfType(), $blameNode, $path);
390
    }
391
392
    /**
393
     * Scalars determine if a value is valid via parseValue(), which can
394
     * throw to indicate failure. If it throws, maintain a reference to
395
     * the original error.
396
     *
397
     * @param mixed         $value
398
     * @param ScalarType    $type
399
     * @param NodeInterface $blameNode
400
     * @param Path|null     $path
401
     * @return CoercedValue
402
     */
403
    protected
404
    function coerceValueForScalarType(
405
        $value,
406
        ScalarType $type,
407
        NodeInterface $blameNode,
408
        ?Path $path
409
    ): CoercedValue {
410
        try {
411
            $parseResult = $type->parseValue($value);
412
            if (null === $parseResult) {
413
                return new CoercedValue(null, [
414
                    new GraphQLException(sprintf('Expected type %s', (string)$type))
415
                ]);
416
            }
417
            return new CoercedValue($parseResult);
418
        } catch (InvalidTypeException|CoercingException $ex) {
419
            return new CoercedValue(null, [
420
                $this->buildCoerceException(
421
                    sprintf('Expected type %s', (string)$type),
422
                    $blameNode,
423
                    $path,
424
                    $ex->getMessage(),
425
                    $ex
426
                )
427
            ]);
428
        }
429
    }
430
431
    /**
432
     * @param mixed         $value
433
     * @param EnumType      $type
434
     * @param NodeInterface $blameNode
435
     * @param Path|null     $path
436
     * @return CoercedValue
437
     * @throws InvariantException
438
     */
439
    protected
440
    function coerceValueForEnumType(
441
        $value,
442
        EnumType $type,
443
        NodeInterface $blameNode,
444
        ?Path $path
445
    ): CoercedValue {
446
        if (\is_string($value) && null !== ($enumValue = $type->getValue($value))) {
447
            return new CoercedValue($enumValue);
448
        }
449
450
        $suggestions = suggestionList((string)$value, \array_map(function (EnumValue $enumValue) {
451
            return $enumValue->getName();
452
        }, $type->getValues()));
453
454
        $didYouMean = (!empty($suggestions))
455
            ? 'did you mean' . \implode(',', $suggestions)
456
            : null;
457
458
        return new CoercedValue(null, [
459
            $this->buildCoerceException(\sprintf('Expected type %s', $type->getName()), $blameNode, $path, $didYouMean)
460
        ]);
461
    }
462
463
    /**
464
     * @param mixed           $value
465
     * @param InputObjectType $type
466
     * @param NodeInterface   $blameNode
467
     * @param Path|null       $path
468
     * @return CoercedValue
469
     * @throws GraphQLException
470
     * @throws InvariantException
471
     */
472
    protected
473
    function coerceValueForInputObjectType(
474
        $value,
475
        InputObjectType $type,
476
        NodeInterface $blameNode,
477
        ?Path $path
478
    ): CoercedValue {
479
        $errors        = [];
480
        $coercedValues = [];
481
        $fields        = $type->getFields();
482
483
        // Ensure every defined field is valid.
484
        foreach ($fields as $field) {
485
            $fieldType = $field->getType();
486
487
            if (!isset($value[$field->getName()])) {
488
                if (!empty($field->getDefaultValue())) {
489
                    $coercedValue[$field->getName()] = $field->getDefaultValue();
490
                } elseif ($fieldType instanceof NonNullType) {
491
                    $errors[] = new GraphQLException(
492
                        \sprintf(
493
                            "Field %s of required type %s! was not provided.",
494
                            $this->printPath(new Path($path, $field->getName())),
495
                            (string)$fieldType->getOfType()
496
                        )
497
                    );
498
                }
499
            } else {
500
                $fieldValue   = $value[$field->getName()];
501
                $coercedValue = $this->coerceValue(
502
                    $fieldValue,
503
                    $fieldType,
504
                    $blameNode,
505
                    new Path($path, $field->getName())
506
                );
507
508
                if ($coercedValue->hasErrors()) {
509
                    $errors = \array_merge($errors, $coercedValue->getErrors());
510
                } elseif (empty($errors)) {
511
                    $coercedValues[$field->getName()] = $coercedValue->getValue();
512
                }
513
            }
514
        }
515
516
        // Ensure every provided field is defined.
517
        foreach ($value as $fieldName => $fieldValue) {
518
            if (!isset($fields[$fieldName])) {
519
                $suggestions = suggestionList($fieldName, \array_keys($fields));
520
                $didYouMean  = (!empty($suggestions))
521
                    ? 'did you mean' . \implode(',', $suggestions)
522
                    : null;
523
524
                $errors[] = $this->buildCoerceException(
525
                    \sprintf('Field "%s" is not defined by type %s', $fieldName, $type->getName()),
526
                    $blameNode,
527
                    $path,
528
                    $didYouMean
529
                );
530
            }
531
        }
532
533
        return new CoercedValue($coercedValues, $errors);
534
    }
535
536
    /**
537
     * @param mixed         $value
538
     * @param ListType      $type
539
     * @param NodeInterface $blameNode
540
     * @param Path|null     $path
541
     * @return CoercedValue
542
     * @throws GraphQLException
543
     * @throws InvariantException
544
     */
545
    protected
546
    function coerceValueForListType(
547
        $value,
548
        ListType $type,
549
        NodeInterface $blameNode,
550
        ?Path $path
551
    ): CoercedValue {
552
        $itemType = $type->getOfType();
553
554
        if (\is_array($value) || $value instanceof \Traversable) {
555
            $errors        = [];
556
            $coercedValues = [];
557
            foreach ($value as $index => $itemValue) {
558
                $coercedValue = $this->coerceValue($itemValue, $itemType, $blameNode, new Path($path, $index));
559
560
                if ($coercedValue->hasErrors()) {
561
                    $errors = \array_merge($errors, $coercedValue->getErrors());
562
                } else {
563
                    $coercedValues[] = $coercedValue->getValue();
564
                }
565
            }
566
567
            return new CoercedValue($coercedValues, $errors);
568
        }
569
570
        // Lists accept a non-list value as a list of one.
571
        $coercedValue = $this->coerceValue($value, $itemType, $blameNode);
572
573
        return new CoercedValue([$coercedValue->getValue()], $coercedValue->getErrors());
574
    }
575
576
    /**
577
     * @param string                $message
578
     * @param NodeInterface         $blameNode
579
     * @param Path|null             $path
580
     * @param null|string           $subMessage
581
     * @param GraphQLException|null $originalException
582
     * @return GraphQLException
583
     */
584
    protected
585
    function buildCoerceException(
586
        string $message,
587
        NodeInterface $blameNode,
588
        ?Path $path,
589
        ?string $subMessage = null,
590
        ?GraphQLException $originalException = null
591
    ) {
592
        $stringPath = $this->printPath($path);
593
594
        return new CoercingException(
595
            $message .
596
            (($stringPath !== '') ? ' at ' . $stringPath : $stringPath) .
597
            (($subMessage !== null) ? '; ' . $subMessage : '.'),
598
            [$blameNode],
599
            null,
600
            null,
601
            // TODO: Change this to null
602
            [],
603
            null,
604
            $originalException
605
        );
606
    }
607
608
    /**
609
     * @param Path|null $path
610
     * @return string
611
     */
612
    protected
613
    function printPath(
614
        ?Path $path
615
    ) {
616
        $stringPath  = '';
617
        $currentPath = $path;
618
619
        while ($currentPath !== null) {
620
            $stringPath = \is_string($currentPath->getKey())
621
                ? '.' . $currentPath->getKey() . $stringPath
622
                : '[' . (string)$currentPath->getKey() . ']' . $stringPath;
623
624
            $currentPath = $currentPath->getPrevious();
625
        }
626
627
        return !empty($stringPath) ? 'value' . $stringPath : '';
628
    }
629
630
    /**
631
     * @param VariableNode  $variableNode
632
     * @param TypeInterface $argumentType
633
     * @param string        $argumentName
634
     * @param array         $variableValues
635
     * @param mixed         $defaultValue
636
     * @return mixed
637
     * @throws ExecutionException
638
     */
639
    protected
640
    function coerceValueForVariableNode(
641
        VariableNode $variableNode,
642
        TypeInterface $argumentType,
643
        string $argumentName,
644
        array $variableValues,
645
        $defaultValue
646
    ) {
647
        $variableName = $variableNode->getNameValue();
648
649
        if (!empty($variableValues) && isset($variableValues[$variableName])) {
650
            // Note: this does not check that this variable value is correct.
651
            // This assumes that this query has been validated and the variable
652
            // usage here is of the correct type.
653
            return $variableValues[$variableName];
654
        }
655
656
        if (null !== $defaultValue) {
657
            return $defaultValue;
658
        }
659
660
        if ($argumentType instanceof NonNullType) {
661
            throw new ExecutionException(
662
                \sprintf(
663
                    'Argument "%s" of required type "%s" was provided the variable "$%s" which was not provided a runtime value.',
664
                    $argumentName,
665
                    $argumentType,
666
                    $variableName
667
                ),
668
                [$variableNode]
669
            );
670
        }
671
    }
672
}
673