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.

Error   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 348
Duplicated Lines 0 %

Test Coverage

Coverage 91.26%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 47
eloc 116
c 1
b 0
f 0
dl 0
loc 348
ccs 94
cts 103
cp 0.9126
rs 8.64

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getSource() 0 9 4
A formatError() 0 3 1
A isClientSafe() 0 3 1
A getPositions() 0 21 4
A getCategory() 0 3 1
B __construct() 0 37 10
B createLocatedError() 0 36 10
A toSerializableArray() 0 24 4
A getNodes() 0 3 1
A __toString() 0 3 1
A getExtensions() 0 3 1
B getLocations() 0 34 7
A jsonSerialize() 0 3 1
A getPath() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Error 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 Error, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Error;
6
7
use Exception;
8
use GraphQL\Language\AST\Node;
9
use GraphQL\Language\Source;
10
use GraphQL\Language\SourceLocation;
11
use GraphQL\Utils\Utils;
12
use JsonSerializable;
13
use Throwable;
14
use Traversable;
15
use function array_filter;
16
use function array_map;
17
use function array_values;
18
use function is_array;
19
use function iterator_to_array;
20
21
/**
22
 * Describes an Error found during the parse, validate, or
23
 * execute phases of performing a GraphQL operation. In addition to a message
24
 * and stack trace, it also includes information about the locations in a
25
 * GraphQL document and/or execution result that correspond to the Error.
26
 *
27
 * When the error was caused by an exception thrown in resolver, original exception
28
 * is available via `getPrevious()`.
29
 *
30
 * Also read related docs on [error handling](error-handling.md)
31
 *
32
 * Class extends standard PHP `\Exception`, so all standard methods of base `\Exception` class
33
 * are available in addition to those listed below.
34
 */
35
class Error extends Exception implements JsonSerializable, ClientAware
36
{
37
    const CATEGORY_GRAPHQL  = 'graphql';
38
    const CATEGORY_INTERNAL = 'internal';
39
40
    /**
41
     * A message describing the Error for debugging purposes.
42
     *
43
     * @var string
44
     */
45
    public $message;
46
47
    /** @var SourceLocation[] */
48
    private $locations;
49
50
    /**
51
     * An array describing the JSON-path into the execution response which
52
     * corresponds to this error. Only included for errors during execution.
53
     *
54
     * @var mixed[]|null
55
     */
56
    public $path;
57
58
    /**
59
     * An array of GraphQL AST Nodes corresponding to this error.
60
     *
61
     * @var Node[]|null
62
     */
63
    public $nodes;
64
65
    /**
66
     * The source GraphQL document for the first location of this error.
67
     *
68
     * Note that if this Error represents more than one node, the source may not
69
     * represent nodes after the first node.
70
     *
71
     * @var Source|null
72
     */
73
    private $source;
74
75
    /** @var int[]|null */
76
    private $positions;
77
78
    /** @var bool */
79
    private $isClientSafe;
80
81
    /** @var string */
82
    protected $category;
83
84
    /** @var mixed[]|null */
85
    protected $extensions;
86
87
    /**
88
     * @param string                       $message
89
     * @param Node|Node[]|Traversable|null $nodes
90
     * @param mixed[]|null                 $positions
91
     * @param mixed[]|null                 $path
92
     * @param Throwable                    $previous
93
     * @param mixed[]                      $extensions
94
     */
95 553
    public function __construct(
96
        $message,
97
        $nodes = null,
98
        ?Source $source = null,
99
        $positions = null,
100
        $path = null,
101
        $previous = null,
102
        array $extensions = []
103
    ) {
104 553
        parent::__construct($message, 0, $previous);
105
106
        // Compute list of blame nodes.
107 553
        if ($nodes instanceof Traversable) {
108 49
            $nodes = iterator_to_array($nodes);
109 553
        } elseif ($nodes && ! is_array($nodes)) {
110 70
            $nodes = [$nodes];
111
        }
112
113 553
        $this->nodes      = $nodes;
0 ignored issues
show
Documentation Bug introduced by
It seems like $nodes can also be of type GraphQL\Language\AST\Node. However, the property $nodes is declared as type GraphQL\Language\AST\Node[]|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
114 553
        $this->source     = $source;
115 553
        $this->positions  = $positions;
116 553
        $this->path       = $path;
117 553
        $this->extensions = $extensions ?: (
118 552
        $previous && $previous instanceof self
119 41
            ? $previous->extensions
120 552
            : []
121
        );
122
123 553
        if ($previous instanceof ClientAware) {
124 71
            $this->isClientSafe = $previous->isClientSafe();
125 71
            $this->category     = $previous->getCategory() ?: self::CATEGORY_INTERNAL;
126 530
        } elseif ($previous) {
127 28
            $this->isClientSafe = false;
128 28
            $this->category     = self::CATEGORY_INTERNAL;
129
        } else {
130 506
            $this->isClientSafe = true;
131 506
            $this->category     = self::CATEGORY_GRAPHQL;
132
        }
133 553
    }
134
135
    /**
136
     * Given an arbitrary Error, presumably thrown while attempting to execute a
137
     * GraphQL operation, produce a new GraphQLError aware of the location in the
138
     * document responsible for the original Error.
139
     *
140
     * @param mixed        $error
141
     * @param Node[]|null  $nodes
142
     * @param mixed[]|null $path
143
     *
144
     * @return Error
145
     */
146 61
    public static function createLocatedError($error, $nodes = null, $path = null)
147
    {
148 61
        if ($error instanceof self) {
149 28
            if ($error->path && $error->nodes) {
150 18
                return $error;
151
            }
152
153 10
            $nodes = $nodes ?: $error->nodes;
154 10
            $path  = $path ?: $error->path;
155
        }
156
157 61
        $source     = $positions = $originalError = null;
158 61
        $extensions = [];
159
160 61
        if ($error instanceof self) {
161 10
            $message       = $error->getMessage();
162 10
            $originalError = $error;
163 10
            $nodes         = $error->nodes ?: $nodes;
164 10
            $source        = $error->source;
165 10
            $positions     = $error->positions;
166 10
            $extensions    = $error->extensions;
167 53
        } elseif ($error instanceof Throwable) {
168 52
            $message       = $error->getMessage();
169 52
            $originalError = $error;
170
        } else {
171 1
            $message = (string) $error;
172
        }
173
174 61
        return new static(
175 61
            $message ?: 'An unknown error occurred.',
176 61
            $nodes,
177 61
            $source,
178 61
            $positions,
179 61
            $path,
180 61
            $originalError,
181 61
            $extensions
0 ignored issues
show
Bug introduced by
It seems like $extensions can also be of type null; however, parameter $extensions of GraphQL\Error\Error::__construct() 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

181
            /** @scrutinizer ignore-type */ $extensions
Loading history...
182
        );
183
    }
184
185
    /**
186
     * @return mixed[]
187
     */
188 202
    public static function formatError(Error $error)
189
    {
190 202
        return $error->toSerializableArray();
0 ignored issues
show
Deprecated Code introduced by
The function GraphQL\Error\Error::toSerializableArray() has been deprecated: Use FormattedError::createFromException() instead ( Ignorable by Annotation )

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

190
        return /** @scrutinizer ignore-deprecated */ $error->toSerializableArray();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
191
    }
192
193
    /**
194
     * @inheritdoc
195
     */
196 104
    public function isClientSafe()
197
    {
198 104
        return $this->isClientSafe;
199
    }
200
201
    /**
202
     * @inheritdoc
203
     */
204 103
    public function getCategory()
205
    {
206 103
        return $this->category;
207
    }
208
209
    /**
210
     * @return Source|null
211
     */
212 419
    public function getSource()
213
    {
214 419
        if ($this->source === null) {
215 352
            if (! empty($this->nodes[0]) && ! empty($this->nodes[0]->loc)) {
216 297
                $this->source = $this->nodes[0]->loc->source;
217
            }
218
        }
219
220 419
        return $this->source;
221
    }
222
223
    /**
224
     * @return int[]
225
     */
226 418
    public function getPositions()
227
    {
228 418
        if ($this->positions === null && ! empty($this->nodes)) {
229 305
            $positions = array_map(
230
                static function ($node) : ?int {
231 305
                    return isset($node->loc) ? $node->loc->start : null;
232 305
                },
233 305
                $this->nodes
234
            );
235
236 305
            $positions = array_filter(
237 305
                $positions,
238
                static function ($p) : bool {
239 305
                    return $p !== null;
240 305
                }
241
            );
242
243 305
            $this->positions = array_values($positions);
244
        }
245
246 418
        return $this->positions;
247
    }
248
249
    /**
250
     * An array of locations within the source GraphQL document which correspond to this error.
251
     *
252
     * Each entry has information about `line` and `column` within source GraphQL document:
253
     * $location->line;
254
     * $location->column;
255
     *
256
     * Errors during validation often contain multiple locations, for example to
257
     * point out to field mentioned in multiple fragments. Errors during execution include a
258
     * single location, the field which produced the error.
259
     *
260
     * @return SourceLocation[]
261
     *
262
     * @api
263
     */
264 408
    public function getLocations()
265
    {
266 408
        if ($this->locations === null) {
267 408
            $positions = $this->getPositions();
268 408
            $source    = $this->getSource();
269 408
            $nodes     = $this->nodes;
270
271 408
            if ($positions && $source) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $positions of type integer[] 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...
272 362
                $this->locations = array_map(
273
                    static function ($pos) use ($source) : SourceLocation {
274 362
                        return $source->getLocation($pos);
275 362
                    },
276 362
                    $positions
277
                );
278 49
            } elseif ($nodes) {
279
                $locations       = array_filter(
280
                    array_map(
281
                        static function ($node) {
282
                            if ($node->loc && $node->loc->source) {
283
                                return $node->loc->source->getLocation($node->loc->start);
284
                            }
285
286
                            return null;
287
                        },
288
                        $nodes
289
                    )
290
                );
291 49
                $this->locations = array_values($locations);
292
            } else {
293
                $this->locations = [];
294
            }
295 408
        }
296
297
        return $this->locations;
298
    }
299
300
    /**
301 14
     * @return Node[]|null
302
     */
303 14
    public function getNodes()
304
    {
305
        return $this->nodes;
306
    }
307
308
    /**
309
     * Returns an array describing the path from the root value to the field which produced this error.
310
     * Only included for execution errors.
311
     *
312
     * @return mixed[]|null
313
     *
314 15
     * @api
315
     */
316 15
    public function getPath()
317
    {
318
        return $this->path;
319
    }
320
321
    /**
322 85
     * @return mixed[]
323
     */
324 85
    public function getExtensions()
325
    {
326
        return $this->extensions;
327
    }
328
329
    /**
330
     * Returns array representation of error suitable for serialization
331
     *
332
     * @deprecated Use FormattedError::createFromException() instead
333
     *
334
     * @return mixed[]
335
     *
336
     * @codeCoverageIgnore
337
     */
338
    public function toSerializableArray()
339
    {
340
        $arr = [
341
            'message' => $this->getMessage(),
342
        ];
343
344
        $locations = Utils::map(
345
            $this->getLocations(),
346
            static function (SourceLocation $loc) : array {
347
                return $loc->toSerializableArray();
348
            }
349
        );
350
351
        if (! empty($locations)) {
352
            $arr['locations'] = $locations;
353
        }
354
        if (! empty($this->path)) {
355
            $arr['path'] = $this->path;
356
        }
357
        if (! empty($this->extensions)) {
358
            $arr['extensions'] = $this->extensions;
359
        }
360
361
        return $arr;
362
    }
363
364
    /**
365
     * Specify data which should be serialized to JSON
366
     *
367
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
368
     *
369
     * @return mixed data which can be serialized by <b>json_encode</b>,
370
     * which is a value of any type other than a resource.
371
     */
372
    public function jsonSerialize()
373
    {
374
        return $this->toSerializableArray();
0 ignored issues
show
Deprecated Code introduced by
The function GraphQL\Error\Error::toSerializableArray() has been deprecated: Use FormattedError::createFromException() instead ( Ignorable by Annotation )

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

374
        return /** @scrutinizer ignore-deprecated */ $this->toSerializableArray();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
375
    }
376
377
    /**
378 10
     * @return string
379
     */
380 10
    public function __toString()
381
    {
382
        return FormattedError::printError($this);
383
    }
384
}
385