Passed
Push — master ( f24e00...29eba8 )
by Vladimir
10:15
created

ResolveInfo::foldSelectionSet()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 8

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 26
ccs 19
cts 19
cp 1
rs 8.4444
c 0
b 0
f 0
cc 8
nc 9
nop 2
crap 8
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Type\Definition;
6
7
use GraphQL\Language\AST\FieldNode;
8
use GraphQL\Language\AST\FragmentDefinitionNode;
9
use GraphQL\Language\AST\FragmentSpreadNode;
10
use GraphQL\Language\AST\InlineFragmentNode;
11
use GraphQL\Language\AST\OperationDefinitionNode;
12
use GraphQL\Language\AST\SelectionSetNode;
13
use GraphQL\Type\Schema;
14
use function array_merge_recursive;
15
16
/**
17
 * Structure containing information useful for field resolution process.
18
 * Passed as 3rd argument to every field resolver. See [docs on field resolving (data fetching)](data-fetching.md).
19
 */
20
class ResolveInfo
21
{
22
    /**
23
     * The name of the field being resolved
24
     *
25
     * @api
26
     * @var string
27
     */
28
    public $fieldName;
29
30
    /**
31
     * AST of all nodes referencing this field in the query.
32
     *
33
     * @api
34
     * @var FieldNode[]
35
     */
36
    public $fieldNodes = [];
37
38
    /**
39
     * Expected return type of the field being resolved
40
     *
41
     * @api
42
     * @var ScalarType|ObjectType|InterfaceType|UnionType|EnumType|ListOfType|NonNull
43
     */
44
    public $returnType;
45
46
    /**
47
     * Parent type of the field being resolved
48
     *
49
     * @api
50
     * @var ObjectType
51
     */
52
    public $parentType;
53
54
    /**
55
     * Path to this field from the very root value
56
     *
57
     * @api
58
     * @var string[][]
59
     */
60
    public $path;
61
62
    /**
63
     * Instance of a schema used for execution
64
     *
65
     * @api
66
     * @var Schema
67
     */
68
    public $schema;
69
70
    /**
71
     * AST of all fragments defined in query
72
     *
73
     * @api
74
     * @var FragmentDefinitionNode[]
75
     */
76
    public $fragments = [];
77
78
    /**
79
     * Root value passed to query execution
80
     *
81
     * @api
82
     * @var mixed|null
83
     */
84
    public $rootValue;
85
86
    /**
87
     * AST of operation definition node (query, mutation)
88
     *
89
     * @api
90
     * @var OperationDefinitionNode|null
91
     */
92
    public $operation;
93
94
    /**
95
     * Array of variables passed to query execution
96
     *
97
     * @api
98
     * @var mixed[]
99
     */
100
    public $variableValues = [];
101
102
    /** @var QueryPlan */
103
    private $queryPlan;
104
105
    /**
106
     * @param FieldNode[]                                                               $fieldNodes
107
     * @param ScalarType|ObjectType|InterfaceType|UnionType|EnumType|ListOfType|NonNull $returnType
108
     * @param string[][]                                                                $path
109
     * @param FragmentDefinitionNode[]                                                  $fragments
110
     * @param mixed|null                                                                $rootValue
111
     * @param mixed[]                                                                   $variableValues
112
     */
113 193
    public function __construct(
114
        string $fieldName,
115
        iterable $fieldNodes,
116
        $returnType,
117
        ObjectType $parentType,
118
        array $path,
119
        Schema $schema,
120
        array $fragments,
121
        $rootValue,
122
        ?OperationDefinitionNode $operation,
123
        array $variableValues
124
    ) {
125 193
        $this->fieldName      = $fieldName;
126 193
        $this->fieldNodes     = $fieldNodes;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fieldNodes of type iterable is incompatible with the declared type GraphQL\Language\AST\FieldNode[] of property $fieldNodes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
127 193
        $this->returnType     = $returnType;
128 193
        $this->parentType     = $parentType;
129 193
        $this->path           = $path;
130 193
        $this->schema         = $schema;
131 193
        $this->fragments      = $fragments;
132 193
        $this->rootValue      = $rootValue;
133 193
        $this->operation      = $operation;
134 193
        $this->variableValues = $variableValues;
135 193
    }
136
137
    /**
138
     * Helper method that returns names of all fields selected in query for
139
     * $this->fieldName up to $depth levels
140
     *
141
     * Example:
142
     * query MyQuery{
143
     * {
144
     *   root {
145
     *     id,
146
     *     nested {
147
     *      nested1
148
     *      nested2 {
149
     *        nested3
150
     *      }
151
     *     }
152
     *   }
153
     * }
154
     *
155
     * Given this ResolveInfo instance is a part of "root" field resolution, and $depth === 1,
156
     * method will return:
157
     * [
158
     *     'id' => true,
159
     *     'nested' => [
160
     *         nested1 => true,
161
     *         nested2 => true
162
     *     ]
163
     * ]
164
     *
165
     * Warning: this method it is a naive implementation which does not take into account
166
     * conditional typed fragments. So use it with care for fields of interface and union types.
167
     *
168
     * @param int $depth How many levels to include in output
169
     *
170
     * @return bool[]
171
     *
172
     * @api
173
     */
174 3
    public function getFieldSelection($depth = 0)
175
    {
176 3
        $fields = [];
177
178
        /** @var FieldNode $fieldNode */
179 3
        foreach ($this->fieldNodes as $fieldNode) {
180 3
            $fields = array_merge_recursive($fields, $this->foldSelectionSet($fieldNode->selectionSet, $depth));
0 ignored issues
show
Bug introduced by
It seems like $fieldNode->selectionSet can also be of type null; however, parameter $selectionSet of GraphQL\Type\Definition\...nfo::foldSelectionSet() does only seem to accept GraphQL\Language\AST\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

180
            $fields = array_merge_recursive($fields, $this->foldSelectionSet(/** @scrutinizer ignore-type */ $fieldNode->selectionSet, $depth));
Loading history...
181
        }
182
183 3
        return $fields;
184
    }
185
186 2
    public function lookAhead() : QueryPlan
187
    {
188 2
        if ($this->queryPlan === null) {
189 2
            $this->queryPlan = new QueryPlan(
190 2
                $this->parentType,
191 2
                $this->schema,
192 2
                $this->fieldNodes,
193 2
                $this->variableValues,
194 2
                $this->fragments
195
            );
196
        }
197
198 2
        return $this->queryPlan;
199
    }
200
201
    /**
202
     * @return bool[]
203
     */
204 3
    private function foldSelectionSet(SelectionSetNode $selectionSet, int $descend) : array
205
    {
206 3
        $fields = [];
207 3
        foreach ($selectionSet->selections as $selectionNode) {
208 3
            if ($selectionNode instanceof FieldNode) {
209 3
                $fields[$selectionNode->name->value] = $descend > 0 && ! empty($selectionNode->selectionSet)
210 2
                    ? $this->foldSelectionSet($selectionNode->selectionSet, $descend - 1)
211 3
                    : true;
212 2
            } elseif ($selectionNode instanceof FragmentSpreadNode) {
213 2
                $spreadName = $selectionNode->name->value;
214 2
                if (isset($this->fragments[$spreadName])) {
215
                    /** @var FragmentDefinitionNode $fragment */
216 2
                    $fragment = $this->fragments[$spreadName];
217 2
                    $fields   = array_merge_recursive(
218 2
                        $this->foldSelectionSet($fragment->selectionSet, $descend),
219 2
                        $fields
220
                    );
221
                }
222 2
            } elseif ($selectionNode instanceof InlineFragmentNode) {
223 2
                $fields = array_merge_recursive(
224 2
                    $this->foldSelectionSet($selectionNode->selectionSet, $descend),
225 3
                    $fields
226
                );
227
            }
228
        }
229 3
        return $fields;
230
    }
231
}
232