Passed
Pull Request — master (#117)
by Quang
02:20
created

ExecutionStrategy::defaultFieldResolver()   C

Complexity

Conditions 7
Paths 20

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 6.9811
c 0
b 0
f 0
cc 7
eloc 11
nc 20
nop 4
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
     * @var array
65
     */
66
    protected static $defaultFieldResolver = [__CLASS__, 'defaultFieldResolver'];
67
68
    /**
69
     * AbstractStrategy constructor.
70
     * @param ExecutionContext        $context
71
     *
72
     * @param OperationDefinitionNode $operation
73
     */
74
    public function __construct(
75
        ExecutionContext $context,
76
        OperationDefinitionNode $operation,
77
        $rootValue
78
    ) {
79
        $this->context        = $context;
80
        $this->operation      = $operation;
81
        $this->rootValue      = $rootValue;
82
        $this->valuesResolver = new ValuesResolver();
83
    }
84
85
    /**
86
     * @return array|null
87
     */
88
    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...
89
90
    /**
91
     * @param ObjectType       $runtimeType
92
     * @param SelectionSetNode $selectionSet
93
     * @param                  $fields
94
     * @param                  $visitedFragmentNames
95
     * @return mixed
96
     * @throws InvalidTypeException
97
     * @throws \Digia\GraphQL\Error\ExecutionException
98
     * @throws \Digia\GraphQL\Error\InvariantException
99
     */
100
    protected function collectFields(
101
        ObjectType $runtimeType,
102
        SelectionSetNode $selectionSet,
103
        &$fields,
104
        &$visitedFragmentNames
105
    ) {
106
        foreach ($selectionSet->getSelections() as $selection) {
107
            // Check if this Node should be included first
108
            if (!$this->shouldIncludeNode($selection)) {
109
                continue;
110
            }
111
            // Collect fields
112
            if ($selection instanceof FieldNode) {
113
                $fieldName = $this->getFieldNameKey($selection);
114
                if (!isset($runtimeType->getFields()[$selection->getNameValue()])) {
115
                    continue;
116
                }
117
118
                if (!isset($fields[$fieldName])) {
119
                    $fields[$fieldName] = [];
120
                }
121
122
                $fields[$fieldName][] = $selection;
123
            } elseif ($selection instanceof InlineFragmentNode) {
124
                if (!$this->doesFragmentConditionMatch($selection, $runtimeType)) {
125
                    continue;
126
                }
127
128
                $this->collectFields($runtimeType, $selection->getSelectionSet(), $fields, $visitedFragmentNames);
129
            } elseif ($selection instanceof FragmentSpreadNode) {
130
                $fragmentName = $selection->getNameValue();
131
132
                if (!empty($visitedFragmentNames[$fragmentName])) {
133
                    continue;
134
                }
135
136
                $visitedFragmentNames[$fragmentName] = true;
137
                /** @var FragmentDefinitionNode $fragment */
138
                $fragment = $this->context->getFragments()[$fragmentName];
139
                $this->collectFields($runtimeType, $fragment->getSelectionSet(), $fields, $visitedFragmentNames);
140
            }
141
        }
142
143
        return $fields;
144
    }
145
146
147
    /**
148
     * @param $node
149
     * @return bool
150
     * @throws InvalidTypeException
151
     * @throws \Digia\GraphQL\Error\ExecutionException
152
     * @throws \Digia\GraphQL\Error\InvariantException
153
     */
154
    private function shouldIncludeNode(NodeInterface $node): bool
155
    {
156
157
        $contextVariables = $this->context->getVariableValues();
158
159
        $skip = $this->valuesResolver->getDirectiveValues(GraphQLSkipDirective(), $node, $contextVariables);
160
161
        if ($skip && $skip['if'] === true) {
162
            return false;
163
        }
164
165
        $include = $this->valuesResolver->getDirectiveValues(GraphQLIncludeDirective(), $node, $contextVariables);
166
167
        if ($include && $include['if'] === false) {
168
            return false;
169
        }
170
171
        return true;
172
    }
173
174
    /**
175
     * @param FragmentDefinitionNode|InlineFragmentNode $fragment
176
     * @param ObjectType                                $type
177
     * @return bool
178
     * @throws InvalidTypeException
179
     */
180
    private function doesFragmentConditionMatch(
181
        NodeInterface $fragment,
182
        ObjectType $type
183
    ): bool {
184
        $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

184
        /** @scrutinizer ignore-call */ 
185
        $typeConditionNode = $fragment->getTypeCondition();
Loading history...
185
186
        if (!$typeConditionNode) {
187
            return true;
188
        }
189
190
        $conditionalType = typeFromAST($this->context->getSchema(), $typeConditionNode);
191
192
        if (gettype($conditionalType) === gettype($type)) {
193
            return true;
194
        }
195
196
        if ($conditionalType instanceof AbstractTypeInterface) {
197
            return $this->context->getSchema()->isPossibleType($conditionalType, $type);
198
        }
199
200
        return false;
201
    }
202
203
    /**
204
     * @TODO: consider to move this to FieldNode
205
     * @param FieldNode $node
206
     * @return string
207
     */
208
    private function getFieldNameKey(FieldNode $node): string
209
    {
210
        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...
211
    }
212
213
    /**
214
     * Implements the "Evaluating selection sets" section of the spec for "read" mode.
215
     * @param ObjectType $objectType
216
     * @param            $rootValue
217
     * @param            $path
218
     * @param            $fields
219
     * @return array
220
     * @throws InvalidTypeException
221
     * @throws \Digia\GraphQL\Error\ExecutionException
222
     * @throws \Digia\GraphQL\Error\InvariantException
223
     * @throws \Throwable
224
     */
225
    protected function executeFields(
226
        ObjectType $objectType,
227
        $rootValue,
228
        $path,
229
        $fields
230
    ): array {
231
        $finalResults = [];
232
233
        foreach ($fields as $fieldName => $fieldNodes) {
234
            $fieldPath   = $path;
235
            $fieldPath[] = $fieldName;
236
237
            $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...
238
                $objectType,
239
                $rootValue,
240
                $fieldNodes,
241
                $fieldPath
242
            );
243
244
            $finalResults[$fieldName] = $result;
245
        }
246
247
        return $finalResults;
248
    }
249
250
    /**
251
     * Implements the "Evaluating selection sets" section of the spec for "write" mode.
252
     *
253
     * @param ObjectType $objectType
254
     * @param            $rootValue
255
     * @param            $path
256
     * @param            $fields
257
     * @return array
258
     * @throws InvalidTypeException
259
     * @throws \Digia\GraphQL\Error\ExecutionException
260
     * @throws \Digia\GraphQL\Error\InvariantException
261
     * @throws \Throwable
262
     */
263
    public function executeFieldsSerially(
264
        ObjectType $objectType,
265
        $rootValue,
266
        $path,
267
        $fields
268
    ) {
269
        //@TODO execute fields serially
270
        $finalResults = [];
271
272
        foreach ($fields as $fieldName => $fieldNodes) {
273
            $fieldPath   = $path;
274
            $fieldPath[] = $fieldName;
275
276
            $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...
277
                $objectType,
278
                $rootValue,
279
                $fieldNodes,
280
                $fieldPath
281
            );
282
283
            $finalResults[$fieldName] = $result;
284
        }
285
286
        return $finalResults;
287
    }
288
289
    /**
290
     * @param Schema     $schema
291
     * @param ObjectType $parentType
292
     * @param string     $fieldName
293
     * @return \Digia\GraphQL\Type\Definition\Field|null
294
     * @throws InvalidTypeException
295
     */
296
    public function getFieldDefinition(
297
        Schema $schema,
298
        ObjectType $parentType,
299
        string $fieldName
300
    ) {
301
        $schemaMetaFieldDefinition   = SchemaMetaFieldDefinition();
302
        $typeMetaFieldDefinition     = TypeMetaFieldDefinition();
303
        $typeNameMetaFieldDefinition = TypeNameMetaFieldDefinition();
304
305
        if ($fieldName === $schemaMetaFieldDefinition->getName() && $schema->getQuery() === $parentType) {
306
            return $schemaMetaFieldDefinition;
307
        }
308
309
        if ($fieldName === $typeMetaFieldDefinition->getName() && $schema->getQuery() === $parentType) {
310
            return $typeMetaFieldDefinition;
311
        }
312
313
        if ($fieldName === $typeNameMetaFieldDefinition->getName()) {
314
            return $typeNameMetaFieldDefinition;
315
        }
316
317
        $fields = $parentType->getFields();
318
319
        return $fields[$fieldName] ?? null;
320
    }
321
322
323
    /**
324
     * @param ObjectType $parentType
325
     * @param            $rootValue
326
     * @param            $fieldNodes
327
     * @param            $path
328
     * @return array|null|\Throwable
329
     * @throws InvalidTypeException
330
     * @throws \Digia\GraphQL\Error\ExecutionException
331
     * @throws \Digia\GraphQL\Error\InvariantException
332
     * @throws \Throwable
333
     */
334
    protected function resolveField(
335
        ObjectType $parentType,
336
        $rootValue,
337
        $fieldNodes,
338
        $path
339
    ) {
340
        /** @var FieldNode $fieldNode */
341
        $fieldNode = $fieldNodes[0];
342
343
        $field = $this->getFieldDefinition($this->context->getSchema(), $parentType, $fieldNode->getNameValue());
344
345
        if (!$field) {
346
            return null;
347
        }
348
349
        $info = $this->buildResolveInfo($fieldNodes, $fieldNode, $field, $parentType, $path, $this->context);
350
351
        $resolveFunction = $this->determineResolveFunction($field, $parentType, $this->context);
352
353
        $result = $this->resolveFieldValueOrError(
354
            $field,
355
            $fieldNode,
356
            $resolveFunction,
357
            $rootValue,
358
            $this->context,
359
            $info
360
        );
361
362
        $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...
363
            $field->getType(),
364
            $fieldNodes,
365
            $info,
366
            $path,
367
            $result// $result is passed as $source
368
        );
369
370
        return $result;
371
    }
372
373
    /**
374
     * @param array            $fieldNodes
375
     * @param FieldNode        $fieldNode
376
     * @param Field            $field
377
     * @param ObjectType       $parentType
378
     * @param                  $path
379
     * @param ExecutionContext $context
380
     * @return ResolveInfo
381
     */
382
    private function buildResolveInfo(
383
        array $fieldNodes,
384
        FieldNode $fieldNode,
385
        Field $field,
386
        ObjectType $parentType,
387
        $path,
388
        ExecutionContext $context
389
    ) {
390
        return new ResolveInfo([
391
            'fieldName'      => $fieldNode->getNameValue(),
392
            'fieldNodes'     => $fieldNodes,
393
            'returnType'     => $field->getType(),
394
            'parentType'     => $parentType,
395
            'path'           => $path,
396
            'schema'         => $context->getSchema(),
397
            'fragments'      => $context->getFragments(),
398
            'rootValue'      => $context->getRootValue(),
399
            'operation'      => $context->getOperation(),
400
            'variableValues' => $context->getVariableValues(),
401
        ]);
402
    }
403
404
    /**
405
     * @param Field            $field
406
     * @param ObjectType       $objectType
407
     * @param ExecutionContext $context
408
     * @return callable|mixed|null
409
     */
410
    private function determineResolveFunction(
411
        Field $field,
412
        ObjectType $objectType,
413
        ExecutionContext $context
414
    ) {
415
416
        if ($field->hasResolve()) {
417
            return $field->getResolve();
418
        }
419
420
        if ($objectType->hasResolve()) {
421
            return $objectType->getResolve();
422
        }
423
424
        return $this->context->getFieldResolver() ?? self::$defaultFieldResolver;
425
    }
426
427
    /**
428
     * @param TypeInterface $fieldType
429
     * @param               $fieldNodes
430
     * @param ResolveInfo   $info
431
     * @param               $path
432
     * @param               $result
433
     * @return null
434
     * @throws \Throwable
435
     */
436
    public function completeValueCatchingError(
437
        TypeInterface $fieldType,
438
        $fieldNodes,
439
        ResolveInfo $info,
440
        $path,
441
        &$result
442
    ) {
443
        if ($fieldType instanceof NonNullType) {
444
            return $this->completeValueWithLocatedError(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->completeVa... $info, $path, $result) also could return the type array which is incompatible with the documented return type null.
Loading history...
445
                $fieldType,
446
                $fieldNodes,
447
                $info,
448
                $path,
449
                $result
450
            );
451
        }
452
453
        try {
454
            $completed = $this->completeValueWithLocatedError(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $completed is correct as $this->completeValueWith... $info, $path, $result) targeting Digia\GraphQL\Execution\...ValueWithLocatedError() 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...
455
                $fieldType,
456
                $fieldNodes,
457
                $info,
458
                $path,
459
                $result
460
            );
461
462
            return $completed;
463
        } catch (\Exception $ex) {
464
            $this->context->addError(new ExecutionException($ex->getMessage()));
465
            return null;
466
        }
467
    }
468
469
    /**
470
     * @param TypeInterface $fieldType
471
     * @param               $fieldNodes
472
     * @param ResolveInfo   $info
473
     * @param               $path
474
     * @param               $result
475
     * @throws \Throwable
476
     */
477
    public function completeValueWithLocatedError(
478
        TypeInterface $fieldType,
479
        $fieldNodes,
480
        ResolveInfo $info,
481
        $path,
482
        $result
483
    ) {
484
        try {
485
            $completed = $this->completeValue(
486
                $fieldType,
487
                $fieldNodes,
488
                $info,
489
                $path,
490
                $result
491
            );
492
            return $completed;
493
        } catch (\Exception $ex) {
494
            //@TODO throw located error
495
            throw $ex;
496
        } catch (\Throwable $ex) {
497
            //@TODO throw located error
498
            throw $ex;
499
        }
500
    }
501
502
    /**
503
     * @param TypeInterface $returnType
504
     * @param               $fieldNodes
505
     * @param ResolveInfo   $info
506
     * @param               $path
507
     * @param               $result
508
     * @return array|mixed
509
     * @throws ExecutionException
510
     * @throws \Throwable
511
     */
512
    private function completeValue(
513
        TypeInterface $returnType,
514
        $fieldNodes,
515
        ResolveInfo $info,
516
        $path,
517
        &$result
518
    ) {
519
        if ($result instanceof \Throwable) {
520
            throw $result;
521
        }
522
523
        // If result is null-like, return null.
524
        if (null === $result) {
525
            return null;
526
        }
527
528
        if ($returnType instanceof NonNullType) {
529
            $completed = $this->completeValue(
530
                $returnType->getOfType(),
531
                $fieldNodes,
532
                $info,
533
                $path,
534
                $result
535
            );
536
537
            if ($completed === null) {
538
                throw new ExecutionException(
539
                    sprintf(
540
                        'Cannot return null for non-nullable field %s.%s.',
541
                        $info->getParentType(), $info->getFieldName()
542
                    )
543
                );
544
            }
545
546
            return $completed;
547
        }
548
549
        // If field type is List, complete each item in the list with the inner type
550
        if ($returnType instanceof ListType) {
551
            return $this->completeListValue($returnType, $fieldNodes, $info, $path, $result);
552
        }
553
554
555
        // If field type is Scalar or Enum, serialize to a valid value, returning
556
        // null if serialization is not possible.
557
        if ($returnType instanceof LeafTypeInterface) {
558
            return $this->completeLeafValue($returnType, $result);
559
        }
560
561
        //@TODO Make a function for checking abstract type?
562
        if ($returnType instanceof InterfaceType || $returnType instanceof UnionType) {
563
            return $this->completeAbstractValue($returnType, $fieldNodes, $info, $path, $result);
564
        }
565
566
        // Field type must be Object, Interface or Union and expect sub-selections.
567
        if ($returnType instanceof ObjectType) {
568
            return $this->completeObjectValue($returnType, $fieldNodes, $info, $path, $result);
569
        }
570
571
        throw new ExecutionException("Cannot complete value of unexpected type \"{$returnType}\".");
572
    }
573
574
    /**
575
     * @param AbstractTypeInterface $returnType
576
     * @param                       $fieldNodes
577
     * @param ResolveInfo           $info
578
     * @param                       $path
579
     * @param                       $result
580
     * @return array
581
     * @throws ExecutionException
582
     * @throws InvalidTypeException
583
     * @throws \Digia\GraphQL\Error\InvariantException
584
     * @throws \Throwable
585
     */
586
    private function completeAbstractValue(
587
        AbstractTypeInterface $returnType,
588
        $fieldNodes,
589
        ResolveInfo $info,
590
        $path,
591
        &$result
592
    ) {
593
        $runtimeType = $returnType->resolveType($result, $this->context, $info);
594
595
        if (null === $runtimeType) {
596
            throw new ExecutionException(
597
                sprintf(
598
                    "GraphQL Interface Type `%s` returned `null` from it`s `resolveType` function for value: %s",
599
                    $returnType->getName(), toString($result)
600
                )
601
            );
602
        }
603
604
        //@TODO Check if $runtimeType is a valid runtime type
605
        return $this->completeObjectValue(
606
            $runtimeType,
607
            $fieldNodes,
608
            $info,
609
            $path,
610
            $result
611
        );
612
    }
613
614
    /**
615
     * @param ListType    $returnType
616
     * @param             $fieldNodes
617
     * @param ResolveInfo $info
618
     * @param             $path
619
     * @param             $result
620
     * @return array
621
     * @throws \Throwable
622
     */
623
    private function completeListValue(
624
        ListType $returnType,
625
        $fieldNodes,
626
        ResolveInfo $info,
627
        $path,
628
        &$result
629
    ) {
630
        $itemType = $returnType->getOfType();
631
632
        $completedItems = [];
633
634
        foreach ($result as $key => $item) {
635
            $fieldPath        = $path;
636
            $fieldPath[]      = $key;
637
            $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...
638
            $completedItems[] = $completedItem;
639
        }
640
641
        return $completedItems;
642
    }
643
644
    /**
645
     * @param LeafTypeInterface $returnType
646
     * @param                   $result
647
     * @return mixed
648
     * @throws ExecutionException
649
     */
650
    private function completeLeafValue(LeafTypeInterface $returnType, &$result)
651
    {
652
        $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

652
        /** @scrutinizer ignore-call */ 
653
        $serializedResult = $returnType->serialize($result);
Loading history...
653
654
        if ($serializedResult === null) {
655
            throw new ExecutionException(
656
                sprintf('Expected a value of type "%s" but received: %s', toString($returnType), toString($result))
657
            );
658
        }
659
660
        return $serializedResult;
661
    }
662
663
    /**
664
     * @param ObjectType  $returnType
665
     * @param             $fieldNodes
666
     * @param ResolveInfo $info
667
     * @param             $path
668
     * @param             $result
669
     * @return array
670
     * @throws ExecutionException
671
     * @throws InvalidTypeException
672
     * @throws \Digia\GraphQL\Error\InvariantException
673
     * @throws \Throwable
674
     */
675
    private function completeObjectValue(
676
        ObjectType $returnType,
677
        $fieldNodes,
678
        ResolveInfo $info,
679
        $path,
680
        &$result
681
    ) {
682
        return $this->collectAndExecuteSubFields(
683
            $returnType,
684
            $fieldNodes,
685
            $info,
686
            $path,
687
            $result
688
        );
689
    }
690
691
    /**
692
     * @param Field            $field
693
     * @param FieldNode        $fieldNode
694
     * @param callable         $resolveFunction
695
     * @param                  $rootValue
696
     * @param ExecutionContext $context
697
     * @param ResolveInfo      $info
698
     * @return array|\Throwable
699
     */
700
    private function resolveFieldValueOrError(
701
        Field $field,
702
        FieldNode $fieldNode,
703
        ?callable $resolveFunction,
704
        $rootValue,
705
        ExecutionContext $context,
706
        ResolveInfo $info
707
    ) {
708
        try {
709
            $args = $this->valuesResolver->coerceArgumentValues($field, $fieldNode, $context->getVariableValues());
710
711
            return $resolveFunction($rootValue, $args, $context->getContextValue(), $info);
712
        } catch (\Throwable $error) {
713
            return $error;
714
        }
715
    }
716
717
    /**
718
     * Try to resolve a field without any field resolver function.
719
     *
720
     * @param array|object $rootValue
721
     * @param              $args
722
     * @param              $context
723
     * @param ResolveInfo  $info
724
     * @return mixed|null
725
     */
726
    public static function defaultFieldResolver($rootValue, $args, $context, ResolveInfo $info)
727
    {
728
        $fieldName = $info->getFieldName();
729
        $property  = null;
730
731
        if (is_array($rootValue) && isset($rootValue[$fieldName])) {
732
            $property = $rootValue[$fieldName];
733
        }
734
735
        if (is_object($rootValue)) {
736
            $getter = 'get' . ucfirst($fieldName);
737
            if (method_exists($rootValue, $fieldName)) {
738
                $property = $rootValue->{$getter}();
739
            }
740
741
            if (property_exists($rootValue, $fieldName)) {
742
                $property = $rootValue->{$fieldName};
743
            }
744
        }
745
746
747
        return $property instanceof \Closure ? $property($rootValue, $args, $context, $info) : $property;
748
    }
749
750
    /**
751
     * @param ObjectType  $returnType
752
     * @param             $fieldNodes
753
     * @param ResolveInfo $info
754
     * @param             $path
755
     * @param             $result
756
     * @return array
757
     * @throws InvalidTypeException
758
     * @throws \Digia\GraphQL\Error\ExecutionException
759
     * @throws \Digia\GraphQL\Error\InvariantException
760
     * @throws \Throwable
761
     */
762
    private function collectAndExecuteSubFields(
763
        ObjectType $returnType,
764
        $fieldNodes,
765
        ResolveInfo $info,
766
        $path,
767
        &$result
768
    ) {
769
        $subFields = [];
770
        $visitedFragmentNames = [];
771
772
        foreach ($fieldNodes as $fieldNode) {
773
            /** @var FieldNode $fieldNode */
774
            if ($fieldNode->getSelectionSet() !== null) {
775
                $subFields = $this->collectFields(
776
                    $returnType,
777
                    $fieldNode->getSelectionSet(),
778
                    $subFields,
779
                    $visitedFragmentNames
780
                );
781
            }
782
        }
783
784
        if (count($subFields) > 0) {
785
            return $this->executeFields($returnType, $result, $path, $subFields);
786
        }
787
788
        return $result;
789
    }
790
}
791