GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Value::coerceValue()   F
last analyzed

Complexity

Conditions 31
Paths 116

Size

Total Lines 178
Code Lines 113

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 109
CRAP Score 31.0056

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 113
dl 0
loc 178
ccs 109
cts 111
cp 0.982
rs 3.2266
c 2
b 0
f 0
cc 31
nc 116
nop 4
crap 31.0056

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Utils;
6
7
use Exception;
8
use GraphQL\Error\Error;
9
use GraphQL\Language\AST\Node;
10
use GraphQL\Type\Definition\EnumType;
11
use GraphQL\Type\Definition\InputObjectType;
12
use GraphQL\Type\Definition\InputType;
13
use GraphQL\Type\Definition\ListOfType;
14
use GraphQL\Type\Definition\NonNull;
15
use GraphQL\Type\Definition\ScalarType;
16
use stdClass;
17
use Throwable;
18
use Traversable;
19
use function array_key_exists;
20
use function array_keys;
21
use function array_map;
22
use function array_merge;
23
use function is_array;
24
use function is_object;
25
use function is_string;
26
use function sprintf;
27
28
/**
29
 * Coerces a PHP value given a GraphQL Type.
30
 *
31
 * Returns either a value which is valid for the provided type or a list of
32
 * encountered coercion errors.
33
 */
34
class Value
35
{
36
    /**
37
     * Given a type and any value, return a runtime value coerced to match the type.
38
     *
39
     * @param ScalarType|EnumType|InputObjectType|ListOfType|NonNull $type
40
     * @param mixed[]                                                $path
41
     */
42 81
    public static function coerceValue($value, InputType $type, $blameNode = null, ?array $path = null)
43
    {
44 81
        if ($type instanceof NonNull) {
45 33
            if ($value === null) {
46 3
                return self::ofErrors([
47 3
                    self::coercionError(
48 3
                        sprintf('Expected non-nullable type %s not to be null', $type),
49 3
                        $blameNode,
50 3
                        $path
51
                    ),
52
                ]);
53
            }
54
55 33
            return self::coerceValue($value, $type->getWrappedType(), $blameNode, $path);
0 ignored issues
show
Bug introduced by
$type->getWrappedType() 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

55
            return self::coerceValue($value, /** @scrutinizer ignore-type */ $type->getWrappedType(), $blameNode, $path);
Loading history...
56
        }
57
58 81
        if ($value === null) {
59
            // Explicitly return the value null.
60 4
            return self::ofValue(null);
61
        }
62
63 79
        if ($type instanceof ScalarType) {
64
            // Scalars determine if a value is valid via parseValue(), which can
65
            // throw to indicate failure. If it throws, maintain a reference to
66
            // the original error.
67
            try {
68 70
                return self::ofValue($type->parseValue($value));
69 27
            } catch (Throwable $error) {
70 27
                return self::ofErrors([
71 27
                    self::coercionError(
72 27
                        sprintf('Expected type %s', $type->name),
73 27
                        $blameNode,
74 27
                        $path,
75 27
                        $error->getMessage(),
76 27
                        $error
77
                    ),
78
                ]);
79
            }
80
        }
81
82 27
        if ($type instanceof EnumType) {
83 7
            if (is_string($value)) {
84 5
                $enumValue = $type->getValue($value);
85 5
                if ($enumValue) {
86 4
                    return self::ofValue($enumValue->value);
87
                }
88
            }
89
90 3
            $suggestions = Utils::suggestionList(
91 3
                Utils::printSafe($value),
92 3
                array_map(
93
                    static function ($enumValue) {
94 3
                        return $enumValue->name;
95 3
                    },
96 3
                    $type->getValues()
97
                )
98
            );
99
100 3
            $didYouMean = $suggestions
101 1
                ? 'did you mean ' . Utils::orList($suggestions) . '?'
102 3
                : null;
103
104 3
            return self::ofErrors([
105 3
                self::coercionError(
106 3
                    sprintf('Expected type %s', $type->name),
107 3
                    $blameNode,
108 3
                    $path,
109 3
                    $didYouMean
110
                ),
111
            ]);
112
        }
113
114 20
        if ($type instanceof ListOfType) {
115 11
            $itemType = $type->getWrappedType();
116 11
            if (is_array($value) || $value instanceof Traversable) {
117 10
                $errors       = [];
118 10
                $coercedValue = [];
119 10
                foreach ($value as $index => $itemValue) {
120 10
                    $coercedItem = self::coerceValue(
121 10
                        $itemValue,
122 10
                        $itemType,
123 10
                        $blameNode,
124 10
                        self::atPath($path, $index)
125
                    );
126 10
                    if ($coercedItem['errors']) {
127 2
                        $errors = self::add($errors, $coercedItem['errors']);
128
                    } else {
129 10
                        $coercedValue[] = $coercedItem['value'];
130
                    }
131
                }
132
133 10
                return $errors ? self::ofErrors($errors) : self::ofValue($coercedValue);
134
            }
135
            // Lists accept a non-list value as a list of one.
136 2
            $coercedItem = self::coerceValue($value, $itemType, $blameNode);
137
138 2
            return $coercedItem['errors'] ? $coercedItem : self::ofValue([$coercedItem['value']]);
139
        }
140
141 12
        if ($type instanceof InputObjectType) {
142 12
            if (! is_object($value) && ! is_array($value) && ! $value instanceof Traversable) {
143 2
                return self::ofErrors([
144 2
                    self::coercionError(
145 2
                        sprintf('Expected type %s to be an object', $type->name),
146 2
                        $blameNode,
147 2
                        $path
148
                    ),
149
                ]);
150
            }
151
152
            // Cast \stdClass to associative array before checking the fields. Note that the coerced value will be an array.
153 11
            if ($value instanceof stdClass) {
154 2
                $value = (array) $value;
155
            }
156
157 11
            $errors       = [];
158 11
            $coercedValue = [];
159 11
            $fields       = $type->getFields();
160 11
            foreach ($fields as $fieldName => $field) {
161 11
                if (array_key_exists($fieldName, $value)) {
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type Traversable and object; however, parameter $search of array_key_exists() does only seem to accept array, 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

161
                if (array_key_exists($fieldName, /** @scrutinizer ignore-type */ $value)) {
Loading history...
162 10
                    $fieldValue   = $value[$fieldName];
163 10
                    $coercedField = self::coerceValue(
164 10
                        $fieldValue,
165 10
                        $field->getType(),
166 10
                        $blameNode,
167 10
                        self::atPath($path, $fieldName)
168
                    );
169 10
                    if ($coercedField['errors']) {
170 3
                        $errors = self::add($errors, $coercedField['errors']);
171
                    } else {
172 10
                        $coercedValue[$fieldName] = $coercedField['value'];
173
                    }
174 10
                } elseif ($field->defaultValueExists()) {
175
                    $coercedValue[$fieldName] = $field->defaultValue;
176 10
                } elseif ($field->getType() instanceof NonNull) {
177 2
                    $fieldPath = self::printPath(self::atPath($path, $fieldName));
178 2
                    $errors    = self::add(
179 2
                        $errors,
180 2
                        self::coercionError(
181 2
                            sprintf(
182 2
                                'Field %s of required type %s was not provided',
183 2
                                $fieldPath,
184 2
                                $field->type->toString()
185
                            ),
186 11
                            $blameNode
187
                        )
188
                    );
189
                }
190
            }
191
192
            // Ensure every provided field is defined.
193 11
            foreach ($value as $fieldName => $field) {
194 11
                if (array_key_exists($fieldName, $fields)) {
195 10
                    continue;
196
                }
197
198 4
                $suggestions = Utils::suggestionList(
199 4
                    (string) $fieldName,
200 4
                    array_keys($fields)
201
                );
202 4
                $didYouMean  = $suggestions
203 1
                    ? 'did you mean ' . Utils::orList($suggestions) . '?'
204 4
                    : null;
205 4
                $errors      = self::add(
206 4
                    $errors,
207 4
                    self::coercionError(
208 4
                        sprintf('Field "%s" is not defined by type %s', $fieldName, $type->name),
209 4
                        $blameNode,
210 4
                        $path,
211 4
                        $didYouMean
212
                    )
213
                );
214
            }
215
216 11
            return $errors ? self::ofErrors($errors) : self::ofValue($coercedValue);
217
        }
218
219
        throw new Error(sprintf('Unexpected type %s', $type->name));
0 ignored issues
show
Bug introduced by
Accessing name on the interface GraphQL\Type\Definition\InputType suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
220
    }
221
222 38
    private static function ofErrors($errors)
223
    {
224 38
        return ['errors' => $errors, 'value' => Utils::undefined()];
225
    }
226
227
    /**
228
     * @param string                   $message
229
     * @param Node                     $blameNode
230
     * @param mixed[]|null             $path
231
     * @param string                   $subMessage
232
     * @param Exception|Throwable|null $originalError
233
     *
234
     * @return Error
235
     */
236 38
    private static function coercionError(
237
        $message,
238
        $blameNode,
239
        ?array $path = null,
240
        $subMessage = null,
241
        $originalError = null
242
    ) {
243 38
        $pathStr = self::printPath($path);
244
245
        // Return a GraphQLError instance
246 38
        return new Error(
247
            $message .
248 38
            ($pathStr ? ' at ' . $pathStr : '') .
249 38
            ($subMessage ? '; ' . $subMessage : '.'),
250 38
            $blameNode,
251 38
            null,
252 38
            null,
253 38
            null,
254 38
            $originalError
255
        );
256
    }
257
258
    /**
259
     * Build a string describing the path into the value where the error was found
260
     *
261
     * @param mixed[]|null $path
262
     *
263
     * @return string
264
     */
265 38
    private static function printPath(?array $path = null)
266
    {
267 38
        $pathStr     = '';
268 38
        $currentPath = $path;
269 38
        while ($currentPath) {
270
            $pathStr     =
271 6
                (is_string($currentPath['key'])
272 4
                    ? '.' . $currentPath['key']
273 6
                    : '[' . $currentPath['key'] . ']') . $pathStr;
274 6
            $currentPath = $currentPath['prev'];
275
        }
276
277 38
        return $pathStr ? 'value' . $pathStr : '';
278
    }
279
280
    /**
281
     * @param mixed $value
282
     *
283
     * @return (mixed|null)[]
284
     */
285 49
    private static function ofValue($value)
286
    {
287 49
        return ['errors' => null, 'value' => $value];
288
    }
289
290
    /**
291
     * @param mixed|null $prev
292
     * @param mixed|null $key
293
     *
294
     * @return (mixed|null)[]
295
     */
296 18
    private static function atPath($prev, $key)
297
    {
298 18
        return ['prev' => $prev, 'key' => $key];
299
    }
300
301
    /**
302
     * @param Error[]       $errors
303
     * @param Error|Error[] $moreErrors
304
     *
305
     * @return Error[]
306
     */
307 9
    private static function add($errors, $moreErrors)
308
    {
309 9
        return array_merge($errors, is_array($moreErrors) ? $moreErrors : [$moreErrors]);
310
    }
311
}
312