GraphQLException   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 314
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 79
dl 0
loc 314
rs 9.2
c 4
b 0
f 0
wmc 40

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getOriginalException() 0 3 1
A setExtensions() 0 4 1
A getOriginalErrorMessage() 0 3 2
A getLocations() 0 3 1
A getExtensions() 0 3 1
A __construct() 0 19 1
A getPath() 0 3 1
A toArray() 0 17 3
B resolvePositions() 0 21 7
A hasSource() 0 3 1
A getNodes() 0 3 1
A getLocationsAsArray() 0 5 2
A resolveSource() 0 11 5
A resolveLocations() 0 23 6
A resolveNodes() 0 13 3
A getSource() 0 3 1
A getPositions() 0 3 1
A __toString() 0 3 1
A hasLocations() 0 3 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace Digia\GraphQL\Error;
4
5
use Digia\GraphQL\Language\Node\NodeInterface;
6
use Digia\GraphQL\Language\Source;
7
use Digia\GraphQL\Language\SourceLocation;
8
use Digia\GraphQL\Util\ArrayToJsonTrait;
9
use Digia\GraphQL\Util\SerializationInterface;
10
11
/**
12
 * An GraphQLException describes an exception thrown during the execute
13
 * phase of performing a GraphQL operation. In addition to a message
14
 * and stack trace, it also includes information about the locations in a
15
 * GraphQL document and/or execution result that correspond to the Error.
16
 */
17
class GraphQLException extends AbstractException implements SerializationInterface
18
{
19
    use ArrayToJsonTrait;
20
21
    /**
22
     * An array of { line, column } locations within the source GraphQL document
23
     * which correspond to this error.
24
     *
25
     * Errors during validation often contain multiple locations, for example to
26
     * point out two things with the same name. Errors during execution include a
27
     * single location, the field which produced the error.
28
     *
29
     * @var array|null
30
     */
31
    protected $locations;
32
33
    /**
34
     * An array describing the JSON-path into the execution response which
35
     * corresponds to this error. Only included for errors during execution.
36
     *
37
     * @var string[]|null
38
     */
39
    protected $path;
40
41
    /**
42
     * An array of GraphQL AST Nodes corresponding to this error.
43
     *
44
     * @var NodeInterface[]|null
45
     */
46
    protected $nodes;
47
48
    /**
49
     * The source GraphQL document for the first location of this error.
50
     *
51
     * Note that if this Error represents more than one node, the source may not
52
     * represent nodes after the first node.
53
     *
54
     * @var Source|null
55
     */
56
    protected $source;
57
58
    /**
59
     * An array of character offsets within the source GraphQL document
60
     * which correspond to this error.
61
     *
62
     * @var int[]|null
63
     */
64
    protected $positions;
65
66
    /**
67
     * Extension fields to add to the formatted error.
68
     *
69
     * @var array|null
70
     */
71
    protected $extensions;
72
73
    /**
74
     * @var null|\Throwable
75
     */
76
    protected $originalException;
77
78
    /**
79
     * ExecutionException constructor.
80
     *
81
     * @param string          $message
82
     * @param array|null      $nodes
83
     * @param Source|null     $source
84
     * @param array|null      $positions
85
     * @param array|null      $path
86
     * @param array|null      $extensions
87
     * @param \Throwable|null $originalException
88
     */
89
    public function __construct(
90
        string $message,
91
        ?array $nodes = null,
92
        ?Source $source = null,
93
        ?array $positions = null,
94
        ?array $path = null,
95
        ?array $extensions = null,
96
        ?\Throwable $originalException = null
97
    ) {
98
        parent::__construct($message);
99
100
        $this->resolveNodes($nodes);
101
        $this->resolveSource($source);
102
        $this->resolvePositions($positions);
103
        $this->resolveLocations($positions, $source);
104
105
        $this->path              = $path;
106
        $this->extensions        = $extensions;
107
        $this->originalException = $originalException;
108
    }
109
110
    /**
111
     * @return NodeInterface[]
112
     */
113
    public function getNodes(): ?array
114
    {
115
        return $this->nodes;
116
    }
117
118
    /**
119
     * @return bool
120
     */
121
    public function hasSource(): bool
122
    {
123
        return null !== $this->source;
124
    }
125
126
    /**
127
     * @return Source|null
128
     */
129
    public function getSource(): ?Source
130
    {
131
        return $this->source;
132
    }
133
134
    /**
135
     * @return int[]|null
136
     */
137
    public function getPositions(): ?array
138
    {
139
        return $this->positions;
140
    }
141
142
    /**
143
     * @return bool
144
     */
145
    public function hasLocations(): bool
146
    {
147
        return !empty($this->locations);
148
    }
149
150
    /**
151
     * @return array|null
152
     */
153
    public function getLocations(): ?array
154
    {
155
        return $this->locations;
156
    }
157
158
    /**
159
     * @return array|null
160
     */
161
    public function getLocationsAsArray(): ?array
162
    {
163
        return !empty($this->locations) ? \array_map(function (SourceLocation $location) {
164
            return $location->toArray();
165
        }, $this->locations) : null;
166
    }
167
168
    /**
169
     * @return array|null
170
     */
171
    public function getPath(): ?array
172
    {
173
        return $this->path;
174
    }
175
176
    /**
177
     * @return array|null
178
     */
179
    public function getExtensions(): ?array
180
    {
181
        return $this->extensions;
182
    }
183
184
    /**
185
     * @param array|null $extensions
186
     * @return self
187
     */
188
    public function setExtensions(?array $extensions): self
189
    {
190
        $this->extensions = $extensions;
191
        return $this;
192
    }
193
194
    /**
195
     * @return \Throwable|null
196
     */
197
    public function getOriginalException(): ?\Throwable
198
    {
199
        return $this->originalException;
200
    }
201
202
    /**
203
     * @return null|string
204
     */
205
    public function getOriginalErrorMessage(): ?string
206
    {
207
        return null !== $this->originalException ? $this->originalException->getMessage() : null;
208
    }
209
210
    /**
211
     * @inheritdoc
212
     */
213
    public function toArray(): array
214
    {
215
        $result = [
216
            'message'   => $this->message,
217
            // TODO: Do not include `locations` if `null` (similar to `path` and `extensions`).
218
            'locations' => $this->getLocationsAsArray(),
219
        ];
220
221
        if (null !== $this->path) {
222
            $result['path'] = $this->path;
223
        }
224
225
        if (null !== $this->extensions) {
226
            $result['extensions'] = $this->extensions;
227
        }
228
229
        return $result;
230
    }
231
232
    /**
233
     * @inheritdoc
234
     */
235
    public function __toString(): string
236
    {
237
        return printError($this);
238
    }
239
240
    /**
241
     * @param array|null $nodes
242
     * @return $this
243
     */
244
    protected function resolveNodes(?array $nodes): self
245
    {
246
        if (\is_array($nodes)) {
0 ignored issues
show
introduced by
The condition is_array($nodes) is always true.
Loading history...
247
            $nodes = !empty($nodes) ? $nodes : [];
248
        } else {
249
            $nodes = [$nodes];
250
        }
251
252
        $this->nodes = \array_filter($nodes, function ($node) {
253
            return null !== $node;
254
        });
255
256
        return $this;
257
    }
258
259
    /**
260
     * @param Source|null $source
261
     * @return $this
262
     */
263
    protected function resolveSource(?Source $source): self
264
    {
265
        if (null === $source && !empty($this->nodes)) {
266
            $firstNode = $this->nodes[0] ?? null;
267
            $location  = null !== $firstNode ? $firstNode->getLocation() : null;
268
            $source    = null !== $location ? $location->getSource() : null;
269
        }
270
271
        $this->source = $source;
272
273
        return $this;
274
    }
275
276
    /**
277
     * @param array|null $positions
278
     * @return $this
279
     */
280
    protected function resolvePositions(?array $positions): self
281
    {
282
        if (null === $positions && !empty($this->nodes)) {
0 ignored issues
show
introduced by
The condition null === $positions is always false.
Loading history...
283
            $positions = \array_reduce($this->nodes, function (array $list, ?NodeInterface $node) {
284
                if (null !== $node) {
285
                    $location = $node->getLocation();
286
                    if (null !== $location) {
287
                        $list[] = $location->getStart();
288
                    }
289
                }
290
                return $list;
291
            }, []);
292
        }
293
294
        if (null !== $positions && empty($positions)) {
295
            $positions = null;
296
        }
297
298
        $this->positions = $positions;
299
300
        return $this;
301
    }
302
303
    /**
304
     * @param array|null  $positions
305
     * @param Source|null $source
306
     * @return $this
307
     */
308
    protected function resolveLocations(?array $positions, ?Source $source): self
309
    {
310
        $locations = null;
311
312
        if (null !== $positions && null !== $source) {
313
            $locations = \array_map(function ($position) use ($source) {
314
                return SourceLocation::fromSource($source, $position);
315
            }, $positions);
316
        } elseif (!empty($this->nodes)) {
317
            $locations = \array_reduce($this->nodes, function (array $list, NodeInterface $node) {
318
                $location = $node->getLocation();
319
                if (null !== $location) {
320
                    $list[] = SourceLocation::fromSource($location->getSource(), $location->getStart());
0 ignored issues
show
Bug introduced by
It seems like $location->getSource() can also be of type null; however, parameter $source of Digia\GraphQL\Language\S...eLocation::fromSource() does only seem to accept Digia\GraphQL\Language\Source, 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

320
                    $list[] = SourceLocation::fromSource(/** @scrutinizer ignore-type */ $location->getSource(), $location->getStart());
Loading history...
321
                }
322
                return $list;
323
            }, []);
324
        }
325
326
        if ($locations !== null) {
327
            $this->locations = $locations;
328
        }
329
330
        return $this;
331
    }
332
}
333