Passed
Pull Request — master (#321)
by Christoffer
04:53
created

FieldCollector::doesFragmentConditionMatch()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 2
dl 0
loc 19
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL\Execution\Strategy;
4
5
use Digia\GraphQL\Error\InvalidTypeException;
6
use Digia\GraphQL\Error\InvariantException;
7
use Digia\GraphQL\Execution\ExecutionContext;
8
use Digia\GraphQL\Execution\ExecutionException;
9
use Digia\GraphQL\Execution\ValuesResolver;
10
use Digia\GraphQL\Language\Node\FieldNode;
11
use Digia\GraphQL\Language\Node\FragmentDefinitionNode;
12
use Digia\GraphQL\Language\Node\FragmentSpreadNode;
13
use Digia\GraphQL\Language\Node\InlineFragmentNode;
14
use Digia\GraphQL\Language\Node\NodeInterface;
15
use Digia\GraphQL\Language\Node\SelectionSetNode;
16
use Digia\GraphQL\Type\Definition\AbstractTypeInterface;
17
use Digia\GraphQL\Type\Definition\ObjectType;
18
use Digia\GraphQL\Util\ConversionException;
19
use Digia\GraphQL\Util\TypeASTConverter;
20
21
class FieldCollector
22
{
23
    /**
24
     * @var ExecutionContext
25
     */
26
    protected $context;
27
28
    /**
29
     * @var ValuesResolver
30
     */
31
    protected $valuesResolver;
32
33
    /**
34
     * FieldCollector constructor.
35
     * @param ExecutionContext $context
36
     * @param ValuesResolver   $valuesResolver
37
     */
38
    public function __construct(ExecutionContext $context, ValuesResolver $valuesResolver)
39
    {
40
        $this->context        = $context;
41
        $this->valuesResolver = $valuesResolver;
42
    }
43
44
    /**
45
     * @param ObjectType       $runtimeType
46
     * @param SelectionSetNode $selectionSet
47
     * @param array            $fields
48
     * @param array            $visitedFragmentNames
49
     * @return array
50
     * @throws InvalidTypeException
51
     * @throws ExecutionException
52
     * @throws InvariantException
53
     * @throws ConversionException
54
     */
55
    public function collectFields(
56
        ObjectType $runtimeType,
57
        SelectionSetNode $selectionSet,
58
        array &$fields,
59
        array &$visitedFragmentNames
60
    ): array {
61
        foreach ($selectionSet->getSelections() as $selection) {
62
            // Check if this Node should be included first
63
            if (!$this->shouldIncludeNode($selection)) {
64
                continue;
65
            }
66
67
            // Collect fields
68
            if ($selection instanceof FieldNode) {
69
                $fieldName = $selection->getAliasOrNameValue();
70
71
                if (!isset($fields[$fieldName])) {
72
                    $fields[$fieldName] = [];
73
                }
74
75
                $fields[$fieldName][] = $selection;
76
77
                continue;
78
            }
79
80
            if ($selection instanceof InlineFragmentNode) {
81
                if (!$this->doesFragmentConditionMatch($selection, $runtimeType)) {
82
                    continue;
83
                }
84
85
                $this->collectFields($runtimeType, $selection->getSelectionSet(), $fields, $visitedFragmentNames);
0 ignored issues
show
Bug introduced by
It seems like $selection->getSelectionSet() can also be of type null; however, parameter $selectionSet of Digia\GraphQL\Execution\...lector::collectFields() does only seem to accept Digia\GraphQL\Language\Node\SelectionSetNode, 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

85
                $this->collectFields($runtimeType, /** @scrutinizer ignore-type */ $selection->getSelectionSet(), $fields, $visitedFragmentNames);
Loading history...
86
87
                continue;
88
            }
89
90
            if ($selection instanceof FragmentSpreadNode) {
91
                $fragmentName = $selection->getNameValue();
92
93
                if (isset($visitedFragmentNames[$fragmentName])) {
94
                    continue;
95
                }
96
97
                $visitedFragmentNames[$fragmentName] = true;
98
99
                $fragment = $this->context->getFragments()[$fragmentName];
100
101
                $this->collectFields($runtimeType, $fragment->getSelectionSet(), $fields, $visitedFragmentNames);
102
103
                continue;
104
            }
105
        }
106
107
        return $fields;
108
    }
109
110
    /**
111
     * @param NodeInterface $node
112
     * @return bool
113
     * @throws InvalidTypeException
114
     * @throws ExecutionException
115
     * @throws InvariantException
116
     */
117
    protected function shouldIncludeNode(NodeInterface $node): bool
118
    {
119
        $contextVariables = $this->context->getVariableValues();
120
121
        $skip = $this->valuesResolver->coerceDirectiveValues(SkipDirective(), $node, $contextVariables);
122
123
        if ($skip && $skip['if'] === true) {
124
            return false;
125
        }
126
127
        $include = $this->valuesResolver->coerceDirectiveValues(IncludeDirective(), $node, $contextVariables);
128
129
        if ($include && $include['if'] === false) {
130
            return false;
131
        }
132
133
        return true;
134
    }
135
136
    /**
137
     * @param FragmentDefinitionNode|InlineFragmentNode $fragment
138
     * @param ObjectType                                $type
139
     * @return bool
140
     * @throws InvariantException
141
     * @throws ConversionException
142
     */
143
    protected function doesFragmentConditionMatch($fragment, ObjectType $type): bool
144
    {
145
        $typeConditionNode = $fragment->getTypeCondition();
146
147
        if (null === $typeConditionNode) {
148
            return true;
149
        }
150
151
        $conditionalType = TypeASTConverter::convert($this->context->getSchema(), $typeConditionNode);
152
153
        if ($type === $conditionalType) {
0 ignored issues
show
introduced by
The condition $type === $conditionalType is always false.
Loading history...
154
            return true;
155
        }
156
157
        if ($conditionalType instanceof AbstractTypeInterface) {
158
            return $this->context->getSchema()->isPossibleType($conditionalType, $type);
159
        }
160
161
        return false;
162
    }
163
164
}
165