Passed
Push — master ( ca348b...1f6505 )
by Marcio
03:43
created

Response::__toString()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 7
c 1
b 0
f 1
dl 0
loc 12
rs 10
cc 3
nc 3
nop 0
1
<?php
2
/**
3
 *
4
 * KNUT7 K7F (https://marciozebedeu.com/)
5
 * KNUT7 K7F (tm) : Rapid Development Framework (https://marciozebedeu.com/)
6
 *
7
 * Licensed under The MIT License
8
 * For full copyright and license information, please see the LICENSE.txt
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @link      https://github.com/knut7/framework/ for the canonical source repository
12
 * @copyright (c) 2015.  KNUT7  Software Technologies AO Inc. (https://marciozebedeu.com/)
13
 * @license   https://marciozebedeu.com/license/new-bsd New BSD License
14
 * @author    Marcio Zebedeu - [email protected]
15
 * @version   1.0.15
16
 *
17
 *
18
 */
19
20
namespace Ballybran\Core\Http;
21
22
23
class Response extends Message
24
{
25
    /**
26
     * This is the list of currently registered HTTP status codes.
27
     *
28
     * @var array
29
     */
30
    public static $statusCodes = [
31
        100 => 'Continue',
32
        101 => 'Switching Protocols',
33
        102 => 'Processing',
34
        200 => 'OK',
35
        201 => 'Created',
36
        202 => 'Accepted',
37
        203 => 'Non-Authorative Information',
38
        204 => 'No Content',
39
        205 => 'Reset Content',
40
        206 => 'Partial Content',
41
        207 => 'Multi-Status', // RFC 4918
42
        208 => 'Already Reported', // RFC 5842
43
        226 => 'IM Used', // RFC 3229
44
        300 => 'Multiple Choices',
45
        301 => 'Moved Permanently',
46
        302 => 'Found',
47
        303 => 'See Other',
48
        304 => 'Not Modified',
49
        305 => 'Use Proxy',
50
        307 => 'Temporary Redirect',
51
        308 => 'Permanent Redirect',
52
        400 => 'Bad Request',
53
        401 => 'Unauthorized',
54
        402 => 'Payment Required',
55
        403 => 'Forbidden',
56
        404 => 'Not Found',
57
        405 => 'Method Not Allowed',
58
        406 => 'Not Acceptable',
59
        407 => 'Proxy Authentication Required',
60
        408 => 'Request Timeout',
61
        409 => 'Conflict',
62
        410 => 'Gone',
63
        411 => 'Length Required',
64
        412 => 'Precondition failed',
65
        413 => 'Request Entity Too Large',
66
        414 => 'Request-URI Too Long',
67
        415 => 'Unsupported Media Type',
68
        416 => 'Requested Range Not Satisfiable',
69
        417 => 'Expectation Failed',
70
        418 => 'I\'m a teapot', // RFC 2324
71
        421 => 'Misdirected Request', // RFC7540 (HTTP/2)
72
        422 => 'Unprocessable Entity', // RFC 4918
73
        423 => 'Locked', // RFC 4918
74
        424 => 'Failed Dependency', // RFC 4918
75
        426 => 'Upgrade Required',
76
        428 => 'Precondition Required', // RFC 6585
77
        429 => 'Too Many Requests', // RFC 6585
78
        431 => 'Request Header Fields Too Large', // RFC 6585
79
        451 => 'Unavailable For Legal Reasons', // draft-tbray-http-legally-restricted-status
80
        500 => 'Internal Server Error',
81
        501 => 'Not Implemented',
82
        502 => 'Bad Gateway',
83
        503 => 'Service Unavailable',
84
        504 => 'Gateway Timeout',
85
        505 => 'HTTP Version not supported',
86
        506 => 'Variant Also Negotiates',
87
        507 => 'Insufficient Storage', // RFC 4918
88
        508 => 'Loop Detected', // RFC 5842
89
        509 => 'Bandwidth Limit Exceeded', // non-standard
90
        510 => 'Not extended',
91
        511 => 'Network Authentication Required', // RFC 6585
92
    ];
93
94
    /**
95
     * HTTP status code.
96
     *
97
     * @var int
98
     */
99
    protected $status;
100
101
    /**
102
     * HTTP status text.
103
     *
104
     * @var string
105
     */
106
    protected $statusText;
107
108
    /**
109
     * Creates the response object.
110
     *
111
     * @param string|int $status
112
     * @param array      $headers
113
     * @param resource   $body
114
     */
115
    public function __construct($status = 500, array $headers = null, $body = null)
116
    {
117
        if (null !== $status) {
0 ignored issues
show
introduced by
The condition null !== $status is always true.
Loading history...
118
            $this->setStatus($status);
119
        }
120
        if (null !== $headers) {
121
            $this->setHeaders($headers);
122
        }
123
        if (null !== $body) {
124
            $this->setBody($body);
125
        }
126
    }
127
128
    /**
129
     * Returns the current HTTP status code.
130
     */
131
    public function getStatus(): int
132
    {
133
        return $this->status;
134
    }
135
136
    /**
137
     * Returns the human-readable status string.
138
     *
139
     * In the case of a 200, this may for example be 'OK'.
140
     */
141
    public function getStatusText(): string
142
    {
143
        return $this->statusText;
144
    }
145
146
    /**
147
     * Sets the HTTP status code.
148
     *
149
     * This can be either the full HTTP status code with human readable string,
150
     * for example: "403 I can't let you do that, Dave".
151
     *
152
     * Or just the code, in which case the appropriate default message will be
153
     * added.
154
     *
155
     * @param string|int $status
156
     *
157
     * @throws \InvalidArgumentException
158
     */
159
    public function setStatus($status)
160
    {
161
        if (ctype_digit($status) || is_int($status)) {
162
            $statusCode = $status;
163
            $statusText = self::$statusCodes[$status] ?? 'Unknown';
164
        } else {
165
            list(
166
                $statusCode,
167
                $statusText
168
            ) = explode(' ', $status, 2);
169
            $statusCode = (int) $statusCode;
170
        }
171
        if ($statusCode < 100 || $statusCode > 999) {
172
            throw new \InvalidArgumentException('The HTTP status code must be exactly 3 digits');
173
        }
174
175
        $this->status = $statusCode;
0 ignored issues
show
Documentation Bug introduced by
It seems like $statusCode can also be of type string. However, the property $status is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
176
        $this->statusText = $statusText;
177
    }
178
179
    /**
180
     * Serializes the response object as a string.
181
     *
182
     * This is useful for debugging purposes.
183
     */
184
    public function __toString(): string
185
    {
186
        $str = 'HTTP/'.$this->httpVersion.' '.$this->getStatus().' '.$this->getStatusText()."\r\n";
187
        foreach ($this->getHeaders() as $key => $value) {
188
            foreach ($value as $v) {
189
                $str .= $key.': '.$v."\r\n";
190
            }
191
        }
192
        $str .= "\r\n";
193
        $str .= $this->getBodyAsString();
194
195
        return $str;
196
    }
197
}