Passed
Pull Request — master (#190)
by Sebastian
03:29
created

GraphQLException::resolvePositions()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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