Completed
Pull Request — master (#349)
by Christoffer
02:52
created

ValuesResolver::coerceArgumentValues()   D

Complexity

Conditions 21
Paths 110

Size

Total Lines 158
Code Lines 72

Duplication

Lines 0
Ratio 0 %

Importance

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