Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
StatusCode
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
9 / 9
15
100.00% covered (success)
100.00%
1 / 1
 getCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getReasonPhrase
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCategory
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
6
 isInformational
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSuccess
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRedirection
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isClientError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isServerError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isError
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5/**
6 * This file is part of php-fast-forward/http-message.
7 *
8 * This source file is subject to the license bundled
9 * with this source code in the file LICENSE.
10 *
11 * @copyright Copyright (c) 2025-2026 Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
12 * @license   https://opensource.org/licenses/MIT MIT License
13 *
14 * @see       https://github.com/php-fast-forward/http-message
15 * @see       https://github.com/php-fast-forward
16 * @see       https://datatracker.ietf.org/doc/html/rfc2119
17 */
18
19namespace FastForward\Http\Message;
20
21/**
22 * Enum StatusCode.
23 *
24 * Defines HTTP status codes in accordance with the IETF RFC 9110 and related specifications.
25 * This enum provides a structured representation of common HTTP response codes, grouped by their respective categories:
26 *
27 * - Informational (1xx)
28 * - Successful (2xx)
29 * - Redirection (3xx)
30 * - Client Errors (4xx)
31 * - Server Errors (5xx)
32 *
33 * All status codes MUST adhere to the official HTTP specification and SHALL be used consistently within HTTP responses.
34 */
35enum StatusCode: int
36{
37    // Informational 1xx
38
39    /** @var int The server has received the request headers and the client SHOULD proceed to send the request body. */
40    case Continue = 100;
41
42    /** @var int The requester has asked the server to switch protocols. */
43    case SwitchingProtocols = 101;
44
45    /** @var int The server has received and is processing the request, but no response is available yet. */
46    case Processing = 102;
47
48    /** @var int Used to return some response headers before the final HTTP message. */
49    case EarlyHints = 103;
50
51    // Successful 2xx
52
53    /** @var int The request has succeeded. */
54    case Ok = 200;
55
56    /** @var int The request has been fulfilled and has resulted in the creation of a new resource. */
57    case Created = 201;
58
59    /** @var int The request has been accepted for processing, but the processing has not been completed. */
60    case Accepted = 202;
61
62    /** @var int The request was successful, but the enclosed payload has been modified from that of the origin server. */
63    case NonAuthoritativeInformation = 203;
64
65    /** @var int The server successfully processed the request and is not returning any content. */
66    case NoContent = 204;
67
68    /** @var int The server successfully processed the request, but is not returning any content, and requests that the requester reset the document view. */
69    case ResetContent = 205;
70
71    /** @var int The server is delivering only part of the resource due to a range header sent by the client. */
72    case PartialContent = 206;
73
74    /** @var int The message body that follows is an XML message and can contain a number of separate response codes. */
75    case MultiStatus = 207;
76
77    /** @var int The members of a DAV binding have already been enumerated in a previous reply to this request, and are not being included again. */
78    case AlreadyReported = 208;
79
80    /** @var int The server has fulfilled a request for the resource, and the response is a representation of the result of one or more instance-manipulations. */
81    case ImUsed = 226;
82
83    // Redirection 3xx
84
85    /** @var int Indicates multiple options for the resource from which the client may choose. */
86    case MultipleChoices = 300;
87
88    /** @var int This and all future requests SHOULD be directed to the given URI. */
89    case MovedPermanently = 301;
90
91    /** @var int The resource resides temporarily under a different URI. */
92    case Found = 302;
93
94    /** @var int The response to the request can be found under another URI using a GET method. */
95    case SeeOther = 303;
96
97    /** @var int Indicates the resource has not been modified since the version specified by the request headers. */
98    case NotModified = 304;
99
100    /** @var int The requested resource is only available through a proxy, whose address is provided in the response. */
101    case UseProxy = 305;
102
103    /** @var int Reserved for future use. */
104    case Reserved = 306;
105
106    /** @var int Instructs the client to repeat the request with a different URI. */
107    case TemporaryRedirect = 307;
108
109    /** @var int The request and all future requests SHOULD be repeated using another URI. */
110    case PermanentRedirect = 308;
111
112    // Client Errors 4xx
113
114    /** @var int The server cannot process the request due to a client error. */
115    case BadRequest = 400;
116
117    /** @var int Authentication is required and has failed or has not yet been provided. */
118    case Unauthorized = 401;
119
120    /** @var int Payment is required to access the requested resource. */
121    case PaymentRequired = 402;
122
123    /** @var int The client does not have permission to access the resource. */
124    case Forbidden = 403;
125
126    /** @var int The requested resource could not be found. */
127    case NotFound = 404;
128
129    /** @var int A request method is not supported for the requested resource. */
130    case MethodNotAllowed = 405;
131
132    /** @var int The requested resource is capable of generating only content not acceptable according to the Accept headers. */
133    case NotAcceptable = 406;
134
135    /** @var int Proxy authentication is required to access the resource. */
136    case ProxyAuthenticationRequired = 407;
137
138    /** @var int The client did not produce a request within the time that the server was prepared to wait. */
139    case RequestTimeout = 408;
140
141    /** @var int The request could not be completed due to a conflict with the current state of the resource. */
142    case Conflict = 409;
143
144    /** @var int The resource requested is no longer available and will not be available again. */
145    case Gone = 410;
146
147    /** @var int The request did not specify the length of its content, which is required by the requested resource. */
148    case LengthRequired = 411;
149
150    /** @var int The server does not meet one of the preconditions specified by the requester. */
151    case PreconditionFailed = 412;
152
153    /** @var int The request is larger than the server is willing or able to process. */
154    case PayloadTooLarge = 413;
155
156    /** @var int The URI provided was too long for the server to process. */
157    case UriTooLong = 414;
158
159    /** @var int The server does not support the media format of the requested data. */
160    case UnsupportedMediaType = 415;
161
162    /** @var int The client has asked for a portion of the file, but the server cannot supply that portion. */
163    case RangeNotSatisfiable = 416;
164
165    /** @var int The server cannot meet the expectations specified in the Expect request header. */
166    case ExpectationFailed = 417;
167
168    /** @var int This code is returned by HTCPCP-compliant teapots. */
169    case ImATeapot = 418;
170
171    /** @var int The request was directed at a server that is not able to produce a response. */
172    case MisdirectedRequest = 421;
173
174    /** @var int The request was well-formed but was unable to be followed due to semantic errors. */
175    case UnprocessableEntity = 422;
176
177    /** @var int The resource that is being accessed is locked. */
178    case Locked = 423;
179
180    /** @var int The request failed due to failure of a previous request. */
181    case FailedDependency = 424;
182
183    /** @var int Indicates the server is unwilling to risk processing a request that might be replayed. */
184    case TooEarly = 425;
185
186    /** @var int The client should switch to a different protocol such as TLS/1.0. */
187    case UpgradeRequired = 426;
188
189    /** @var int The origin server requires the request to be conditional. */
190    case PreconditionRequired = 428;
191
192    /** @var int The user has sent too many requests in a given amount of time. */
193    case TooManyRequests = 429;
194
195    /** @var int The server is unwilling to process the request because its header fields are too large. */
196    case RequestHeaderFieldsTooLarge = 431;
197
198    /** @var int The requested resource is unavailable for legal reasons. */
199    case UnavailableForLegalReasons = 451;
200
201    // Server Errors 5xx
202
203    /** @var int The server encountered an unexpected condition that prevented it from fulfilling the request. */
204    case InternalServerError = 500;
205
206    /** @var int The server does not support the functionality required to fulfill the request. */
207    case NotImplemented = 501;
208
209    /** @var int The server, while acting as a gateway or proxy, received an invalid response from the upstream server. */
210    case BadGateway = 502;
211
212    /** @var int The server is currently unavailable due to maintenance or overload. */
213    case ServiceUnavailable = 503;
214
215    /** @var int The server did not receive a timely response from an upstream server. */
216    case GatewayTimeout = 504;
217
218    /** @var int The server does not support the HTTP protocol version used in the request. */
219    case VersionNotSupported = 505;
220
221    /** @var int Transparent content negotiation for the request results in a circular reference. */
222    case VariantAlsoNegotiates = 506;
223
224    /** @var int The server is unable to store the representation needed to complete the request. */
225    case InsufficientStorage = 507;
226
227    /** @var int The server detected an infinite loop while processing a request. */
228    case LoopDetected = 508;
229
230    /** @var int Further extensions to the request are required for the server to fulfill it. */
231    case NotExtended = 510;
232
233    /** @var int The client needs to authenticate to gain network access. */
234    case NetworkAuthenticationRequired = 511;
235
236    /**
237     * Returns the numeric HTTP status code.
238     *
239     * @return int the numeric status code as defined by the HTTP specification
240     */
241    public function getCode(): int
242    {
243        return $this->value;
244    }
245
246    /**
247     * Returns a human-readable description of the status code.
248     *
249     * The description is derived from the enum name, replacing underscores with spaces and capitalizing each word.
250     *
251     * @return string the reason phrase corresponding to the status code
252     */
253    public function getReasonPhrase(): string
254    {
255        return preg_replace('/(?<!^)[A-Z]/', ' $0', $this->name);
256    }
257
258    /**
259     * Returns the category of the status code.
260     *
261     * Categories are based on the first digit of the status code:
262     * - 1: Informational
263     * - 2: Success
264     * - 3: Redirection
265     * - 4: Client Error
266     * - 5: Server Error
267     *
268     * @return string
269     */
270    public function getCategory(): string
271    {
272        return match (intdiv($this->value, 100)) {
273            1 => 'Informational',
274            2 => 'Success',
275            3 => 'Redirection',
276            4 => 'Client Error',
277            5 => 'Server Error',
278        };
279    }
280
281    /**
282     * Returns true if the status code is informational (1xx).
283     *
284     * @return bool
285     */
286    public function isInformational(): bool
287    {
288        return 1 === intdiv($this->value, 100);
289    }
290
291    /**
292     * Returns true if the status code indicates success (2xx).
293     *
294     * @return bool
295     */
296    public function isSuccess(): bool
297    {
298        return 2 === intdiv($this->value, 100);
299    }
300
301    /**
302     * Returns true if the status code indicates redirection (3xx).
303     *
304     * @return bool
305     */
306    public function isRedirection(): bool
307    {
308        return 3 === intdiv($this->value, 100);
309    }
310
311    /**
312     * Returns true if the status code indicates a client error (4xx).
313     *
314     * @return bool
315     */
316    public function isClientError(): bool
317    {
318        return 4 === intdiv($this->value, 100);
319    }
320
321    /**
322     * Returns true if the status code indicates a server error (5xx).
323     *
324     * @return bool
325     */
326    public function isServerError(): bool
327    {
328        return 5 === intdiv($this->value, 100);
329    }
330
331    /**
332     * Returns true if the status code indicates any type of error (client or server).
333     *
334     * @return bool
335     */
336    public function isError(): bool
337    {
338        if ($this->isClientError()) {
339            return true;
340        }
341
342        return $this->isServerError();
343    }
344}