Values   C
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 309
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 85.4%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 60
c 1
b 0
f 0
lcom 1
cbo 7
dl 0
loc 309
ccs 117
cts 137
cp 0.854
rs 6.0975

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getVariableValues() 0 9 3
B getArgumentValues() 0 19 5
B getDirectiveValue() 0 19 6
B getVariableValue() 0 22 5
C isValidValue() 0 46 14
C coerceValue() 0 43 11
C coerceValueAST() 0 73 16

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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
namespace Fubhy\GraphQL\Executor;
4
5
use Fubhy\GraphQL\Language\Node;
6
use Fubhy\GraphQL\Language\Node\VariableDefinition;
7
use Fubhy\GraphQL\Schema;
8
use Fubhy\GraphQL\Type\Definition\Types\TypeInterface;
9
use Fubhy\GraphQL\Type\Directives\DirectiveInterface;
10
use Fubhy\GraphQL\Type\Definition\FieldDefinition;
11
use Fubhy\GraphQL\Type\Definition\Types\EnumType;
12
use Fubhy\GraphQL\Type\Definition\Types\InputObjectType;
13
use Fubhy\GraphQL\Type\Definition\Types\ListModifier;
14
use Fubhy\GraphQL\Type\Definition\Types\NonNullModifier;
15
use Fubhy\GraphQL\Type\Definition\Types\ScalarType;
16
use Fubhy\GraphQL\Utility\TypeInfo;
17
18
class Values
19
{
20
    /**
21
     * Prepares an object map of variables of the correct type based on the
22
     * provided variable definitions and arbitrary input. If the input cannot be
23
     * coerced to match the variable definitions, a Error will be thrown.
24
     *
25
     * @param Schema $schema
26
     * @param array $asts
27
     * @param array $inputs
28
     *
29
     * @return array
30
     *
31
     * @throws \Exception
32
     */
33 297
    public static function getVariableValues(Schema $schema, array $asts, array $inputs)
34
    {
35 297
        $values = [];
36 297
        foreach ($asts as $ast) {
37 69
            $variable = $ast->get('variable')->get('name')->get('value');
38 69
            $values[$variable] = self::getvariableValue($schema, $ast, isset($inputs[$variable]) ? $inputs[$variable] : NULL);
39 279
        }
40 279
        return $values;
41
    }
42
43
    /**
44
     * Prepares an object map of argument values given a list of argument
45
     * definitions and list of argument AST nodes.
46
     *
47
     * @param $arguments
48
     * @param $asts
49
     * @param $variables
50
     *
51
     * @return array|null
52
     */
53 276
    public static function getArgumentValues($arguments, $asts, $variables)
54
    {
55 276
        if (!$arguments || count($arguments) === 0) {
56 210
            return NULL;
57
        }
58
59
        $map = array_reduce($asts, function ($carry, $argument) {
60 141
            $carry[$argument->get('name')->get('value')] = $argument;
61 141
            return $carry;
62 165
        }, []);
63
64 165
        $result = [];
65 165
        foreach ($arguments as $argument) {
66 165
            $name = $argument->getName();
67 165
            $value = isset($map[$name]) ? $map[$name]->get('value') : NULL;
68 165
            $result[$name] = self::coerceValueAST($argument->getType(), $value, $variables);
69 165
        }
70 165
        return $result;
71
    }
72
73
    /**
74
     * @param DirectiveInterface $definition
75
     * @param $directives
76
     * @param $variables
77
     *
78
     * @return array|mixed|null|string
79
     */
80
    public static function getDirectiveValue(DirectiveInterface $definition, $directives, $variables)
81
    {
82
        $ast = NULL;
83
        if ($directives) {
84
            foreach ($directives as $directive) {
85
                if ($directive->get('name')->get('value') === $definition->getName()) {
86
                    $ast = $directive;
87
                    break;
88
                }
89
            }
90
        }
91
        if ($ast) {
92
            if (!$definition->getType()) {
93
                return NULL;
94
            }
95
96
            return self::coerceValueAST($definition->getType(), $ast->get('value'), $variables);
97
        }
98
    }
99
100
    /**
101
     * Given a variable definition, and any value of input, return a value which
102
     * adheres to the variable definition, or throw an error.
103
     *
104
     * @param Schema $schema
105
     * @param VariableDefinition $definition
106
     * @param $input
107
     *
108
     * @return array|mixed|null|string
109
     *
110
     * @throws \Exception
111
     */
112 69
    protected static function getVariableValue(Schema $schema, VariableDefinition $definition, $input)
113
    {
114 69
        $type = TypeInfo::typeFromAST($schema, $definition->get('type'));
115 69
        if (!$type) {
116
            return NULL;
117
        }
118
119 69
        if (self::isValidValue($type, $input)) {
120 51
            if (!isset($input)) {
121 12
                $default = $definition->get('defaultValue');
122
123 12
                if ($default) {
124
                    return self::coerceValueAST($type, $default);
125
                }
126 12
            }
127
128 51
            return self::coerceValue($type, $input);
129
        }
130
131
        // @todo Fix exception message once printer is ported.
132 21
        throw new \Exception(sprintf('Variable $%s expected value of different type.', $definition->get('variable')->get('name')->get('value')));
133
    }
134
135
    /**
136
     * Given a type and any value, return true if that value is valid.
137
     *
138
     * @param TypeInterface $type
139
     * @param $value
140
     *
141
     * @return bool
142
     */
143 69
    protected static function isValidValue(TypeInterface $type, $value)
144
    {
145 69
        if ($type instanceof NonNullModifier) {
146 45
            if (NULL === $value) {
147 21
                return FALSE;
148
            }
149
150 33
            return self::isValidValue($type->getWrappedType(), $value);
151
        }
152
153 57
        if ($value === NULL) {
154 18
            return TRUE;
155
        }
156
157 45
        if ($type instanceof ListModifier) {
158 27
            $itemType = $type->getWrappedType();
159 27
            if (is_array($value)) {
160 21
                foreach ($value as $item) {
161 21
                    if (!self::isValidValue($itemType, $item)) {
162 6
                        return FALSE;
163
                    }
164 21
                }
165
166 15
                return TRUE;
167
            } else {
168 9
                return self::isValidValue($itemType, $value);
169
            }
170
        }
171
172 45
        if ($type instanceof InputObjectType) {
173 3
            $fields = $type->getFields();
174 3
            foreach ($fields as $fieldName => $field) {
175 3
                if (!self::isValidValue($field->getType(), isset($value[$fieldName]) ? $value[$fieldName] : NULL)) {
176 3
                    return FALSE;
177
                }
178 3
            }
179
180 3
            return TRUE;
181
        }
182
183 45
        if ($type instanceof ScalarType || $type instanceof EnumType) {
184 45
            return NULL !== $type->coerce($value);
185
        }
186
187
        return FALSE;
188
    }
189
190
    /**
191
     * Given a type and any value, return a runtime value coerced to match the
192
     * type.
193
     *
194
     * @param TypeInterface $type
195
     * @param $value
196
     *
197
     * @return array|mixed|null|string
198
     */
199 51
    protected static function coerceValue(TypeInterface $type, $value)
200
    {
201 51
        if ($type instanceof NonNullModifier) {
202
            // Note: we're not checking that the result of coerceValue is non-null.
203
            // We only call this function after calling isValidValue.
204 27
            return self::coerceValue($type->getWrappedType(), $value);
205
        }
206
207 51
        if (!isset($value)) {
208 18
            return NULL;
209
        }
210
211 39
        if ($type instanceof ListModifier) {
212 21
            $itemType = $type->getWrappedType();
213 21
            if (is_array($value)) {
214
                return array_map(function ($item) use ($itemType) {
215 15
                    return Values::coerceValue($itemType, $item);
216 15
                }, $value);
217
            } else {
218 9
                return [self::coerceValue($itemType, $value)];
219
            }
220
        }
221
222 39
        if ($type instanceof InputObjectType) {
223 3
            $fields = $type->getFields();
224 3
            $object = [];
225 3
            foreach ($fields as $fieldName => $field) {
226 3
                $fieldValue = self::coerceValue($field->getType(), $value[$fieldName]);
227 3
                $object[$fieldName] = $fieldValue === NULL ? $field->getDefaultValue() : $fieldValue;
228 3
            }
229
230 3
            return $object;
231
        }
232
233 39
        if ($type instanceof ScalarType || $type instanceof EnumType) {
234 39
            $coerced = $type->coerce($value);
235 39
            if (NULL !== $coerced) {
236 39
                return $coerced;
237
            }
238
        }
239
240
        return NULL;
241
    }
242
243
    /**
244
     * Given a type and a value AST node known to match this type, build a
245
     * runtime value.
246
     *
247
     * @param TypeInterface $type
248
     * @param $ast
249
     * @param null $variables
250
     *
251
     * @return array|mixed|null|string
252
     */
253 165
    protected static function coerceValueAST(TypeInterface $type, $ast, $variables = NULL)
254
    {
255 165
        if ($type instanceof NonNullModifier) {
256
            // Note: we're not checking that the result of coerceValueAST is non-null.
257
            // We're assuming that this query has been validated and the value used
258
            // here is of the correct type.
259 99
            return self::coerceValueAST($type->getWrappedType(), $ast, $variables);
260
        }
261
262 165
        if (!$ast) {
263 45
            return NULL;
264
        }
265
266 141
        if ($ast::KIND === Node::KIND_VARIABLE) {
267 54
            $variableName = $ast->get('name')->get('value');
268
269 54
            if (!isset($variables, $variables[$variableName])) {
270 15
                return NULL;
271
            }
272
273
            // Note: we're not doing any checking that this variable is correct. We're
274
            // assuming that this query has been validated and the variable usage here
275
            // is of the correct type.
276 39
            return $variables[$variableName];
277
        }
278
279 87
        if ($type instanceof ListModifier) {
280 3
            $itemType = $type->getWrappedType();
281
282 3
            if ($ast::KIND === Node::KIND_ARRAY_VALUE) {
283 3
                $tmp = [];
284 3
                foreach ($ast->get('values') as $itemAST) {
285 3
                    $tmp[] = self::coerceValueAST($itemType, $itemAST, $variables);
286 3
                }
287
288 3
                return $tmp;
289
            } else {
290 3
                return [self::coerceValueAST($itemType, $ast, $variables)];
291
            }
292
        }
293
294 87
        if ($type instanceof InputObjectType) {
295 3
            $fields = $type->getFields();
296
297 3
            if ($ast::KIND !== Node::KIND_OBJECT_VALUE) {
298
                return NULL;
299
            }
300
301 3
            $asts = array_reduce($ast->get('fields'), function ($carry, $field) {
302 3
                $carry[$field->get('name')->get('value')] = $field;
303 3
                return $carry;
304 3
            }, []);
305
306 3
            $object = [];
307 3
            foreach ($fields as $name => $item) {
308 3
                $field = $asts[$name];
309 3
                $fieldValue = self::coerceValueAST($item->getType(), $field ? $field->get('value') : NULL, $variables);
310 3
                $object[$name] = $fieldValue === NULL ? $item->getDefaultValue() : $fieldValue;
311 3
            }
312
313 3
            return $object;
314
        }
315
316 87
        if ($type instanceof ScalarType || $type instanceof EnumType) {
317 87
            $coerced = $type->coerceLiteral($ast);
318
319 87
            if (isset($coerced)) {
320 84
                return $coerced;
321
            }
322 3
        }
323
324 3
        return NULL;
325
    }
326
}
327