Passed
Pull Request — master (#200)
by Quang
02:36
created

FieldCollector   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 19
dl 0
loc 123
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
D collectFields() 0 41 9
A __construct() 0 3 1
A doesFragmentConditionMatch() 0 21 4
B shouldIncludeNode() 0 18 5
1
<?php
2
3
namespace Digia\GraphQL\Execution;
4
5
use Digia\GraphQL\Error\InvalidTypeException;
6
use Digia\GraphQL\Language\Node\FieldNode;
7
use Digia\GraphQL\Language\Node\FragmentDefinitionNode;
8
use Digia\GraphQL\Language\Node\FragmentSpreadNode;
9
use Digia\GraphQL\Language\Node\InlineFragmentNode;
10
use Digia\GraphQL\Language\Node\NodeInterface;
11
use Digia\GraphQL\Language\Node\SelectionSetNode;
12
use Digia\GraphQL\Type\Definition\AbstractTypeInterface;
13
use Digia\GraphQL\Type\Definition\ObjectType;
14
use function Digia\GraphQL\Util\typeFromAST;
15
16
/**
17
 * Class FieldCollector
18
 * @package Digia\GraphQL\Execution
19
 */
20
class FieldCollector
21
{
22
    /**
23
     * @var ExecutionContext
24
     */
25
    protected $context;
26
27
    /**
28
     * FieldCollector constructor.
29
     * @param ExecutionContext $context
30
     */
31
    public function __construct(ExecutionContext $context)
32
    {
33
        $this->context = $context;
34
    }
35
36
    /**
37
     * @param ObjectType       $runtimeType
38
     * @param SelectionSetNode $selectionSet
39
     * @param array            $fields
40
     * @param array            $visitedFragmentNames
41
     * @return array
42
     * @throws InvalidTypeException
43
     * @throws \Digia\GraphQL\Error\ExecutionException
44
     * @throws \Digia\GraphQL\Error\InvariantException
45
     */
46
    public function collectFields(
47
        ObjectType $runtimeType,
48
        SelectionSetNode $selectionSet,
49
        array &$fields,
50
        array &$visitedFragmentNames
51
    ): array {
52
        foreach ($selectionSet->getSelections() as $selection) {
53
            // Check if this Node should be included first
54
            if (!$this->shouldIncludeNode($selection)) {
55
                continue;
56
            }
57
            // Collect fields
58
            if ($selection instanceof FieldNode) {
59
                $fieldName = $selection->getAliasOrNameValue();
60
61
                if (!isset($fields[$fieldName])) {
62
                    $fields[$fieldName] = [];
63
                }
64
65
                $fields[$fieldName][] = $selection;
66
            } elseif ($selection instanceof InlineFragmentNode) {
67
                if (!$this->doesFragmentConditionMatch($selection, $runtimeType)) {
68
                    continue;
69
                }
70
71
                $this->collectFields($runtimeType, $selection->getSelectionSet(), $fields, $visitedFragmentNames);
72
            } elseif ($selection instanceof FragmentSpreadNode) {
73
                $fragmentName = $selection->getNameValue();
74
75
                if (!empty($visitedFragmentNames[$fragmentName])) {
76
                    continue;
77
                }
78
79
                $visitedFragmentNames[$fragmentName] = true;
80
                /** @var FragmentDefinitionNode $fragment */
81
                $fragment = $this->context->getFragments()[$fragmentName];
82
                $this->collectFields($runtimeType, $fragment->getSelectionSet(), $fields, $visitedFragmentNames);
83
            }
84
        }
85
86
        return $fields;
87
    }
88
89
    /**
90
     * @param $node
91
     * @return bool
92
     * @throws InvalidTypeException
93
     * @throws \Digia\GraphQL\Error\ExecutionException
94
     * @throws \Digia\GraphQL\Error\InvariantException
95
     */
96
    protected function shouldIncludeNode(NodeInterface $node): bool
97
    {
98
99
        $contextVariables = $this->context->getVariableValues();
100
101
        $skip = coerceDirectiveValues(SkipDirective(), $node, $contextVariables);
102
103
        if ($skip && $skip['if'] === true) {
104
            return false;
105
        }
106
107
        $include = coerceDirectiveValues(IncludeDirective(), $node, $contextVariables);
108
109
        if ($include && $include['if'] === false) {
110
            return false;
111
        }
112
113
        return true;
114
    }
115
116
    /**
117
     * @param FragmentDefinitionNode|InlineFragmentNode $fragment
118
     * @param ObjectType                                $type
119
     * @return bool
120
     * @throws InvalidTypeException
121
     */
122
    protected function doesFragmentConditionMatch(
123
        NodeInterface $fragment,
124
        ObjectType $type
125
    ): bool {
126
        $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\FragmentDefinitionNode or Digia\GraphQL\Language\Node\InlineFragmentNode. ( Ignorable by Annotation )

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

126
        /** @scrutinizer ignore-call */ 
127
        $typeConditionNode = $fragment->getTypeCondition();
Loading history...
127
128
        if (!$typeConditionNode) {
129
            return true;
130
        }
131
132
        $conditionalType = typeFromAST($this->context->getSchema(), $typeConditionNode);
133
134
        if ($conditionalType === $type) {
135
            return true;
136
        }
137
138
        if ($conditionalType instanceof AbstractTypeInterface) {
139
            return $this->context->getSchema()->isPossibleType($conditionalType, $type);
140
        }
141
142
        return false;
143
    }
144
145
}