Failed Conditions
Push — master ( a4f39b...12ee90 )
by Vladimir
11:17
created

ValidationContext::getErrors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Validator;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Language\AST\DocumentNode;
9
use GraphQL\Language\AST\FieldNode;
10
use GraphQL\Language\AST\FragmentDefinitionNode;
11
use GraphQL\Language\AST\FragmentSpreadNode;
12
use GraphQL\Language\AST\HasSelectionSet;
13
use GraphQL\Language\AST\InlineFragmentNode;
14
use GraphQL\Language\AST\NodeKind;
15
use GraphQL\Language\AST\OperationDefinitionNode;
16
use GraphQL\Language\AST\SelectionSetNode;
17
use GraphQL\Language\AST\VariableNode;
18
use GraphQL\Language\Visitor;
19
use GraphQL\Type\Definition\EnumType;
20
use GraphQL\Type\Definition\FieldDefinition;
21
use GraphQL\Type\Definition\InputObjectType;
22
use GraphQL\Type\Definition\InputType;
23
use GraphQL\Type\Definition\ListOfType;
24
use GraphQL\Type\Definition\NonNull;
25
use GraphQL\Type\Definition\ScalarType;
26
use GraphQL\Type\Definition\Type;
27
use GraphQL\Type\Schema;
28
use GraphQL\Utils\TypeInfo;
29
use SplObjectStorage;
30
use function array_pop;
31
use function call_user_func_array;
32
use function count;
33
34
/**
35
 * An instance of this class is passed as the "this" context to all validators,
36
 * allowing access to commonly useful contextual information from within a
37
 * validation rule.
38
 */
39
class ValidationContext extends ASTValidationContext
40
{
41
    /** @var TypeInfo */
42
    private $typeInfo;
43
44
    /** @var FragmentDefinitionNode[] */
45
    private $fragments;
46
47
    /** @var SplObjectStorage */
48
    private $fragmentSpreads;
49
50
    /** @var SplObjectStorage */
51
    private $recursivelyReferencedFragments;
52
53
    /** @var SplObjectStorage */
54
    private $variableUsages;
55
56
    /** @var SplObjectStorage */
57
    private $recursiveVariableUsages;
58
59 528
    public function __construct(Schema $schema, DocumentNode $ast, TypeInfo $typeInfo)
60
    {
61 528
        parent::__construct($ast, $schema);
62 528
        $this->typeInfo                       = $typeInfo;
63 528
        $this->fragmentSpreads                = new SplObjectStorage();
64 528
        $this->recursivelyReferencedFragments = new SplObjectStorage();
65 528
        $this->variableUsages                 = new SplObjectStorage();
66 528
        $this->recursiveVariableUsages        = new SplObjectStorage();
67 528
    }
68
69
    /**
70
     * @return mixed[][] List of ['node' => VariableNode, 'type' => ?InputObjectType]
71
     */
72 167
    public function getRecursiveVariableUsages(OperationDefinitionNode $operation)
73
    {
74 167
        $usages = $this->recursiveVariableUsages[$operation] ?? null;
75
76 167
        if ($usages === null) {
77 167
            $usages    = $this->getVariableUsages($operation);
78 167
            $fragments = $this->getRecursivelyReferencedFragments($operation);
79
80 167
            $tmp = [$usages];
81 167
            foreach ($fragments as $i => $fragment) {
82 33
                $tmp[] = $this->getVariableUsages($fragments[$i]);
83
            }
84 167
            $usages                                    = call_user_func_array('array_merge', $tmp);
85 167
            $this->recursiveVariableUsages[$operation] = $usages;
86
        }
87
88 167
        return $usages;
89
    }
90
91
    /**
92
     * @return mixed[][] List of ['node' => VariableNode, 'type' => ?InputObjectType]
93
     */
94 167
    private function getVariableUsages(HasSelectionSet $node)
95
    {
96 167
        $usages = $this->variableUsages[$node] ?? null;
97
98 167
        if ($usages === null) {
99 167
            $newUsages = [];
100 167
            $typeInfo  = new TypeInfo($this->schema);
101 167
            Visitor::visit(
102 167
                $node,
103 167
                Visitor::visitWithTypeInfo(
104 167
                    $typeInfo,
105
                    [
106
                        NodeKind::VARIABLE_DEFINITION => static function () {
107 62
                            return false;
108 167
                        },
109
                        NodeKind::VARIABLE            => static function (VariableNode $variable) use (
110 64
                            &$newUsages,
111 64
                            $typeInfo
112
                        ) {
113 64
                            $newUsages[] = [
114 64
                                'node' => $variable,
115 64
                                'type' => $typeInfo->getInputType(),
116 64
                                'defaultValue' => $typeInfo->getDefaultValue(),
117
                            ];
118 167
                        },
119
                    ]
120
                )
121
            );
122 167
            $usages                      = $newUsages;
123 167
            $this->variableUsages[$node] = $usages;
124
        }
125
126 167
        return $usages;
127
    }
128
129
    /**
130
     * @return FragmentDefinitionNode[]
131
     */
132 172
    public function getRecursivelyReferencedFragments(OperationDefinitionNode $operation)
133
    {
134 172
        $fragments = $this->recursivelyReferencedFragments[$operation] ?? null;
135
136 172
        if ($fragments === null) {
137 172
            $fragments      = [];
138 172
            $collectedNames = [];
139 172
            $nodesToVisit   = [$operation];
140 172
            while (! empty($nodesToVisit)) {
141 172
                $node    = array_pop($nodesToVisit);
142 172
                $spreads = $this->getFragmentSpreads($node);
143 172
                foreach ($spreads as $spread) {
144 38
                    $fragName = $spread->name->value;
145
146 38
                    if (! empty($collectedNames[$fragName])) {
147 7
                        continue;
148
                    }
149
150 38
                    $collectedNames[$fragName] = true;
151 38
                    $fragment                  = $this->getFragment($fragName);
152 38
                    if (! $fragment) {
153 1
                        continue;
154
                    }
155
156 37
                    $fragments[]    = $fragment;
157 37
                    $nodesToVisit[] = $fragment;
158
                }
159
            }
160 172
            $this->recursivelyReferencedFragments[$operation] = $fragments;
161
        }
162
163 172
        return $fragments;
164
    }
165
166
    /**
167
     * @param OperationDefinitionNode|FragmentDefinitionNode $node
168
     *
169
     * @return FragmentSpreadNode[]
170
     */
171 188
    public function getFragmentSpreads(HasSelectionSet $node) : array
172
    {
173 188
        $spreads = $this->fragmentSpreads[$node] ?? null;
174 188
        if ($spreads === null) {
175 188
            $spreads = [];
176
            /** @var SelectionSetNode[] $setsToVisit */
177 188
            $setsToVisit = [$node->selectionSet];
178 188
            while (! empty($setsToVisit)) {
179 188
                $set = array_pop($setsToVisit);
180
181 188
                for ($i = 0, $selectionCount = count($set->selections); $i < $selectionCount; $i++) {
182 188
                    $selection = $set->selections[$i];
183 188
                    if ($selection instanceof FragmentSpreadNode) {
184 53
                        $spreads[] = $selection;
185
                    } else {
186
                        /** @var FieldNode|InlineFragmentNode $selection*/
187 180
                        if ($selection->selectionSet) {
188 102
                            $setsToVisit[] = $selection->selectionSet;
189
                        }
190
                    }
191
                }
192
            }
193 188
            $this->fragmentSpreads[$node] = $spreads;
194
        }
195
196 188
        return $spreads;
197
    }
198
199
    /**
200
     * @param string $name
201
     *
202
     * @return FragmentDefinitionNode|null
203
     */
204 84
    public function getFragment($name)
205
    {
206 84
        $fragments = $this->fragments;
207 84
        if (! $fragments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fragments of type GraphQL\Language\AST\FragmentDefinitionNode[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
208 84
            $fragments = [];
209 84
            foreach ($this->getDocument()->definitions as $statement) {
210 84
                if (! ($statement instanceof FragmentDefinitionNode)) {
211 49
                    continue;
212
                }
213
214 84
                $fragments[$statement->name->value] = $statement;
215
            }
216 84
            $this->fragments = $fragments;
217
        }
218
219 84
        return $fragments[$name] ?? null;
220
    }
221
222
    /**
223
     * Returns OutputType
224
     *
225
     * @return Type
226
     */
227 129
    public function getType()
228
    {
229 129
        return $this->typeInfo->getType();
230
    }
231
232
    /**
233
     * @return Type
234
     */
235 220
    public function getParentType()
236
    {
237 220
        return $this->typeInfo->getParentType();
238
    }
239
240
    /**
241
     * @return ScalarType|EnumType|InputObjectType|ListOfType|NonNull
242
     */
243 118
    public function getInputType() : ?InputType
244
    {
245 118
        return $this->typeInfo->getInputType();
246
    }
247
248
    /**
249
     * @return ScalarType|EnumType|InputObjectType|ListOfType|NonNull
250
     */
251 19
    public function getParentInputType() : ?InputType
252
    {
253 19
        return $this->typeInfo->getParentInputType();
254
    }
255
256
    /**
257
     * @return FieldDefinition
258
     */
259 157
    public function getFieldDef()
260
    {
261 157
        return $this->typeInfo->getFieldDef();
262
    }
263
264 5
    public function getDirective()
265
    {
266 5
        return $this->typeInfo->getDirective();
267
    }
268
269 106
    public function getArgument()
270
    {
271 106
        return $this->typeInfo->getArgument();
272
    }
273
}
274