Passed
Push — main ( f8d78a...6afd83 )
by Lode
01:12 queued 12s
created

ErrorObject::setAboutLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
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);
0 ignored issues
show
Deprecated Code introduced by
The function alsvanzelf\jsonapi\objec...rorObject::appendLink() has been deprecated: array links are not supported anymore {@see ->addLink()} ( Ignorable by Annotation )

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

174
		/** @scrutinizer ignore-deprecated */ $this->appendLink('type', $href, $meta);

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...
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