1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace alsvanzelf\jsonapi\objects; |
4
|
|
|
|
5
|
|
|
use alsvanzelf\jsonapi\Document; |
6
|
|
|
use alsvanzelf\jsonapi\exceptions\InputException; |
7
|
|
|
use alsvanzelf\jsonapi\helpers\AtMemberManager; |
8
|
|
|
use alsvanzelf\jsonapi\helpers\Converter; |
9
|
|
|
use alsvanzelf\jsonapi\helpers\ExtensionMemberManager; |
10
|
|
|
use alsvanzelf\jsonapi\helpers\HttpStatusCodeManager; |
11
|
|
|
use alsvanzelf\jsonapi\helpers\LinksManager; |
12
|
|
|
use alsvanzelf\jsonapi\helpers\Validator; |
13
|
|
|
use alsvanzelf\jsonapi\interfaces\ObjectInterface; |
14
|
|
|
|
15
|
|
|
class ErrorObject implements ObjectInterface { |
16
|
|
|
use AtMemberManager, ExtensionMemberManager, HttpStatusCodeManager, LinksManager; |
17
|
|
|
|
18
|
|
|
/** @var string */ |
19
|
|
|
protected $id; |
20
|
|
|
/** @var string */ |
21
|
|
|
protected $code; |
22
|
|
|
/** @var string */ |
23
|
|
|
protected $title; |
24
|
|
|
/** @var string */ |
25
|
|
|
protected $detail; |
26
|
|
|
/** @var array */ |
27
|
|
|
protected $source = []; |
28
|
|
|
/** @var MetaObject */ |
29
|
|
|
protected $meta; |
30
|
|
|
/** @var array */ |
31
|
|
|
protected static $defaults = [ |
32
|
|
|
/** |
33
|
|
|
* add the trace of exceptions when adding exceptions |
34
|
|
|
* in some cases it might be handy to disable if traces are too big |
35
|
|
|
*/ |
36
|
|
|
'includeExceptionTrace' => true, |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* strip a base path from exception file and trace paths |
40
|
|
|
* set this to the applications root to have more readable exception responses |
41
|
|
|
*/ |
42
|
|
|
'stripExceptionBasePath' => null, |
43
|
|
|
]; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @param string|int $genericCode developer-friendly code of the generic type of error |
47
|
|
|
* @param string $genericTitle human-friendly title of the generic type of error |
48
|
|
|
* @param string $specificDetails optional, human-friendly explanation of the specific error |
49
|
|
|
* @param string $specificAboutLink optional, human-friendly explanation of the specific error |
50
|
|
|
* @param string $genericTypeLink optional, human-friendly explanation of the generic type of error |
51
|
|
|
*/ |
52
|
21 |
|
public function __construct($genericCode=null, $genericTitle=null, $specificDetails=null, $specificAboutLink=null, $genericTypeLink=null) { |
53
|
21 |
|
if ($genericCode !== null) { |
54
|
7 |
|
$this->setApplicationCode($genericCode); |
55
|
|
|
} |
56
|
21 |
|
if ($genericTitle !== null) { |
57
|
2 |
|
$this->setHumanExplanation($genericTitle, $specificDetails, $specificAboutLink, $genericTypeLink); |
58
|
|
|
} |
59
|
21 |
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* human api |
63
|
|
|
*/ |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* @param \Exception|\Throwable $exception |
67
|
|
|
* @param array $options optional {@see ErrorObject::$defaults} |
68
|
|
|
* @return ErrorObject |
69
|
|
|
* |
70
|
|
|
* @throws InputException if $exception is not \Exception or \Throwable |
71
|
|
|
*/ |
72
|
12 |
|
public static function fromException($exception, array $options=[]) { |
73
|
12 |
|
if ($exception instanceof \Exception === false && $exception instanceof \Throwable === false) { |
74
|
1 |
|
throw new InputException('input is not a real exception in php5 or php7'); |
75
|
|
|
} |
76
|
|
|
|
77
|
11 |
|
$options = array_merge(self::$defaults, $options); |
78
|
|
|
|
79
|
11 |
|
$errorObject = new self(); |
80
|
|
|
|
81
|
11 |
|
$className = get_class($exception); |
82
|
11 |
|
if (strpos($className, '\\')) { |
83
|
2 |
|
$exploded = explode('\\', $className); |
84
|
2 |
|
$className = end($exploded); |
85
|
|
|
} |
86
|
11 |
|
$errorObject->setApplicationCode(Converter::camelCaseToWords($className)); |
87
|
|
|
|
88
|
11 |
|
$filePath = $exception->getFile(); |
89
|
11 |
|
if ($options['stripExceptionBasePath'] !== null) { |
90
|
3 |
|
$filePath = str_replace($options['stripExceptionBasePath'], '', $filePath); |
91
|
|
|
} |
92
|
|
|
|
93
|
11 |
|
$metaObject = MetaObject::fromArray([ |
94
|
11 |
|
'type' => get_class($exception), |
95
|
11 |
|
'message' => $exception->getMessage(), |
96
|
11 |
|
'code' => $exception->getCode(), |
97
|
11 |
|
'file' => $filePath, |
98
|
11 |
|
'line' => $exception->getLine(), |
99
|
|
|
]); |
100
|
|
|
|
101
|
11 |
|
if ($options['includeExceptionTrace']) { |
102
|
8 |
|
$trace = $exception->getTrace(); |
103
|
8 |
|
if ($options['stripExceptionBasePath'] !== null) { |
104
|
1 |
|
foreach ($trace as &$traceElement) { |
105
|
1 |
|
if (isset($traceElement['file'])) { |
106
|
1 |
|
$traceElement['file'] = str_replace($options['stripExceptionBasePath'], '', $traceElement['file']); |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
|
111
|
8 |
|
$metaObject->add('trace', $trace); |
112
|
|
|
} |
113
|
|
|
|
114
|
11 |
|
$errorObject->setMetaObject($metaObject); |
115
|
|
|
|
116
|
11 |
|
if (Validator::checkHttpStatusCode($exception->getCode())) { |
117
|
3 |
|
$errorObject->setHttpStatusCode($exception->getCode()); |
118
|
|
|
} |
119
|
|
|
|
120
|
11 |
|
return $errorObject; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* explain this particular occurence of the error in a human-friendly way |
125
|
|
|
* |
126
|
|
|
* @param string $genericTitle title of the generic type of error |
127
|
|
|
* @param string $specificDetails optional, explanation of the specific error |
128
|
|
|
* @param string $specificAboutLink optional, explanation of the specific error |
129
|
|
|
* @param string $genericTypeLink optional, explanation of the generic type of error |
130
|
|
|
*/ |
131
|
6 |
|
public function setHumanExplanation($genericTitle, $specificDetails=null, $specificAboutLink=null, $genericTypeLink=null) { |
132
|
6 |
|
$this->setHumanTitle($genericTitle); |
133
|
|
|
|
134
|
6 |
|
if ($specificDetails !== null) { |
135
|
6 |
|
$this->setHumanDetails($specificDetails); |
136
|
|
|
} |
137
|
6 |
|
if ($specificAboutLink !== null) { |
138
|
1 |
|
$this->setAboutLink($specificAboutLink); |
139
|
|
|
} |
140
|
6 |
|
if ($genericTypeLink !== null) { |
141
|
1 |
|
$this->setTypeLink($genericTypeLink); |
142
|
|
|
} |
143
|
6 |
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* set the link about this specific occurence of the error, explained in a human-friendly way |
147
|
|
|
* |
148
|
|
|
* @param string $href |
149
|
|
|
* @param array $meta optional, if given a LinkObject is added, otherwise a link string is added |
150
|
|
|
*/ |
151
|
1 |
|
public function setAboutLink($href, array $meta=[]) { |
152
|
1 |
|
$this->addLink('about', $href, $meta); |
153
|
1 |
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* set the link of the generic type of this error, explained in a human-friendly way |
157
|
|
|
* |
158
|
|
|
* @param string $href |
159
|
|
|
* @param array $meta optional, if given a LinkObject is added, otherwise a link string is added |
160
|
|
|
*/ |
161
|
5 |
|
public function setTypeLink($href, array $meta=[]) { |
162
|
5 |
|
$this->addLink('type', $href, $meta); |
163
|
5 |
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* append a link of the generic type of this error, explained in a human-friendly way |
167
|
|
|
* |
168
|
|
|
* @deprecated array links are not supported anymore {@see ->setTypeLink()} |
169
|
|
|
* |
170
|
|
|
* @param string $href |
171
|
|
|
* @param array $meta optional, if given a LinkObject is added, otherwise a link string is added |
172
|
|
|
*/ |
173
|
1 |
|
public function appendTypeLink($href, array $meta=[]) { |
174
|
1 |
|
$this->appendLink('type', $href, $meta); |
|
|
|
|
175
|
1 |
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* blame the json pointer from the request body causing this error |
179
|
|
|
* |
180
|
|
|
* @see https://tools.ietf.org/html/rfc6901 |
181
|
|
|
* |
182
|
|
|
* @param string $pointer e.g. "/data/attributes/title" or "/data" |
183
|
|
|
*/ |
184
|
1 |
|
public function blameJsonPointer($pointer) { |
185
|
1 |
|
$this->addSource('pointer', $pointer); |
186
|
1 |
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* blame the query parameter from the request causing this error |
190
|
|
|
* |
191
|
|
|
* @param string $parameter |
192
|
|
|
*/ |
193
|
4 |
|
public function blameQueryParameter($parameter) { |
194
|
4 |
|
$this->addSource('parameter', $parameter); |
195
|
4 |
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* @param string $key |
199
|
|
|
* @param mixed $value |
200
|
|
|
*/ |
201
|
3 |
|
public function addMeta($key, $value) { |
202
|
3 |
|
if ($this->meta === null) { |
203
|
3 |
|
$this->setMetaObject(new MetaObject()); |
204
|
|
|
} |
205
|
|
|
|
206
|
3 |
|
$this->meta->add($key, $value); |
207
|
3 |
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* spec api |
211
|
|
|
*/ |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* a unique identifier for this specific occurrence of the error |
215
|
|
|
* |
216
|
|
|
* @param string|int $id |
217
|
|
|
*/ |
218
|
2 |
|
public function setUniqueIdentifier($id) { |
219
|
2 |
|
$this->id = $id; |
220
|
2 |
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* a code expressing the generic type of this error |
224
|
|
|
* it should be application-specific and aimed at developers |
225
|
|
|
* |
226
|
|
|
* @param string|int $genericCode will be casted to a string |
227
|
|
|
*/ |
228
|
18 |
|
public function setApplicationCode($genericCode) { |
229
|
18 |
|
$this->code = (string) $genericCode; |
230
|
18 |
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* add the source of the error |
234
|
|
|
* |
235
|
|
|
* @param string $key {@see ->blameJsonPointer(), ->blameQueryParameter()} |
236
|
|
|
* @param string $value |
237
|
|
|
*/ |
238
|
6 |
|
public function addSource($key, $value) { |
239
|
6 |
|
Validator::checkMemberName($key); |
240
|
|
|
|
241
|
6 |
|
$this->source[$key] = $value; |
242
|
6 |
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* a short human-friendly explanation of the generic type of this error |
246
|
|
|
* |
247
|
|
|
* @param string $genericTitle |
248
|
|
|
*/ |
249
|
7 |
|
public function setHumanTitle($genericTitle) { |
250
|
7 |
|
$this->title = $genericTitle; |
251
|
7 |
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* a human-friendly explanation of this specific occurrence of the error |
255
|
|
|
* |
256
|
|
|
* @param string $specificDetails |
257
|
|
|
*/ |
258
|
7 |
|
public function setHumanDetails($specificDetails) { |
259
|
7 |
|
$this->detail = $specificDetails; |
260
|
7 |
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* @param MetaObject $metaObject |
264
|
|
|
*/ |
265
|
14 |
|
public function setMetaObject(MetaObject $metaObject) { |
266
|
14 |
|
$this->meta = $metaObject; |
267
|
14 |
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* ObjectInterface |
271
|
|
|
*/ |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* @inheritDoc |
275
|
|
|
*/ |
276
|
10 |
|
public function isEmpty() { |
277
|
10 |
|
if ($this->id !== null) { |
278
|
2 |
|
return false; |
279
|
|
|
} |
280
|
10 |
|
if ($this->hasHttpStatusCode()) { |
281
|
3 |
|
return false; |
282
|
|
|
} |
283
|
9 |
|
if ($this->code !== null) { |
284
|
8 |
|
return false; |
285
|
|
|
} |
286
|
3 |
|
if ($this->title !== null) { |
287
|
1 |
|
return false; |
288
|
|
|
} |
289
|
3 |
|
if ($this->detail !== null) { |
290
|
1 |
|
return false; |
291
|
|
|
} |
292
|
3 |
|
if ($this->links !== null && $this->links->isEmpty() === false) { |
293
|
2 |
|
return false; |
294
|
|
|
} |
295
|
3 |
|
if ($this->source !== []) { |
296
|
1 |
|
return false; |
297
|
|
|
} |
298
|
3 |
|
if ($this->meta !== null && $this->meta->isEmpty() === false) { |
299
|
1 |
|
return false; |
300
|
|
|
} |
301
|
3 |
|
if ($this->hasAtMembers()) { |
302
|
1 |
|
return false; |
303
|
|
|
} |
304
|
3 |
|
if ($this->hasExtensionMembers()) { |
305
|
1 |
|
return false; |
306
|
|
|
} |
307
|
|
|
|
308
|
3 |
|
return true; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* @inheritDoc |
313
|
|
|
*/ |
314
|
19 |
|
public function toArray() { |
315
|
19 |
|
$array = []; |
316
|
|
|
|
317
|
19 |
|
if ($this->hasAtMembers()) { |
318
|
1 |
|
$array = array_merge($array, $this->getAtMembers()); |
319
|
|
|
} |
320
|
19 |
|
if ($this->hasExtensionMembers()) { |
321
|
1 |
|
$array = array_merge($array, $this->getExtensionMembers()); |
322
|
|
|
} |
323
|
19 |
|
if ($this->id !== null) { |
324
|
1 |
|
$array['id'] = $this->id; |
325
|
|
|
} |
326
|
19 |
|
if ($this->hasHttpStatusCode()) { |
327
|
7 |
|
$array['status'] = (string) $this->getHttpStatusCode(); |
328
|
|
|
} |
329
|
19 |
|
if ($this->code !== null) { |
330
|
17 |
|
$array['code'] = $this->code; |
331
|
|
|
} |
332
|
19 |
|
if ($this->title !== null) { |
333
|
6 |
|
$array['title'] = $this->title; |
334
|
|
|
} |
335
|
19 |
|
if ($this->detail !== null) { |
336
|
6 |
|
$array['detail'] = $this->detail; |
337
|
|
|
} |
338
|
19 |
|
if ($this->links !== null && $this->links->isEmpty() === false) { |
339
|
7 |
|
$array['links'] = $this->links->toArray(); |
340
|
|
|
} |
341
|
19 |
|
if ($this->source !== []) { |
342
|
5 |
|
$array['source'] = $this->source; |
343
|
|
|
} |
344
|
19 |
|
if ($this->meta !== null && $this->meta->isEmpty() === false) { |
345
|
13 |
|
$array['meta'] = $this->meta->toArray(); |
346
|
|
|
} |
347
|
|
|
|
348
|
19 |
|
return $array; |
349
|
|
|
} |
350
|
|
|
} |
351
|
|
|
|
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.