Completed
Push — master ( 8a1587...b18e13 )
by Quang
02:30
created

ExecutionStrategy::resolveField()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 20
nc 2
nop 4
dl 0
loc 37
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL\Execution;
4
5
use Digia\GraphQL\Error\ExecutionException;
6
use Digia\GraphQL\Error\InvalidTypeException;
7
use Digia\GraphQL\Execution\Resolver\ResolveInfo;
8
use Digia\GraphQL\Language\Node\FieldNode;
9
use Digia\GraphQL\Language\Node\FragmentDefinitionNode;
10
use Digia\GraphQL\Language\Node\FragmentSpreadNode;
11
use Digia\GraphQL\Language\Node\InlineFragmentNode;
12
use Digia\GraphQL\Language\Node\NodeInterface;
13
use Digia\GraphQL\Language\Node\OperationDefinitionNode;
14
use Digia\GraphQL\Language\Node\SelectionSetNode;
15
use Digia\GraphQL\Type\Definition\AbstractTypeInterface;
16
use Digia\GraphQL\Type\Definition\Field;
17
use Digia\GraphQL\Type\Definition\InterfaceType;
18
use Digia\GraphQL\Type\Definition\LeafTypeInterface;
19
use Digia\GraphQL\Type\Definition\ListType;
20
use Digia\GraphQL\Type\Definition\NonNullType;
21
use Digia\GraphQL\Type\Definition\ObjectType;
22
use Digia\GraphQL\Type\Definition\TypeInterface;
23
use Digia\GraphQL\Type\Definition\UnionType;
24
use Digia\GraphQL\Type\Schema;
25
use function Digia\GraphQL\Type\SchemaMetaFieldDefinition;
26
use function Digia\GraphQL\Type\TypeMetaFieldDefinition;
27
use function Digia\GraphQL\Type\TypeNameMetaFieldDefinition;
28
use function Digia\GraphQL\Util\toString;
29
use function Digia\GraphQL\Util\typeFromAST;
30
31
/**
32
 * Class AbstractStrategy
33
 * @package Digia\GraphQL\Execution\Strategies
34
 */
35
abstract class ExecutionStrategy
36
{
37
    /**
38
     * @var ExecutionContext
39
     */
40
    protected $context;
41
42
    /**
43
     * @var OperationDefinitionNode
44
     */
45
    protected $operation;
46
47
    /**
48
     * @var mixed
49
     */
50
    protected $rootValue;
51
52
53
    /**
54
     * @var ValuesResolver
55
     */
56
    protected $valuesResolver;
57
58
    /**
59
     * @var array
60
     */
61
    protected $finalResult;
62
63
    /**
64
     * AbstractStrategy constructor.
65
     * @param ExecutionContext        $context
66
     *
67
     * @param OperationDefinitionNode $operation
68
     */
69
    public function __construct(
70
        ExecutionContext $context,
71
        OperationDefinitionNode $operation,
72
        $rootValue
73
    ) {
74
        $this->context        = $context;
75
        $this->operation      = $operation;
76
        $this->rootValue      = $rootValue;
77
        $this->valuesResolver = new ValuesResolver();
78
    }
79
80
    /**
81
     * @return array|null
82
     */
83
    abstract function execute(): ?array;
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
84
85
    /**
86
     * @param ObjectType       $runtimeType
87
     * @param SelectionSetNode $selectionSet
88
     * @param                  $fields
89
     * @param                  $visitedFragmentNames
90
     * @return mixed
91
     * @throws InvalidTypeException
92
     * @throws \Digia\GraphQL\Error\ExecutionException
93
     * @throws \Digia\GraphQL\Error\InvariantException
94
     */
95
    protected function collectFields(
96
        ObjectType $runtimeType,
97
        SelectionSetNode $selectionSet,
98
        $fields,
99
        $visitedFragmentNames
100
    ) {
101
        foreach ($selectionSet->getSelections() as $selection) {
102
            if ($selection instanceof FieldNode) {
103
                $fieldName = $this->getFieldNameKey($selection);
104
105
                if (!isset($runtimeType->getFields()[$selection->getNameValue()])) {
106
                    continue;
107
                }
108
109
                if (!isset($fields[$fieldName])) {
110
                    $fields[$fieldName] = new \ArrayObject();
111
                }
112
113
                $fields[$fieldName][] = $selection;
114
            } elseif ($selection instanceof InlineFragmentNode) {
115
                if (!$this->shouldIncludeNode($selection) ||
116
                    !$this->doesFragmentConditionMatch($selection, $runtimeType)
117
                ) {
118
                    continue;
119
                }
120
121
                $this->collectFields($runtimeType, $selection->getSelectionSet(), $fields, $visitedFragmentNames);
122
            } elseif ($selection instanceof FragmentSpreadNode) {
123
                $fragmentName = $selection->getNameValue();
124
125
                if (!empty($visitedFragmentNames[$fragmentName]) ||
126
                    !$this->shouldIncludeNode($selection)
127
                ) {
128
                    continue;
129
                }
130
131
                $visitedFragmentNames[$fragmentName] = true;
132
                /** @var FragmentDefinitionNode $fragment */
133
                $fragment = $this->context->getFragments()[$fragmentName];
134
                $this->collectFields($runtimeType, $fragment->getSelectionSet(), $fields, $visitedFragmentNames);
135
            }
136
        }
137
138
        return $fields;
139
    }
140
141
142
    /**
143
     * @param $node
144
     * @return bool
145
     * @throws InvalidTypeException
146
     * @throws \Digia\GraphQL\Error\ExecutionException
147
     * @throws \Digia\GraphQL\Error\InvariantException
148
     */
149
    private function shouldIncludeNode(NodeInterface $node): bool
150
    {
151
152
        $contextVariables = $this->context->getVariableValues();
153
154
        $skip = $this->valuesResolver->getDirectiveValues(GraphQLSkipDirective(), $node, $contextVariables);
155
156
        if ($skip && $skip['if'] === true) {
157
            return false;
158
        }
159
160
        $include = $this->valuesResolver->getDirectiveValues(GraphQLSkipDirective(), $node, $contextVariables);
161
162
        if ($include && $include['if'] === false) {
163
            return false;
164
        }
165
166
        return true;
167
    }
168
169
    /**
170
     * @param FragmentDefinitionNode|InlineFragmentNode $fragment
171
     * @param ObjectType                                $type
172
     * @return bool
173
     * @throws InvalidTypeException
174
     */
175
    private function doesFragmentConditionMatch(
176
        NodeInterface $fragment,
177
        ObjectType $type
178
    ): bool {
179
        $typeConditionNode = $fragment->getTypeCondition();
0 ignored issues
show
Bug introduced by
The method getTypeCondition() does not exist on Digia\GraphQL\Language\Node\NodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\NodeInterface such as Digia\GraphQL\Language\Node\InlineFragmentNode or Digia\GraphQL\Language\Node\FragmentDefinitionNode. ( Ignorable by Annotation )

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

179
        /** @scrutinizer ignore-call */ 
180
        $typeConditionNode = $fragment->getTypeCondition();
Loading history...
180
181
        if (!$typeConditionNode) {
182
            return true;
183
        }
184
185
        $conditionalType = typeFromAST($this->context->getSchema(), $typeConditionNode);
186
187
        if ($conditionalType === $type) {
188
            return true;
189
        }
190
191
        if ($conditionalType instanceof AbstractTypeInterface) {
192
            return $this->context->getSchema()->isPossibleType($conditionalType, $type);
193
        }
194
195
        return false;
196
    }
197
198
    /**
199
     * @TODO: consider to move this to FieldNode
200
     * @param FieldNode $node
201
     * @return string
202
     */
203
    private function getFieldNameKey(FieldNode $node): string
204
    {
205
        return $node->getAlias() ? $node->getAlias()->getValue() : $node->getNameValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $node->getAlias()...: $node->getNameValue() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
206
    }
207
208
    /**
209
     * Implements the "Evaluating selection sets" section of the spec for "read" mode.
210
     * @param ObjectType $objectType
211
     * @param            $rootValue
212
     * @param            $path
213
     * @param            $fields
214
     * @return array
215
     * @throws InvalidTypeException
216
     * @throws \Digia\GraphQL\Error\ExecutionException
217
     * @throws \Digia\GraphQL\Error\InvariantException
218
     * @throws \Throwable
219
     */
220
    protected function executeFields(
221
        ObjectType $objectType,
222
        $rootValue,
223
        $path,
224
        $fields
225
    ): array {
226
        $finalResults = [];
227
228
        foreach ($fields as $fieldName => $fieldNodes) {
229
            $fieldPath   = $path;
230
            $fieldPath[] = $fieldName;
231
232
            $result = $this->resolveField(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->resolveField($obj...fieldNodes, $fieldPath) targeting Digia\GraphQL\Execution\...trategy::resolveField() 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...
233
                $objectType,
234
                $rootValue,
235
                $fieldNodes,
236
                $fieldPath
237
            );
238
239
            $finalResults[$fieldName] = $result;
240
        }
241
242
        return $finalResults;
243
    }
244
245
    /**
246
     * Implements the "Evaluating selection sets" section of the spec for "write" mode.
247
     *
248
     * @param ObjectType $objectType
249
     * @param            $rootValue
250
     * @param            $path
251
     * @param            $fields
252
     * @return array
253
     * @throws InvalidTypeException
254
     * @throws \Digia\GraphQL\Error\ExecutionException
255
     * @throws \Digia\GraphQL\Error\InvariantException
256
     * @throws \Throwable
257
     */
258
    public function executeFieldsSerially(
259
        ObjectType $objectType,
260
        $rootValue,
261
        $path,
262
        $fields
263
    ) {
264
        //@TODO execute fields serially
265
        $finalResults = [];
266
267
        foreach ($fields as $fieldName => $fieldNodes) {
268
            $fieldPath   = $path;
269
            $fieldPath[] = $fieldName;
270
271
            $result = $this->resolveField($objectType,
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->resolveField($obj...fieldNodes, $fieldPath) targeting Digia\GraphQL\Execution\...trategy::resolveField() 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...
272
                $rootValue,
273
                $fieldNodes,
274
                $fieldPath
275
            );
276
277
            $finalResults[$fieldName] = $result;
278
        }
279
280
        return $finalResults;
281
    }
282
283
    /**
284
     * @param Schema     $schema
285
     * @param ObjectType $parentType
286
     * @param string     $fieldName
287
     * @return \Digia\GraphQL\Type\Definition\Field|null
288
     * @throws InvalidTypeException
289
     */
290
    public function getFieldDefinition(
291
        Schema $schema,
292
        ObjectType $parentType,
293
        string $fieldName
294
    ) {
295
        $schemaMetaFieldDefinition   = SchemaMetaFieldDefinition();
296
        $typeMetaFieldDefinition     = TypeMetaFieldDefinition();
297
        $typeNameMetaFieldDefinition = TypeNameMetaFieldDefinition();
298
299
        if ($fieldName === $schemaMetaFieldDefinition->getName() && $schema->getQuery() === $parentType) {
300
            return $schemaMetaFieldDefinition;
301
        }
302
303
        if ($fieldName === $typeMetaFieldDefinition->getName() && $schema->getQuery() === $parentType) {
304
            return $typeMetaFieldDefinition;
305
        }
306
307
        if ($fieldName === $typeNameMetaFieldDefinition->getName()) {
308
            return $typeNameMetaFieldDefinition;
309
        }
310
311
        $fields = $parentType->getFields();
312
313
        return $fields[$fieldName] ?? null;
314
    }
315
316
317
    /**
318
     * @param ObjectType $parentType
319
     * @param            $rootValue
320
     * @param            $fieldNodes
321
     * @param            $path
322
     * @return array|null|\Throwable
323
     * @throws InvalidTypeException
324
     * @throws \Digia\GraphQL\Error\ExecutionException
325
     * @throws \Digia\GraphQL\Error\InvariantException
326
     * @throws \Throwable
327
     */
328
    protected function resolveField(
329
        ObjectType $parentType,
330
        $rootValue,
331
        $fieldNodes,
332
        $path
333
    ) {
334
        /** @var FieldNode $fieldNode */
335
        $fieldNode = $fieldNodes[0];
336
337
        $field = $this->getFieldDefinition($this->context->getSchema(), $parentType, $fieldNode->getNameValue());
338
339
        if (!$field) {
340
            return null;
341
        }
342
343
        $info = $this->buildResolveInfo($fieldNodes, $fieldNode, $field, $parentType, $path, $this->context);
344
345
        $resolveFunction = $this->determineResolveFunction($field, $parentType, $this->context);
346
347
        $result = $this->resolveFieldValueOrError(
348
            $field,
349
            $fieldNode,
350
            $resolveFunction,
351
            $rootValue,
352
            $this->context,
353
            $info
354
        );
355
356
        $result = $this->completeValueCatchingError(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->completeValueCatc... $info, $path, $result) targeting Digia\GraphQL\Execution\...eteValueCatchingError() 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...
357
            $field->getType(),
358
            $fieldNodes,
359
            $info,
360
            $path,
361
            $result// $result is passed as $source
362
        );
363
364
        return $result;
365
    }
366
367
    /**
368
     * @param array            $fieldNodes
369
     * @param FieldNode        $fieldNode
370
     * @param Field            $field
371
     * @param ObjectType       $parentType
372
     * @param                  $path
373
     * @param ExecutionContext $context
374
     * @return ResolveInfo
375
     */
376
    private function buildResolveInfo(
377
        \ArrayAccess $fieldNodes,
378
        FieldNode $fieldNode,
379
        Field $field,
380
        ObjectType $parentType,
381
        $path,
382
        ExecutionContext $context
383
    ) {
384
        return new ResolveInfo([
385
            'fieldName'      => $fieldNode->getNameValue(),
386
            'fieldNodes'     => $fieldNodes,
387
            'returnType'     => $field->getType(),
388
            'parentType'     => $parentType,
389
            'path'           => $path,
390
            'schema'         => $context->getSchema(),
391
            'fragments'      => $context->getFragments(),
392
            'rootValue'      => $context->getRootValue(),
393
            'operation'      => $context->getOperation(),
394
            'variableValues' => $context->getVariableValues(),
395
        ]);
396
    }
397
398
    /**
399
     * @param Field            $field
400
     * @param ObjectType       $objectType
401
     * @param ExecutionContext $context
402
     * @return callable|mixed|null
403
     */
404
    private function determineResolveFunction(
405
        Field $field,
406
        ObjectType $objectType,
407
        ExecutionContext $context
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

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

407
        /** @scrutinizer ignore-unused */ ExecutionContext $context

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
408
    ) {
409
410
        if ($field->hasResolve()) {
411
            return $field->getResolve();
412
        }
413
414
        if ($objectType->hasResolve()) {
415
            return $objectType->getResolve();
416
        }
417
418
        return $this->context->getFieldResolver();
419
    }
420
421
    /**
422
     * @param TypeInterface $fieldType
423
     * @param               $fieldNodes
424
     * @param ResolveInfo   $info
425
     * @param               $path
426
     * @param               $result
427
     * @return null
428
     * @throws \Throwable
429
     */
430
    public function completeValueCatchingError(
431
        TypeInterface $fieldType,
432
        $fieldNodes,
433
        ResolveInfo $info,
434
        $path,
435
        &$result
436
    ) {
437
        if ($fieldType instanceof NonNullType) {
438
            return $this->completeValueWithLocatedError(
439
                $fieldType,
440
                $fieldNodes,
441
                $info,
442
                $path,
443
                $result
444
            );
445
        }
446
447
        try {
448
            $completed = $this->completeValueWithLocatedError(
449
                $fieldType,
450
                $fieldNodes,
451
                $info,
452
                $path,
453
                $result
454
            );
455
456
            return $completed;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $completed also could return the type array which is incompatible with the documented return type null.
Loading history...
457
        } catch (\Exception $ex) {
458
            $this->context->addError(new ExecutionException($ex->getMessage()));
459
            return null;
460
        }
461
    }
462
463
    /**
464
     * @param TypeInterface $fieldType
465
     * @param               $fieldNodes
466
     * @param ResolveInfo   $info
467
     * @param               $path
468
     * @param               $result
469
     * @throws \Throwable
470
     */
471
    public function completeValueWithLocatedError(
472
        TypeInterface $fieldType,
473
        $fieldNodes,
474
        ResolveInfo $info,
475
        $path,
476
        $result
477
    ) {
478
        try {
479
            $completed = $this->completeValue(
480
                $fieldType,
481
                $fieldNodes,
482
                $info,
483
                $path,
484
                $result
485
            );
486
            return $completed;
487
        } catch (\Exception $ex) {
488
            //@TODO throw located error
489
            throw $ex;
490
        } catch (\Throwable $ex) {
491
            //@TODO throw located error
492
            throw $ex;
493
        }
494
    }
495
496
    /**
497
     * @param TypeInterface $returnType
498
     * @param               $fieldNodes
499
     * @param ResolveInfo   $info
500
     * @param               $path
501
     * @param               $result
502
     * @return array|mixed
503
     * @throws ExecutionException
504
     * @throws \Throwable
505
     */
506
    private function completeValue(
507
        TypeInterface $returnType,
508
        $fieldNodes,
509
        ResolveInfo $info,
510
        $path,
511
        &$result
512
    ) {
513
        if ($result instanceof \Throwable) {
514
            throw $result;
515
        }
516
517
        // If result is null-like, return null.
518
        if (null === $result) {
519
            return null;
520
        }
521
522
        if ($returnType instanceof NonNullType) {
523
            $completed = $this->completeValue(
524
                $returnType->getOfType(),
525
                $fieldNodes,
526
                $info,
527
                $path,
528
                $result
529
            );
530
531
            if ($completed === null) {
532
                throw new ExecutionException(
533
                    sprintf(
534
                        'Cannot return null for non-nullable field %s.%s.',
535
                        $info->getParentType(), $info->getFieldName()
536
                    )
537
                );
538
            }
539
540
            return $completed;
541
        }
542
543
        // If field type is List, complete each item in the list with the inner type
544
        if ($returnType instanceof ListType) {
545
            return $this->completeListValue($returnType, $fieldNodes, $info, $path, $result);
546
        }
547
548
549
        // If field type is Scalar or Enum, serialize to a valid value, returning
550
        // null if serialization is not possible.
551
        if ($returnType instanceof LeafTypeInterface) {
552
            return $this->completeLeafValue($returnType, $result);
553
        }
554
555
        //@TODO Make a function for checking abstract type?
556
        if ($returnType instanceof InterfaceType || $returnType instanceof UnionType) {
557
            return $this->completeAbstractValue($returnType, $fieldNodes, $info, $path, $result);
558
        }
559
560
        // Field type must be Object, Interface or Union and expect sub-selections.
561
        if ($returnType instanceof ObjectType) {
562
            return $this->completeObjectValue($returnType, $fieldNodes, $info, $path, $result);
563
        }
564
565
        throw new ExecutionException("Cannot complete value of unexpected type \"{$returnType}\".");
566
    }
567
568
    /**
569
     * @param AbstractTypeInterface $returnType
570
     * @param                       $fieldNodes
571
     * @param ResolveInfo           $info
572
     * @param                       $path
573
     * @param                       $result
574
     * @return array
575
     * @throws ExecutionException
576
     * @throws InvalidTypeException
577
     * @throws \Digia\GraphQL\Error\InvariantException
578
     * @throws \Throwable
579
     */
580
    private function completeAbstractValue(
581
        AbstractTypeInterface $returnType,
582
        $fieldNodes,
583
        ResolveInfo $info,
584
        $path,
585
        &$result
586
    ) {
587
        $runtimeType = $returnType->resolveType($result, $this->context, $info);
588
589
        if (null === $runtimeType) {
590
            throw new ExecutionException(
591
                sprintf(
592
                    "GraphQL Interface Type `%s` returned `null` from it`s `resolveType` function for value: %s",
593
                    $returnType->getName(), toString($result)
594
                )
595
            );
596
        }
597
598
        //@TODO Check if $runtimeType is a valid runtime type
599
        return $this->completeObjectValue(
600
            $runtimeType,
601
            $fieldNodes,
602
            $info,
603
            $path,
604
            $result
605
        );
606
    }
607
608
    /**
609
     * @param ListType    $returnType
610
     * @param             $fieldNodes
611
     * @param ResolveInfo $info
612
     * @param             $path
613
     * @param             $result
614
     * @return array
615
     * @throws \Throwable
616
     */
617
    private function completeListValue(
618
        ListType $returnType,
619
        $fieldNodes,
620
        ResolveInfo $info,
621
        $path,
622
        &$result
623
    ) {
624
        $itemType = $returnType->getOfType();
625
626
        $completedItems = [];
627
628
        foreach ($result as $key => $item) {
629
            $fieldPath        = $path;
630
            $fieldPath[]      = $key;
631
            $completedItem    = $this->completeValueCatchingError($itemType, $fieldNodes, $info, $fieldPath, $item);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $completedItem is correct as $this->completeValueCatc...nfo, $fieldPath, $item) targeting Digia\GraphQL\Execution\...eteValueCatchingError() 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...
632
            $completedItems[] = $completedItem;
633
        }
634
635
        return $completedItems;
636
    }
637
638
    /**
639
     * @param LeafTypeInterface $returnType
640
     * @param                   $result
641
     * @return mixed
642
     * @throws ExecutionException
643
     */
644
    private function completeLeafValue(LeafTypeInterface $returnType, &$result)
645
    {
646
        $serializedResult = $returnType->serialize($result);
0 ignored issues
show
Bug introduced by
The method serialize() does not exist on Digia\GraphQL\Type\Definition\LeafTypeInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Digia\GraphQL\Type\Definition\LeafTypeInterface. ( Ignorable by Annotation )

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

646
        /** @scrutinizer ignore-call */ 
647
        $serializedResult = $returnType->serialize($result);
Loading history...
647
648
        if ($serializedResult === null) {
649
            throw new ExecutionException(
650
                sprintf('Expected a value of type "%s" but received: %s', toString($returnType), toString($result))
651
            );
652
        }
653
654
        return $serializedResult;
655
    }
656
657
    /**
658
     * @param ObjectType  $returnType
659
     * @param             $fieldNodes
660
     * @param ResolveInfo $info
661
     * @param             $path
662
     * @param             $result
663
     * @return array
664
     * @throws ExecutionException
665
     * @throws InvalidTypeException
666
     * @throws \Digia\GraphQL\Error\InvariantException
667
     * @throws \Throwable
668
     */
669
    private function completeObjectValue(
670
        ObjectType $returnType,
671
        $fieldNodes,
672
        ResolveInfo $info,
673
        $path,
674
        &$result
675
    ) {
676
        return $this->collectAndExecuteSubFields(
677
            $returnType,
678
            $fieldNodes,
679
            $info,
680
            $path,
681
            $result
682
        );
683
    }
684
685
    /**
686
     * @param Field            $field
687
     * @param FieldNode        $fieldNode
688
     * @param callable         $resolveFunction
689
     * @param                  $rootValue
690
     * @param ExecutionContext $context
691
     * @param ResolveInfo      $info
692
     * @return array|\Throwable
693
     */
694
    private function resolveFieldValueOrError(
695
        Field $field,
696
        FieldNode $fieldNode,
697
        callable $resolveFunction,
698
        $rootValue,
699
        ExecutionContext $context,
700
        ResolveInfo $info
701
    ) {
702
        try {
703
            $args = $this->valuesResolver->coerceArgumentValues($field, $fieldNode, $context->getVariableValues());
704
705
            return $resolveFunction($rootValue, $args, $context->getContextValue(), $info);
706
        } catch (\Throwable $error) {
707
            return $error;
708
        }
709
    }
710
711
    /**
712
     * @param ObjectType  $returnType
713
     * @param             $fieldNodes
714
     * @param ResolveInfo $info
715
     * @param             $path
716
     * @param             $result
717
     * @return array
718
     * @throws InvalidTypeException
719
     * @throws \Digia\GraphQL\Error\ExecutionException
720
     * @throws \Digia\GraphQL\Error\InvariantException
721
     * @throws \Throwable
722
     */
723
    private function collectAndExecuteSubFields(
724
        ObjectType $returnType,
725
        $fieldNodes,
726
        ResolveInfo $info,
0 ignored issues
show
Unused Code introduced by
The parameter $info is not used and could be removed. ( Ignorable by Annotation )

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

726
        /** @scrutinizer ignore-unused */ ResolveInfo $info,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
727
        $path,
728
        &$result
729
    ) {
730
        $subFields = new \ArrayObject();
731
732
        foreach ($fieldNodes as $fieldNode) {
733
            /** @var FieldNode $fieldNode */
734
            if ($fieldNode->getSelectionSet() !== null) {
735
                $subFields = $this->collectFields(
736
                    $returnType,
737
                    $fieldNode->getSelectionSet(),
738
                    $subFields,
739
                    new \ArrayObject()
740
                );
741
            }
742
        }
743
744
        if ($subFields->count()) {
745
            return $this->executeFields($returnType, $result, $path, $subFields);
746
        }
747
748
        return $result;
749
    }
750
}
751