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

ValuesResolver::coerceValueForVariableNode()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 5
eloc 13
c 3
b 0
f 0
nc 4
nop 5
dl 0
loc 29
rs 9.5222
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 function isInputType(
309
        ?TypeInterface $type
310
    ) {
311
        return ($type instanceof ScalarType) ||
312
            ($type instanceof EnumType) ||
313
            ($type instanceof InputObjectType) ||
314
            (($type instanceof WrappingTypeInterface) && $this->isInputType($type->getOfType()));
315
    }
316
317
    /**
318
     * Returns either a value which is valid for the provided type or a list of
319
     * encountered coercion errors.
320
     *
321
     * @param mixed|array   $value
322
     * @param mixed         $type
323
     * @param NodeInterface $blameNode
324
     * @param Path|null     $path
325
     * @return CoercedValue
326
     * @throws GraphQLException
327
     * @throws InvariantException
328
     */
329
    private function coerceValue(
330
        $value,
331
        $type,
332
        $blameNode,
333
        ?Path $path = null
334
    ): CoercedValue {
335
        if ($type instanceof NonNullType) {
336
            return $this->coerceValueForNonNullType($value, $type, $blameNode, $path);
337
        }
338
339
        if (null === $value) {
340
            return new CoercedValue(null);
341
        }
342
343
        if ($type instanceof ScalarType) {
344
            return $this->coerceValueForScalarType($value, $type, $blameNode, $path);
345
        }
346
347
        if ($type instanceof EnumType) {
348
            return $this->coerceValueForEnumType($value, $type, $blameNode, $path);
349
        }
350
351
        if ($type instanceof ListType) {
352
            return $this->coerceValueForListType($value, $type, $blameNode, $path);
353
        }
354
355
        if ($type instanceof InputObjectType) {
356
            return $this->coerceValueForInputObjectType($value, $type, $blameNode, $path);
357
        }
358
359
        throw new GraphQLException('Unexpected type.');
360
    }
361
362
    /**
363
     * @param mixed         $value
364
     * @param NonNullType   $type
365
     * @param NodeInterface $blameNode
366
     * @param Path|null     $path
367
     * @return CoercedValue
368
     * @throws GraphQLException
369
     * @throws InvariantException
370
     */
371
    protected function coerceValueForNonNullType(
372
        $value,
373
        NonNullType $type,
374
        NodeInterface $blameNode,
375
        ?Path $path
376
    ): CoercedValue {
377
        if (null === $value) {
378
            return new CoercedValue(null, [
379
                $this->buildCoerceException(
380
                    sprintf('Expected non-nullable type %s not to be null', (string)$type),
381
                    $blameNode,
382
                    $path
383
                )
384
            ]);
385
        }
386
        return $this->coerceValue($value, $type->getOfType(), $blameNode, $path);
387
    }
388
389
    /**
390
     * Scalars determine if a value is valid via parseValue(), which can
391
     * throw to indicate failure. If it throws, maintain a reference to
392
     * the original error.
393
     *
394
     * @param mixed         $value
395
     * @param ScalarType    $type
396
     * @param NodeInterface $blameNode
397
     * @param Path|null     $path
398
     * @return CoercedValue
399
     */
400
    protected function coerceValueForScalarType(
401
        $value,
402
        ScalarType $type,
403
        NodeInterface $blameNode,
404
        ?Path $path
405
    ): CoercedValue {
406
        try {
407
            $parseResult = $type->parseValue($value);
408
            if (null === $parseResult) {
409
                return new CoercedValue(null, [
410
                    new GraphQLException(sprintf('Expected type %s', (string)$type))
411
                ]);
412
            }
413
            return new CoercedValue($parseResult);
414
        } catch (InvalidTypeException|CoercingException $ex) {
415
            return new CoercedValue(null, [
416
                $this->buildCoerceException(
417
                    sprintf('Expected type %s', (string)$type),
418
                    $blameNode,
419
                    $path,
420
                    $ex->getMessage(),
421
                    $ex
422
                )
423
            ]);
424
        }
425
    }
426
427
    /**
428
     * @param mixed         $value
429
     * @param EnumType      $type
430
     * @param NodeInterface $blameNode
431
     * @param Path|null     $path
432
     * @return CoercedValue
433
     * @throws InvariantException
434
     */
435
    protected function coerceValueForEnumType(
436
        $value,
437
        EnumType $type,
438
        NodeInterface $blameNode,
439
        ?Path $path
440
    ): CoercedValue {
441
        if (\is_string($value) && null !== ($enumValue = $type->getValue($value))) {
442
            return new CoercedValue($enumValue);
443
        }
444
445
        $suggestions = suggestionList((string)$value, \array_map(function (EnumValue $enumValue) {
446
            return $enumValue->getName();
447
        }, $type->getValues()));
448
449
        $didYouMean = (!empty($suggestions))
450
            ? 'did you mean' . \implode(',', $suggestions)
451
            : null;
452
453
        return new CoercedValue(null, [
454
            $this->buildCoerceException(\sprintf('Expected type %s', $type->getName()), $blameNode, $path, $didYouMean)
455
        ]);
456
    }
457
458
    /**
459
     * @param mixed           $value
460
     * @param InputObjectType $type
461
     * @param NodeInterface   $blameNode
462
     * @param Path|null       $path
463
     * @return CoercedValue
464
     * @throws GraphQLException
465
     * @throws InvariantException
466
     */
467
    protected function coerceValueForInputObjectType(
468
        $value,
469
        InputObjectType $type,
470
        NodeInterface $blameNode,
471
        ?Path $path
472
    ): CoercedValue {
473
        $errors        = [];
474
        $coercedValues = [];
475
        $fields        = $type->getFields();
476
477
        // Ensure every defined field is valid.
478
        foreach ($fields as $field) {
479
            $fieldType = $field->getType();
480
481
            if (!isset($value[$field->getName()])) {
482
                if (!empty($field->getDefaultValue())) {
483
                    $coercedValue[$field->getName()] = $field->getDefaultValue();
484
                } elseif ($fieldType instanceof NonNullType) {
485
                    $errors[] = new GraphQLException(
486
                        \sprintf(
487
                            "Field %s of required type %s! was not provided.",
488
                            $this->printPath(new Path($path, $field->getName())),
489
                            (string)$fieldType->getOfType()
490
                        )
491
                    );
492
                }
493
            } else {
494
                $fieldValue   = $value[$field->getName()];
495
                $coercedValue = $this->coerceValue(
496
                    $fieldValue,
497
                    $fieldType,
498
                    $blameNode,
499
                    new Path($path, $field->getName())
500
                );
501
502
                if ($coercedValue->hasErrors()) {
503
                    $errors = \array_merge($errors, $coercedValue->getErrors());
504
                } elseif (empty($errors)) {
505
                    $coercedValues[$field->getName()] = $coercedValue->getValue();
506
                }
507
            }
508
        }
509
510
        // Ensure every provided field is defined.
511
        foreach ($value as $fieldName => $fieldValue) {
512
            if (!isset($fields[$fieldName])) {
513
                $suggestions = suggestionList($fieldName, \array_keys($fields));
514
                $didYouMean  = (!empty($suggestions))
515
                    ? 'did you mean' . \implode(',', $suggestions)
516
                    : null;
517
518
                $errors[] = $this->buildCoerceException(
519
                    \sprintf('Field "%s" is not defined by type %s', $fieldName, $type->getName()),
520
                    $blameNode,
521
                    $path,
522
                    $didYouMean
523
                );
524
            }
525
        }
526
527
        return new CoercedValue($coercedValues, $errors);
528
    }
529
530
    /**
531
     * @param mixed         $value
532
     * @param ListType      $type
533
     * @param NodeInterface $blameNode
534
     * @param Path|null     $path
535
     * @return CoercedValue
536
     * @throws GraphQLException
537
     * @throws InvariantException
538
     */
539
    protected function coerceValueForListType(
540
        $value,
541
        ListType $type,
542
        NodeInterface $blameNode,
543
        ?Path $path
544
    ): CoercedValue {
545
        $itemType = $type->getOfType();
546
547
        if (\is_array($value) || $value instanceof \Traversable) {
548
            $errors        = [];
549
            $coercedValues = [];
550
            foreach ($value as $index => $itemValue) {
551
                $coercedValue = $this->coerceValue($itemValue, $itemType, $blameNode, new Path($path, $index));
552
553
                if ($coercedValue->hasErrors()) {
554
                    $errors = \array_merge($errors, $coercedValue->getErrors());
555
                } else {
556
                    $coercedValues[] = $coercedValue->getValue();
557
                }
558
            }
559
560
            return new CoercedValue($coercedValues, $errors);
561
        }
562
563
        // Lists accept a non-list value as a list of one.
564
        $coercedValue = $this->coerceValue($value, $itemType, $blameNode);
565
566
        return new CoercedValue([$coercedValue->getValue()], $coercedValue->getErrors());
567
    }
568
569
    /**
570
     * @param string                $message
571
     * @param NodeInterface         $blameNode
572
     * @param Path|null             $path
573
     * @param null|string           $subMessage
574
     * @param GraphQLException|null $originalException
575
     * @return GraphQLException
576
     */
577
    protected function buildCoerceException(
578
        string $message,
579
        NodeInterface $blameNode,
580
        ?Path $path,
581
        ?string $subMessage = null,
582
        ?GraphQLException $originalException = null
583
    ) {
584
        $stringPath = $this->printPath($path);
585
586
        return new CoercingException(
587
            $message .
588
            (($stringPath !== '') ? ' at ' . $stringPath : $stringPath) .
589
            (($subMessage !== null) ? '; ' . $subMessage : '.'),
590
            [$blameNode],
591
            null,
592
            null,
593
            // TODO: Change this to null
594
            [],
595
            null,
596
            $originalException
597
        );
598
    }
599
600
    /**
601
     * @param Path|null $path
602
     * @return string
603
     */
604
    protected function printPath(
605
        ?Path $path
606
    ) {
607
        $stringPath  = '';
608
        $currentPath = $path;
609
610
        while ($currentPath !== null) {
611
            $stringPath = \is_string($currentPath->getKey())
612
                ? '.' . $currentPath->getKey() . $stringPath
613
                : '[' . (string)$currentPath->getKey() . ']' . $stringPath;
614
615
            $currentPath = $currentPath->getPrevious();
616
        }
617
618
        return !empty($stringPath) ? 'value' . $stringPath : '';
619
    }
620
621
    /**
622
     * @param VariableNode  $variableNode
623
     * @param TypeInterface $argumentType
624
     * @param string        $argumentName
625
     * @param array         $variableValues
626
     * @param mixed         $defaultValue
627
     * @return mixed
628
     * @throws ExecutionException
629
     */
630
    protected function coerceValueForVariableNode(
631
        VariableNode $variableNode,
632
        TypeInterface $argumentType,
633
        string $argumentName,
634
        array $variableValues,
635
        $defaultValue
636
    ) {
637
        $variableName = $variableNode->getNameValue();
638
639
        if (!empty($variableValues) && isset($variableValues[$variableName])) {
640
            // Note: this does not check that this variable value is correct.
641
            // This assumes that this query has been validated and the variable
642
            // usage here is of the correct type.
643
            return $variableValues[$variableName];
644
        }
645
646
        if (null !== $defaultValue) {
647
            return $defaultValue;
648
        }
649
650
        if ($argumentType instanceof NonNullType) {
651
            throw new ExecutionException(
652
                \sprintf(
653
                    'Argument "%s" of required type "%s" was provided the variable "$%s" which was not provided a runtime value.',
654
                    $argumentName,
655
                    $argumentType,
656
                    $variableName
657
                ),
658
                [$variableNode]
659
            );
660
        }
661
    }
662
}
663