Failed Conditions
Push — master ( 177394...bf4e7d )
by Vladimir
09:19
created

Values   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 284
Duplicated Lines 0 %

Test Coverage

Coverage 90.4%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 42
eloc 123
dl 0
loc 284
ccs 113
cts 125
cp 0.904
rs 9.0399
c 1
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
C getArgumentValuesForMap() 0 79 16
A getDirectiveValues() 0 16 4
A isValidPHPValue() 0 12 2
A valueFromAST() 0 3 1
C getVariableValues() 0 83 15
A getArgumentValues() 0 17 4

How to fix   Complexity   

Complex Class

Complex classes like Values often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Values, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Executor;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Language\AST\ArgumentNode;
9
use GraphQL\Language\AST\BooleanValueNode;
10
use GraphQL\Language\AST\DirectiveNode;
11
use GraphQL\Language\AST\EnumValueDefinitionNode;
12
use GraphQL\Language\AST\EnumValueNode;
13
use GraphQL\Language\AST\FieldDefinitionNode;
14
use GraphQL\Language\AST\FieldNode;
15
use GraphQL\Language\AST\FloatValueNode;
16
use GraphQL\Language\AST\FragmentSpreadNode;
17
use GraphQL\Language\AST\InlineFragmentNode;
18
use GraphQL\Language\AST\IntValueNode;
19
use GraphQL\Language\AST\ListValueNode;
20
use GraphQL\Language\AST\Node;
21
use GraphQL\Language\AST\NodeList;
22
use GraphQL\Language\AST\NullValueNode;
23
use GraphQL\Language\AST\ObjectValueNode;
24
use GraphQL\Language\AST\StringValueNode;
25
use GraphQL\Language\AST\ValueNode;
26
use GraphQL\Language\AST\VariableDefinitionNode;
27
use GraphQL\Language\AST\VariableNode;
28
use GraphQL\Language\Printer;
29
use GraphQL\Type\Definition\Directive;
30
use GraphQL\Type\Definition\EnumType;
31
use GraphQL\Type\Definition\FieldDefinition;
32
use GraphQL\Type\Definition\InputObjectType;
33
use GraphQL\Type\Definition\InputType;
34
use GraphQL\Type\Definition\ListOfType;
35
use GraphQL\Type\Definition\NonNull;
36
use GraphQL\Type\Definition\ScalarType;
37
use GraphQL\Type\Definition\Type;
38
use GraphQL\Type\Schema;
39
use GraphQL\Utils\AST;
40
use GraphQL\Utils\TypeInfo;
41
use GraphQL\Utils\Utils;
42
use GraphQL\Utils\Value;
43
use stdClass;
44
use Throwable;
45
use function array_key_exists;
46
use function array_map;
47
use function sprintf;
48
49
class Values
50
{
51
    /**
52
     * Prepares an object map of variables of the correct type based on the provided
53
     * variable definitions and arbitrary input. If the input cannot be coerced
54
     * to match the variable definitions, a Error will be thrown.
55
     *
56
     * @param VariableDefinitionNode[] $varDefNodes
57
     * @param mixed[]                  $inputs
58
     *
59
     * @return mixed[]
60
     */
61 251
    public static function getVariableValues(Schema $schema, $varDefNodes, array $inputs)
62
    {
63 251
        $errors        = [];
64 251
        $coercedValues = [];
65 251
        foreach ($varDefNodes as $varDefNode) {
66 61
            $varName = $varDefNode->variable->name->value;
67
            /** @var InputType|Type $varType */
68 61
            $varType = TypeInfo::typeFromAST($schema, $varDefNode->type);
69
70 61
            if (! Type::isInputType($varType)) {
71
                // Must use input types for variables. This should be caught during
72
                // validation, however is checked again here for safety.
73 2
                $errors[] = new Error(
74 2
                    sprintf(
75 2
                        'Variable "$%s" expected value of type "%s" which cannot be used as an input type.',
76 2
                        $varName,
77 2
                        Printer::doPrint($varDefNode->type)
78
                    ),
79 2
                    [$varDefNode->type]
80
                );
81
            } else {
82 59
                $hasValue = array_key_exists($varName, $inputs);
83 59
                $value    = $hasValue ? $inputs[$varName] : Utils::undefined();
84
85 59
                if (! $hasValue && $varDefNode->defaultValue) {
86
                    // If no value was provided to a variable with a default value,
87
                    // use the default value.
88 3
                    $coercedValues[$varName] = AST::valueFromAST($varDefNode->defaultValue, $varType);
89 57
                } elseif ((! $hasValue || $value === null) && ($varType instanceof NonNull)) {
90
                    // If no value or a nullish value was provided to a variable with a
91
                    // non-null type (required), produce an error.
92 4
                    $errors[] = new Error(
93 4
                        sprintf(
94 4
                            $hasValue
95 3
                                ? 'Variable "$%s" of non-null type "%s" must not be null.'
96 4
                                : 'Variable "$%s" of required type "%s" was not provided.',
97 4
                            $varName,
98 4
                            Utils::printSafe($varType)
99
                        ),
100 4
                        [$varDefNode]
101
                    );
102 53
                } elseif ($hasValue) {
103 48
                    if ($value === null) {
104
                        // If the explicit value `null` was provided, an entry in the coerced
105
                        // values must exist as the value `null`.
106 4
                        $coercedValues[$varName] = null;
107
                    } else {
108
                        // Otherwise, a non-null value was provided, coerce it to the expected
109
                        // type or report an error if coercion fails.
110 45
                        $coerced = Value::coerceValue($value, $varType, $varDefNode);
0 ignored issues
show
Bug introduced by
$varType of type GraphQL\Type\Definition\Type is incompatible with the type GraphQL\Type\Definition\InputType expected by parameter $type of GraphQL\Utils\Value::coerceValue(). ( Ignorable by Annotation )

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

110
                        $coerced = Value::coerceValue($value, /** @scrutinizer ignore-type */ $varType, $varDefNode);
Loading history...
111
                        /** @var Error[] $coercionErrors */
112 45
                        $coercionErrors = $coerced['errors'];
113 45
                        if ($coercionErrors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $coercionErrors of type GraphQL\Error\Error[] 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...
114 14
                            $messagePrelude = sprintf(
115 14
                                'Variable "$%s" got invalid value %s; ',
116 14
                                $varName,
117 14
                                Utils::printSafeJson($value)
118
                            );
119
120 14
                            foreach ($coercionErrors as $error) {
121 14
                                $errors[] = new Error(
122 14
                                    $messagePrelude . $error->getMessage(),
123 14
                                    $error->getNodes(),
124 14
                                    $error->getSource(),
125 14
                                    $error->getPositions(),
126 14
                                    $error->getPath(),
127 14
                                    $error,
128 14
                                    $error->getExtensions()
129
                                );
130
                            }
131
                        } else {
132 61
                            $coercedValues[$varName] = $coerced['value'];
133
                        }
134
                    }
135
                }
136
            }
137
        }
138
139 251
        if (! empty($errors)) {
140 20
            return [$errors, null];
141
        }
142
143 232
        return [null, $coercedValues];
144
    }
145
146
    /**
147
     * Prepares an object map of argument values given a directive definition
148
     * and a AST node which may contain directives. Optionally also accepts a map
149
     * of variable values.
150
     *
151
     * If the directive does not exist on the node, returns undefined.
152
     *
153
     * @param FragmentSpreadNode|FieldNode|InlineFragmentNode|EnumValueDefinitionNode|FieldDefinitionNode $node
154
     * @param mixed[]|null                                                                                $variableValues
155
     *
156
     * @return mixed[]|null
157
     */
158 339
    public static function getDirectiveValues(Directive $directiveDef, $node, $variableValues = null)
159
    {
160 339
        if (isset($node->directives) && $node->directives instanceof NodeList) {
161 339
            $directiveNode = Utils::find(
162 339
                $node->directives,
163
                static function (DirectiveNode $directive) use ($directiveDef) {
164 9
                    return $directive->name->value === $directiveDef->name;
165 339
                }
166
            );
167
168 339
            if ($directiveNode !== null) {
169 9
                return self::getArgumentValues($directiveDef, $directiveNode, $variableValues);
170
            }
171
        }
172
173 336
        return null;
174
    }
175
176
    /**
177
     * Prepares an object map of argument values given a list of argument
178
     * definitions and list of argument AST nodes.
179
     *
180
     * @param FieldDefinition|Directive $def
181
     * @param FieldNode|DirectiveNode   $node
182
     * @param mixed[]                   $variableValues
183
     *
184
     * @return mixed[]
185
     *
186
     * @throws Error
187
     */
188 226
    public static function getArgumentValues($def, $node, $variableValues = null)
189
    {
190 226
        if (empty($def->args)) {
191 169
            return [];
192
        }
193
194 115
        $argumentNodes = $node->arguments;
195 115
        if (empty($argumentNodes)) {
196
            return [];
197
        }
198
199 115
        $argumentValueMap = [];
200 115
        foreach ($argumentNodes as $argumentNode) {
201 98
            $argumentValueMap[$argumentNode->name->value] = $argumentNode->value;
202
        }
203
204 115
        return static::getArgumentValuesForMap($def, $argumentValueMap, $variableValues, $node);
205
    }
206
207
    /**
208
     * @param FieldDefinition|Directive $fieldDefinition
209
     * @param ArgumentNode[]            $argumentValueMap
210
     * @param mixed[]                   $variableValues
211
     * @param Node|null                 $referenceNode
212
     *
213
     * @return mixed[]
214
     *
215
     * @throws Error
216
     */
217 226
    public static function getArgumentValuesForMap($fieldDefinition, $argumentValueMap, $variableValues = null, $referenceNode = null)
218
    {
219 226
        $argumentDefinitions = $fieldDefinition->args;
220 226
        $coercedValues       = [];
221
222 226
        foreach ($argumentDefinitions as $argumentDefinition) {
223 115
            $name              = $argumentDefinition->name;
224 115
            $argType           = $argumentDefinition->getType();
225 115
            $argumentValueNode = $argumentValueMap[$name] ?? null;
226
227 115
            if ($argumentValueNode instanceof VariableNode) {
228 38
                $variableName = $argumentValueNode->name->value;
229 38
                $hasValue     = $variableValues ? array_key_exists($variableName, $variableValues) : false;
230 38
                $isNull       = $hasValue ? $variableValues[$variableName] === null : false;
231
            } else {
232 85
                $hasValue = $argumentValueNode !== null;
233 85
                $isNull   = $argumentValueNode instanceof NullValueNode;
234
            }
235
236 115
            if (! $hasValue && $argumentDefinition->defaultValueExists()) {
237
                // If no argument was provided where the definition has a default value,
238
                // use the default value.
239 14
                $coercedValues[$name] = $argumentDefinition->defaultValue;
240 111
            } elseif ((! $hasValue || $isNull) && ($argType instanceof NonNull)) {
241
                // If no argument or a null value was provided to an argument with a
242
                // non-null type (required), produce a field error.
243 6
                if ($isNull) {
244 2
                    throw new Error(
245 2
                        'Argument "' . $name . '" of non-null type ' .
246 2
                        '"' . Utils::printSafe($argType) . '" must not be null.',
247 2
                        $referenceNode
248
                    );
249
                }
250
251 4
                if ($argumentValueNode instanceof VariableNode) {
252 2
                    $variableName = $argumentValueNode->name->value;
253 2
                    throw new Error(
254 2
                        'Argument "' . $name . '" of required type "' . Utils::printSafe($argType) . '" was ' .
255 2
                        'provided the variable "$' . $variableName . '" which was not provided ' .
256 2
                        'a runtime value.',
257 2
                        [$argumentValueNode]
258
                    );
259
                }
260
261 2
                throw new Error(
262 2
                    'Argument "' . $name . '" of required type ' .
263 2
                    '"' . Utils::printSafe($argType) . '" was not provided.',
264 2
                    $referenceNode
265
                );
266 105
            } elseif ($hasValue) {
267 89
                if ($argumentValueNode instanceof NullValueNode) {
268
                  // If the explicit value `null` was provided, an entry in the coerced
269
                  // values must exist as the value `null`.
270
                    $coercedValues[$name] = null;
271 89
                } elseif ($argumentValueNode instanceof VariableNode) {
272 30
                    $variableName = $argumentValueNode->name->value;
273 30
                    Utils::invariant($variableValues !== null, 'Must exist for hasValue to be true.');
274
                  // Note: This does no further checking that this variable is correct.
275
                  // This assumes that this query has been validated and the variable
276
                  // usage here is of the correct type.
277 30
                    $coercedValues[$name] = $variableValues[$variableName] ?? null;
278
                } else {
279 63
                    $valueNode    = $argumentValueNode;
280 63
                    $coercedValue = AST::valueFromAST($valueNode, $argType, $variableValues);
0 ignored issues
show
Bug introduced by
It seems like $valueNode can also be of type GraphQL\Language\AST\ArgumentNode; however, parameter $valueNode of GraphQL\Utils\AST::valueFromAST() does only seem to accept GraphQL\Language\AST\ValueNode|null, 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

280
                    $coercedValue = AST::valueFromAST(/** @scrutinizer ignore-type */ $valueNode, $argType, $variableValues);
Loading history...
281 63
                    if (Utils::isInvalid($coercedValue)) {
282
                      // Note: ValuesOfCorrectType validation should catch this before
283
                      // execution. This is a runtime check to ensure execution does not
284
                      // continue with an invalid argument value.
285 3
                        throw new Error(
286 3
                            'Argument "' . $name . '" has invalid value ' . Printer::doPrint($valueNode) . '.',
287 3
                            [$argumentValueNode]
288
                        );
289
                    }
290 108
                    $coercedValues[$name] = $coercedValue;
291
                }
292
            }
293
        }
294
295 219
        return $coercedValues;
296
    }
297
298
    /**
299
     * @deprecated as of 8.0 (Moved to \GraphQL\Utils\AST::valueFromAST)
300
     *
301
     * @param VariableNode|NullValueNode|IntValueNode|FloatValueNode|StringValueNode|BooleanValueNode|EnumValueNode|ListValueNode|ObjectValueNode $valueNode
302
     * @param ScalarType|EnumType|InputObjectType|ListOfType|NonNull                                                                              $type
303
     * @param mixed[]|null                                                                                                                        $variables
304
     *
305
     * @return mixed[]|stdClass|null
306
     */
307
    public static function valueFromAST(ValueNode $valueNode, InputType $type, ?array $variables = null)
308
    {
309
        return AST::valueFromAST($valueNode, $type, $variables);
310
    }
311
312
    /**
313
     * @deprecated as of 0.12 (Use coerceValue() directly for richer information)
314
     *
315
     * @param mixed[] $value
316
     *
317
     * @return string[]
318
     *
319
     * @paarm ScalarType|EnumType|InputObjectType|ListOfType|NonNull $type
320
     */
321
    public static function isValidPHPValue($value, InputType $type)
322
    {
323
        $errors = Value::coerceValue($value, $type)['errors'];
324
325
        return $errors
326
            ? array_map(
327
                static function (Throwable $error) {
328
                    return $error->getMessage();
329
                },
330
                $errors
331
            )
332
            : [];
333
    }
334
}
335