Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
63 / 63 |
|
100.00% |
38 / 38 |
CRAP | |
100.00% |
1 / 1 |
AbstractResponse | |
100.00% |
63 / 63 |
|
100.00% |
38 / 38 |
48 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
7 | |||
create | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setVersion | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setCode | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getCode | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasCode | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setMessage | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getMessage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasMessage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isSuccess | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
3 | |||
isContinue | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isOk | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isCreated | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isAccepted | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isNoContent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isRedirect | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isMovedPermanently | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isFound | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isError | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
isClientError | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isBadRequest | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isUnauthorized | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isForbidden | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isNotFound | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isMethodNotAllowed | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isNotAcceptable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isRequestTimeout | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isConflict | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isLengthRequired | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isUnsupportedMediaType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isUnprocessableEntity | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isTooManyRequests | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isServerError | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isInternalServerError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isBadGateway | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isServiceUnavailable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Http; |
15 | |
16 | use Pop\Mime\Part\Header; |
17 | use Pop\Mime\Part\Body; |
18 | |
19 | /** |
20 | * Abstract HTTP response class |
21 | * |
22 | * @category Pop |
23 | * @package Pop\Http |
24 | * @author Nick Sagona, III <dev@nolainteractive.com> |
25 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
26 | * @license http://www.popphp.org/license New BSD License |
27 | * @version 5.2.0 |
28 | */ |
29 | abstract class AbstractResponse extends AbstractRequestResponse |
30 | { |
31 | |
32 | /** |
33 | * Response codes & messages |
34 | * @var array |
35 | */ |
36 | protected static array $responseCodes = [ |
37 | // Informational 1xx |
38 | 100 => 'Continue', |
39 | 101 => 'Switching Protocols', |
40 | 102 => 'Processing', |
41 | |
42 | // Success 2xx |
43 | 200 => 'OK', |
44 | 201 => 'Created', |
45 | 202 => 'Accepted', |
46 | 203 => 'Non-Authoritative Information', |
47 | 204 => 'No Content', |
48 | 205 => 'Reset Content', |
49 | 206 => 'Partial Content', |
50 | 207 => 'Multi-Status', |
51 | 208 => 'Already Reported', |
52 | 226 => 'IM Used', |
53 | |
54 | // Redirection 3xx |
55 | 300 => 'Multiple Choices', |
56 | 301 => 'Moved Permanently', |
57 | 302 => 'Found', |
58 | 303 => 'See Other', |
59 | 304 => 'Not Modified', |
60 | 305 => 'Use Proxy', |
61 | 306 => 'Switch Proxy', |
62 | 307 => 'Temporary Redirect', |
63 | 308 => 'Permanent Redirect', |
64 | |
65 | // Client Error 4xx |
66 | 400 => 'Bad Request', |
67 | 401 => 'Unauthorized', |
68 | 402 => 'Payment Required', |
69 | 403 => 'Forbidden', |
70 | 404 => 'Not Found', |
71 | 405 => 'Method Not Allowed', |
72 | 406 => 'Not Acceptable', |
73 | 407 => 'Proxy Authentication Required', |
74 | 408 => 'Request Timeout', |
75 | 409 => 'Conflict', |
76 | 410 => 'Gone', |
77 | 411 => 'Length Required', |
78 | 412 => 'Precondition Failed', |
79 | 413 => 'Payload Too Large', |
80 | 414 => 'URI Too Long', |
81 | 415 => 'Unsupported Media Type', |
82 | 416 => 'Range Not Satisfiable', |
83 | 417 => 'Expectation Failed', |
84 | 421 => 'Misdirected Request', |
85 | 422 => 'Unprocessable Entity', |
86 | 423 => 'Locked', |
87 | 424 => 'Failed Dependency', |
88 | 426 => 'Upgrade Required', |
89 | 428 => 'Precondition Required', |
90 | 429 => 'Too Many Requests', |
91 | 431 => 'Request Header Fields Too Large', |
92 | 451 => 'Unavailable For Legal Reasons', |
93 | |
94 | // Server Error 5xx |
95 | 500 => 'Internal Server Error', |
96 | 501 => 'Not Implemented', |
97 | 502 => 'Bad Gateway', |
98 | 503 => 'Service Unavailable', |
99 | 504 => 'Gateway Timeout', |
100 | 505 => 'HTTP Version Not Supported', |
101 | 506 => 'Variant Also Negotiates', |
102 | 507 => 'Insufficient Storage', |
103 | 508 => 'Loop Detected', |
104 | 509 => 'Bandwidth Limit Exceeded', |
105 | 510 => 'Not Extended', |
106 | 511 => 'Network Authentication Required' |
107 | ]; |
108 | |
109 | /** |
110 | * HTTP version for response, i.e. 1.0, 1.1, 2.0, etc. |
111 | * @var string |
112 | */ |
113 | protected string $version = '1.1'; |
114 | |
115 | /** |
116 | * Response code |
117 | * @var ?int |
118 | */ |
119 | protected ?int $code = null; |
120 | |
121 | /** |
122 | * Response message |
123 | * @var ?string |
124 | */ |
125 | protected ?string $message = null; |
126 | |
127 | /** |
128 | * Constructor |
129 | * |
130 | * Instantiate the response object |
131 | * |
132 | * @param array $config |
133 | */ |
134 | public function __construct(array $config = []) |
135 | { |
136 | // Check for config values and set defaults |
137 | if (!isset($config['version'])) { |
138 | $config['version'] = '1.1'; |
139 | } |
140 | if (!isset($config['code'])) { |
141 | $config['code'] = 200; |
142 | } |
143 | |
144 | $this->setVersion($config['version']) |
145 | ->setCode($config['code']); |
146 | |
147 | if (!isset($config['message'])) { |
148 | $config['message'] = self::$responseCodes[$config['code']]; |
149 | } |
150 | if (!isset($config['headers']) || (!is_array($config['headers']))) { |
151 | $config['headers'] = ['Content-Type' => 'text/html']; |
152 | } |
153 | if (isset($config['body'])) { |
154 | $this->setBody($config['body']); |
155 | } |
156 | |
157 | $this->setMessage($config['message']) |
158 | ->addHeaders($config['headers']); |
159 | } |
160 | |
161 | /** |
162 | * Factory method to create a Response object |
163 | * |
164 | * @param array $config |
165 | * @return static |
166 | */ |
167 | public static function create(array $config = []): static |
168 | { |
169 | return new static($config); |
170 | } |
171 | |
172 | /** |
173 | * Set the response version |
174 | * |
175 | * @param float|string $version |
176 | * @return AbstractResponse |
177 | */ |
178 | public function setVersion(float|string $version): AbstractResponse |
179 | { |
180 | $this->version = (string)$version; |
181 | return $this; |
182 | } |
183 | |
184 | /** |
185 | * Get the response HTTP version |
186 | * |
187 | * @return string |
188 | */ |
189 | public function getVersion(): string |
190 | { |
191 | return $this->version; |
192 | } |
193 | |
194 | /** |
195 | * Has the response HTTP version |
196 | * |
197 | * @return bool |
198 | */ |
199 | public function hasVersion(): bool |
200 | { |
201 | return ($this->version !== null); |
202 | } |
203 | |
204 | /** |
205 | * Set the response code |
206 | * |
207 | * @param int $code |
208 | * @throws Exception |
209 | * @return AbstractResponse |
210 | */ |
211 | public function setCode(int $code = 200): AbstractResponse |
212 | { |
213 | if (!array_key_exists($code, self::$responseCodes)) { |
214 | throw new Exception("Error: The header code '" . $code . "' is not allowed."); |
215 | } |
216 | |
217 | $this->code = $code; |
218 | $this->message = self::$responseCodes[$code]; |
219 | |
220 | return $this; |
221 | } |
222 | |
223 | /** |
224 | * Get the response code |
225 | * |
226 | * @return int |
227 | */ |
228 | public function getCode(): int |
229 | { |
230 | return $this->code; |
231 | } |
232 | |
233 | /** |
234 | * Has the response code |
235 | * |
236 | * @return bool |
237 | */ |
238 | public function hasCode(): bool |
239 | { |
240 | return ($this->code !== null); |
241 | } |
242 | |
243 | /** |
244 | * Set the response message |
245 | * |
246 | * @param ?string $message |
247 | * @return AbstractResponse |
248 | */ |
249 | public function setMessage(?string $message = null): AbstractResponse |
250 | { |
251 | $this->message = $message; |
252 | return $this; |
253 | } |
254 | |
255 | /** |
256 | * Get the response HTTP message |
257 | * |
258 | * @return string |
259 | */ |
260 | public function getMessage(): string |
261 | { |
262 | return $this->message; |
263 | } |
264 | |
265 | /** |
266 | * Has the response message |
267 | * |
268 | * @return bool |
269 | */ |
270 | public function hasMessage(): bool |
271 | { |
272 | return ($this->message !== null); |
273 | } |
274 | |
275 | /** |
276 | * Determine if the response is a success |
277 | * |
278 | * @return bool |
279 | */ |
280 | public function isSuccess(): bool |
281 | { |
282 | $type = floor($this->code / 100); |
283 | return (($type == 1) || ($type == 2) || ($type == 3)); |
284 | } |
285 | |
286 | /** |
287 | * Determine if the response is a 100 continue |
288 | * |
289 | * @return bool |
290 | */ |
291 | public function isContinue(): bool |
292 | { |
293 | $type = floor($this->code / 100); |
294 | return ($type == 1); |
295 | } |
296 | |
297 | /** |
298 | * Determine if the response is 200 OK |
299 | * |
300 | * @return bool |
301 | */ |
302 | public function isOk(): bool |
303 | { |
304 | return ($this->code == 200); |
305 | } |
306 | |
307 | /** |
308 | * Determine if the response is 201 created |
309 | * |
310 | * @return bool |
311 | */ |
312 | public function isCreated(): bool |
313 | { |
314 | return ($this->code == 201); |
315 | } |
316 | |
317 | /** |
318 | * Determine if the response is 202 accepted |
319 | * |
320 | * @return bool |
321 | */ |
322 | public function isAccepted(): bool |
323 | { |
324 | return ($this->code == 202); |
325 | } |
326 | |
327 | /** |
328 | * Determine if the response is 204 No Content |
329 | * |
330 | * @return bool |
331 | */ |
332 | public function isNoContent(): bool |
333 | { |
334 | return ($this->code == 204); |
335 | } |
336 | |
337 | /** |
338 | * Determine if the response is a redirect |
339 | * |
340 | * @return bool |
341 | */ |
342 | public function isRedirect(): bool |
343 | { |
344 | $type = floor($this->code / 100); |
345 | return ($type == 3); |
346 | } |
347 | |
348 | /** |
349 | * Determine if the response is a 301 Moved Permanently |
350 | * |
351 | * @return bool |
352 | */ |
353 | public function isMovedPermanently(): bool |
354 | { |
355 | return ($this->code == 301); |
356 | } |
357 | |
358 | /** |
359 | * Determine if the response is a 302 Found |
360 | * |
361 | * @return bool |
362 | */ |
363 | public function isFound(): bool |
364 | { |
365 | return ($this->code == 302); |
366 | } |
367 | |
368 | /** |
369 | * Determine if the response is an error |
370 | * |
371 | * @return bool |
372 | */ |
373 | public function isError(): bool |
374 | { |
375 | $type = floor($this->code / 100); |
376 | return (($type == 4) || ($type == 5)); |
377 | } |
378 | |
379 | /** |
380 | * Determine if the response is a client error |
381 | * |
382 | * @return bool |
383 | */ |
384 | public function isClientError(): bool |
385 | { |
386 | $type = floor($this->code / 100); |
387 | return ($type == 4); |
388 | } |
389 | |
390 | /** |
391 | * Determine if the response is a 400 Bad Request |
392 | * |
393 | * @return bool |
394 | */ |
395 | public function isBadRequest(): bool |
396 | { |
397 | return ($this->code == 400); |
398 | } |
399 | |
400 | /** |
401 | * Determine if the response is a 401 Unauthorized |
402 | * |
403 | * @return bool |
404 | */ |
405 | public function isUnauthorized(): bool |
406 | { |
407 | return ($this->code == 401); |
408 | } |
409 | |
410 | /** |
411 | * Determine if the response is a 403 Forbidden |
412 | * |
413 | * @return bool |
414 | */ |
415 | public function isForbidden(): bool |
416 | { |
417 | return ($this->code == 403); |
418 | } |
419 | |
420 | /** |
421 | * Determine if the response is a 404 Not Found |
422 | * |
423 | * @return bool |
424 | */ |
425 | public function isNotFound(): bool |
426 | { |
427 | return ($this->code == 404); |
428 | } |
429 | |
430 | /** |
431 | * Determine if the response is a 405 Method Not Allowed |
432 | * |
433 | * @return bool |
434 | */ |
435 | public function isMethodNotAllowed(): bool |
436 | { |
437 | return ($this->code == 405); |
438 | } |
439 | |
440 | /** |
441 | * Determine if the response is a 406 Not Acceptable |
442 | * |
443 | * @return bool |
444 | */ |
445 | public function isNotAcceptable(): bool |
446 | { |
447 | return ($this->code == 406); |
448 | } |
449 | |
450 | /** |
451 | * Determine if the response is a 408 Request Timeout |
452 | * |
453 | * @return bool |
454 | */ |
455 | public function isRequestTimeout(): bool |
456 | { |
457 | return ($this->code == 408); |
458 | } |
459 | |
460 | /** |
461 | * Determine if the response is a 409 Conflict |
462 | * |
463 | * @return bool |
464 | */ |
465 | public function isConflict(): bool |
466 | { |
467 | return ($this->code == 409); |
468 | } |
469 | |
470 | /** |
471 | * Determine if the response is a 411 Length Required |
472 | * |
473 | * @return bool |
474 | */ |
475 | public function isLengthRequired(): bool |
476 | { |
477 | return ($this->code == 411); |
478 | } |
479 | |
480 | /** |
481 | * Determine if the response is a 415 Unsupported Media Type |
482 | * |
483 | * @return bool |
484 | */ |
485 | public function isUnsupportedMediaType(): bool |
486 | { |
487 | return ($this->code == 415); |
488 | } |
489 | |
490 | /** |
491 | * Determine if the response is a 422 Unprocessable Entity |
492 | * |
493 | * @return bool |
494 | */ |
495 | public function isUnprocessableEntity(): bool |
496 | { |
497 | return ($this->code == 422); |
498 | } |
499 | |
500 | /** |
501 | * Determine if the response is a 429 Too Many Requests |
502 | * |
503 | * @return bool |
504 | */ |
505 | public function isTooManyRequests(): bool |
506 | { |
507 | return ($this->code == 429); |
508 | } |
509 | |
510 | /** |
511 | * Determine if the response is a server error |
512 | * |
513 | * @return bool |
514 | */ |
515 | public function isServerError(): bool |
516 | { |
517 | $type = floor($this->code / 100); |
518 | return ($type == 5); |
519 | } |
520 | |
521 | /** |
522 | * Determine if the response is a 500 Internal Server Error |
523 | * |
524 | * @return bool |
525 | */ |
526 | public function isInternalServerError(): bool |
527 | { |
528 | return ($this->code == 500); |
529 | } |
530 | |
531 | /** |
532 | * Determine if the response is a 502 Bad Gateway |
533 | * |
534 | * @return bool |
535 | */ |
536 | public function isBadGateway(): bool |
537 | { |
538 | return ($this->code == 502); |
539 | } |
540 | |
541 | /** |
542 | * Determine if the response is a 503 Service Unavailable |
543 | * |
544 | * @return bool |
545 | */ |
546 | public function isServiceUnavailable(): bool |
547 | { |
548 | return ($this->code == 503); |
549 | } |
550 | |
551 | } |