Passed
Pull Request — master (#217)
by Christoffer
02:32
created

FieldCollector   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Importance

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

4 Methods

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

131
        /** @scrutinizer ignore-call */ 
132
        $typeConditionNode = $fragment->getTypeCondition();
Loading history...
132
133
        if (null === $typeConditionNode) {
134
            return true;
135
        }
136
137
        /** @var ObjectType|TypeInterface $conditionalType */
138
        $conditionalType = typeFromAST($this->context->getSchema(), $typeConditionNode);
139
140
        if ($type === $conditionalType) {
141
            return true;
142
        }
143
144
        if ($conditionalType instanceof AbstractTypeInterface) {
145
            return $this->context->getSchema()->isPossibleType($conditionalType, $type);
146
        }
147
148
        return false;
149
    }
150
151
}
152