Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
63 / 63
100.00% covered (success)
100.00%
38 / 38
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractResponse
100.00% covered (success)
100.00%
63 / 63
100.00% covered (success)
100.00%
38 / 38
48
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setVersion
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getVersion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasVersion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setCode
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMessage
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSuccess
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 isContinue
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isOk
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isCreated
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isAccepted
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isNoContent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRedirect
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isMovedPermanently
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isFound
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isError
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 isClientError
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isBadRequest
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isUnauthorized
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isForbidden
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isNotFound
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isMethodNotAllowed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isNotAcceptable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRequestTimeout
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isConflict
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isLengthRequired
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isUnsupportedMediaType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isUnprocessableEntity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isTooManyRequests
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isServerError
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isInternalServerError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isBadGateway
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isServiceUnavailable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
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 */
14namespace Pop\Http;
15
16use Pop\Mime\Part\Header;
17use 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.0.0
28 */
29abstract 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}