Completed
Push — master ( b77e04...7a7e88 )
by Nathan
08:18
created

Status::getReasonPhrase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php declare(strict_types=1);
2
3
namespace Meek\Http;
4
5
use InvalidArgumentException;
6
7
/**
8
 * Value-object modeling the status-code and reason-phrase part of a status line.
9
 *
10
 * @see https://tools.ietf.org/html/rfc7230#section-3.1.2
11
 * @author Nathan Bishop <[email protected]> (https://nathanbishop.name)
12
 * @copyright 2017 Nathan Bishop
13
 * @license The MIT license.
14
 */
15
final class Status
16
{
17
    /**
18
     * @var integer The code that describes the response.
19
     */
20
    private $statusCode;
21
22
    /**
23
     * @var string The phrase describing the status code.
24
     */
25
    private $reasonPhrase;
26
27
    /**
28
     * @var string[] Default reason phrases for selected status codes.
29
     */
30
    private static $defaultReasonPhrases = [
31
        // Informational 1xx
32
        100 => 'Continue',
33
        101 => 'Switching Protocols',
34
35
        // Successful 2xx
36
        200 => 'OK',
37
        201 => 'Created',
38
        202 => 'Accepted',
39
        203 => 'Non-Authoritative Information',
40
        204 => 'No Content',
41
        205 => 'Reset Content',
42
        206 => 'Partial Content',
43
44
        // Redirection 3xx
45
        300 => 'Multiple Choices',
46
        301 => 'Moved Permanently',
47
        302 => 'Found',
48
        303 => 'See Other',
49
        304 => 'Not Modified',
50
        305 => 'Use Proxy',
51
        306 => '(Unused)',
52
        307 => 'Temporary Redirect',
53
54
        // Client Error 4xx
55
        400 => 'Bad Request',
56
        401 => 'Unauthorized',
57
        402 => 'Payment Required',
58
        403 => 'Forbidden',
59
        404 => 'Not Found',
60
        405 => 'Method Not Allowed',
61
        406 => 'Not Acceptable',
62
        407 => 'Proxy Authentication Required',
63
        408 => 'Request Timeout',
64
        409 => 'Conflict',
65
        410 => 'Gone',
66
        411 => 'Length Required',
67
        412 => 'Precondition Failed',
68
        413 => 'Request Entity Too Large',
69
        414 => 'Request-URI Too Long',
70
        415 => 'Unsupported Media Type',
71
        416 => 'Requested Range Not Satisfiable',
72
        417 => 'Expectation Failed',
73
74
        // Server Error 5xx
75
        500 => 'Internal Server Error',
76
        501 => 'Not Implemented',
77
        502 => 'Bad Gateway',
78
        503 => 'Service Unavailable',
79
        504 => 'Gateway Timeout',
80
        505 => 'HTTP Version Not Supported'
81
    ];
82
83
    /**
84
     * Regex to match for valid characters in the reason phrase.
85
     *
86
     * Allowed characters are: horizontal tabs, spaces and any
87
     * printable character from the USASCII character set.
88
     */
89
    const REASON_PHRASE_VALID_CHAR_REGEX = '/^[\t\x20-\x7E]*$/';
90
91
    /**
92
     * Construct a new status object.
93
     *
94
     * @param integer $statusCode A 3 digit code describing the response.
95
     * @param string $reasonPhrase A short phrase describing the status code, can be empty.
96
     */
97 5
    public function __construct(int $statusCode, string $reasonPhrase = '')
98
    {
99 5
        if ($statusCode < 100 || $statusCode > 599) {
100 2
            throw new InvalidArgumentException('The status code must be in the range 1xx-5xx');
101
        }
102
103 3
        if (!preg_match(self::REASON_PHRASE_VALID_CHAR_REGEX, $reasonPhrase)) {
104 1
            throw new InvalidArgumentException('The reason phrase contains invalid characters');
105
        }
106
107
        // attempt to find a reason phrase for the status code
108 2
        if (empty($reasonPhrase)) {
109 1
            $reasonPhrase = $this->getDefaultReasonPhrase($statusCode);
110
        }
111
112 2
        $this->statusCode = $statusCode;
113 2
        $this->reasonPhrase = $reasonPhrase;    // according to RFC reason phrase can be empty
114 2
    }
115
116
    /**
117
     * Retrieve the status code.
118
     *
119
     * @return integer The 3 digit code describing the response.
120
     */
121 1
    public function getStatusCode(): int
122
    {
123 1
        return $this->statusCode;;
124
    }
125
126
    /**
127
     * Retrieve the reason phrase.
128
     *
129
     * @return string The phrase describing the status code.
130
     */
131 1
    public function getReasonPhrase(): string
132
    {
133 1
        return $this->reasonPhrase;
134
    }
135
136
    /**
137
     * Check if is informational status.
138
     *
139
     * @return boolean True, if informational, otherwise false.
140
     */
141 2
    public function isInformational(): bool
142
    {
143 2
        return $this->statusCode > 99 && $this->statusCode < 200;
144
    }
145
146
    /**
147
     * Check if is successful status.
148
     *
149
     * @return boolean True, if successful, otherwise false.
150
     */
151 2
    public function isSuccessful(): bool
152
    {
153 2
        return $this->statusCode > 199 && $this->statusCode < 300;
154
    }
155
156
    /**
157
     * Check if is redirection status.
158
     *
159
     * @return boolean True, if redirection, otherwise false.
160
     */
161 2
    public function isRedirection(): bool
162
    {
163 2
        return $this->statusCode > 299 && $this->statusCode < 400;
164
    }
165
166
    /**
167
     * Check is is client error status.
168
     *
169
     * @return boolean True, if client error, otherwise false.
170
     */
171 2
    public function isClientError(): bool
172
    {
173 2
        return $this->statusCode > 399 && $this->statusCode < 500;
174
    }
175
176
    /**
177
     * Check if is server error status.
178
     *
179
     * @return boolean True, if server error, otherwise false.
180
     */
181 2
    public function isServerError(): bool
182
    {
183 2
        return $this->statusCode > 499 && $this->statusCode < 600;
184
    }
185
186
    /**
187
     * Return the status code and reason phrase formatted as a
188
     * status-line, but without the HTTP version.
189
     *
190
     * @return string The status line without the HTTP version.
191
     */
192 1
    public function getStatusLine(): string
193
    {
194 1
        return sprintf('%d %s', $this->statusCode, $this->reasonPhrase);
195
    }
196
197
    /**
198
     * Return the status code and reason phrase formatted as a
199
     * status-line, but without the HTTP version.
200
     *
201
     * @return string The status line without the HTTP version.
202
     */
203 1
    public function __toString(): string
204
    {
205 1
        return $this->getStatusLine();
206
    }
207
208
    /**
209
     * Attempt to find a default reason phrase for the status code.
210
     *
211
     * @param integer $statusCode The 3 digit status code describing the response.
212
     * @return string The default reason phrase associated with the status code, or nothing if not found.
213
     */
214 41
    private function getDefaultReasonPhrase(int $statusCode): string
215
    {
216 41
        return array_key_exists($statusCode, static::$defaultReasonPhrases) ? static::$defaultReasonPhrases[$statusCode] : '';
0 ignored issues
show
Comprehensibility introduced by
Since Meek\Http\Status is declared final, using late-static binding will have no effect. You might want to replace static with self instead.

Late static binding only has effect in subclasses. A final class cannot be extended anymore so late static binding cannot occurr. Consider replacing static:: with self::.

To learn more about late static binding, please refer to the PHP core documentation.

Loading history...
217
    }
218
}
219