The component provides a set of HTTP structures that provide information about requests, responses, and their dependencies.
This component already included in the
boson-php/runtime
, so no separate installation is required when using the runtime.
#Installation
Via Composer:
composer require boson-php/http
Requirements:
PHP ^8.4
#Request
The Request
class represents an immutable HTTP request:
use Boson\Component\Http\Request; // Create a new request with default values $request = new Request(); // Create a custom request $request = new Request( method: 'POST', url: 'https://example.com/api/users', headers: [ 'content-type' => 'application/json', 'authorization' => 'Bearer token123' ], body: '{"name": "John", "age": 30}' );
All properties are immutable and can only be accessed, not modified.
All request objects are created by the Boson itself within events, such as SchemeRequestReceived. Therefore, to ensure that the object within an event or intention will NOT be changed and all listeners receive identical information, the request object is immutable.
$app->on(function (SchemeRequestReceived $e): void { $request = $e->request; echo $request->url; echo $request->method; // etc. });
#Response
The Response
class represents a mutable HTTP response:
use Boson\Component\Http\Response; // Create a new response with default values $response = new Response(); // Create a custom response $response = new Response( body: '<h1>Hello World</h1>', headers: [ 'content-type' => 'text/html', 'x-custom-header' => 'value' ], status: 200 ); // Modify response $response->headers->add('x-new-header', 'new value'); $response->body = 'New content'; $response->status = 201;
All responses are created by developer in any form. Therefore, for convenience, they are made mutable by default.
$app->on(function (SchemeRequestReceived $e): void { $e->response = new Response('hello!'); $e->response->status = 404; });
#Headers
Both request and response use the HeadersMap
class for header management.
To get a list of headers, use the headers
property in HTTP objects
(for example, $request->headers
or $response->headers
).
The request contains an immutable
HeadersMap
object which provides methods for reading header information.
// Check if header exists if ($request->headers->has('content-type')) { // Get header value $contentType = $request->headers->first('content-type'); }
While the response contains a mutable
implementation of the list of MutableHeadersMap
headers which represents
not only methods of obtaining information, but also its modifications.
// Add new header if (!$response->headers->has('x-custom-header')) { $response->headers->add('x-custom-header', 'value'); } // Remove header $response->headers->remove('content-type');
The headers map is case-insensitive (lowercased) for header names
#Header Line by Name
To obtain a specific header's value, you can use the first()
method.
$type = $request->headers->first('content-type'); // null or string // The name's case does not affect the result $type = $request->headers->first('Content-Type'); // null or string
If the specified header is missing, the null
value will be returned.
You can pass a second argument to specify a default value.
$type = $request->headers->first('content-type', 'text/html'); // The "$type" will contain expected value // from headers or "text/html"
#All Headers by Name
The HTTP specification allows for multiple headers with the same name to be specified. Although this is a rare case, in those cases where this is needed you can get all the passed header values with the specified name.
$values = $request->headers->all('x-example-header'); // // If headers were passed // // X-Example-Header: 23 // X-Example-Header: 42 // // The "$values" will be // // array:2 [ // 0 => "23" // 1 => "42" // ] //
#Existence Check
To check the existence of the specified header (whether it was passed),
you should use the has()
method.
if ($request->headers->has('upgrade')) { throw new LogicException('HTTP/2 disallows the use of this header'); }
#Value Existence Check
To check that a header with the specified value was passed, you
can use the contains()
method.
if (! $request->headers->contains('content-type', 'application/json')) { throw new LogicException('Only JSON requests are available'); }
#Method
HTTP defines a set of request methods to indicate the purpose of the request and what is expected if the request is successful. Although they can also be nouns, these request methods are sometimes referred to as HTTP verbs. Each request method has its own semantics, but some characteristics are shared across multiple methods, specifically request methods can be safe, idempotent, or cacheable.
The HTTP request`s method can be represented and set as a string, but it implements a full-fledged enum-like value object containing a lot of additional information.
use Boson\Component\Http\Request; // A string may be used during instantiation. $request = new Request(method: 'GET'); // The method can be output as a string echo $request->method; // // Expected Output: // // GET //
#Method Class
Methods are part of the request, but you
can use them separately. For this, Boson component provides an enum-like
Boson\Component\Http\Component\Method
class containing a set of known
HTTP cases:
Method::Get
– The GET HTTP methodMethod::Head
– The HEAD HTTP methodMethod::Options
– The OPTIONS HTTP methodMethod::Trace
– The TRACE HTTP methodMethod::Put
– The PUT HTTP methodMethod::Delete
– The DELETE HTTP methodMethod::Post
– The POST HTTP methodMethod::Patch
– The PATCH HTTP methodMethod::Connect
– The CONNECT HTTP method
Since the Method
class implements behavior similar to
PHP enums,
you also have access to the from()
, tryFrom()
and cases()
methods.
Due to many technical limitations of the PHP (for example, unable to use properties, unable to define the
__toString()
method, unable to overridefrom()
&tryFrom()
methods, etc.), this class cannot be implemented using the classic PHP enum.
use Boson\Component\Http\Component\Method; echo Method::from('get'); // GET echo Method::from('wtf'); // Uncaught ValueError: "wtf" is not a valid backing value for // enum-like Boson\Component\Http\Component\Method echo Method::tryFrom('wtf'); // null
Please note that the
from()
andtryFrom()
methods are case-insensitive. Method value will be uppercased.
use Boson\Component\Http\Component\Method; foreach (Method::cases() as $method) { echo $method . "\n"; } // // Expected Output: // // GET // HEAD // OPTIONS // TRACE // PUT // DELETE // POST // PATCH // CONNECT //
#Method Name
To get the name of the HTTP method, you can use the read-only name
property.
Method name is case-insensitive and always converted to uppercase. For
example, if you write "get
" or "post
", then the method name will always be
GET
or POST
.
use Boson\Component\Http\Request; $request = new Request(method: 'get'); echo $request->method->name; // GET
#Method Idempotency
An HTTP method is idempotent if the intended effect on the server of making a single request is the same as the effect of making several identical requests.
For example, the HTTP specification defines several HTTP methods and their
semantics, which includes whether they are idempotent or not. All
safe methods are idempotent, as well
as PUT
and DELETE
. The POST
and PATCH
methods are not guaranteed
to be idempotent.
In any case, you don't need to remember by heart which HTTP methods are
idempotent, it is enough to use the isIdempotent
property to get this
information.
$get = new Request(method: 'get'); if ($get->method->isIdempotent) { echo 'GET is idempotent'; } $post = new Request(method: 'post'); if ($post->method->isIdempotent) { echo 'POST is idempotent'; } // // Expected Output: // // GET is idempotent //
#Method Safety
An HTTP method is safe if it doesn't alter the state of the server.
In other words, a method is safe if it leads to a read-only operation.
Several common HTTP methods are safe: GET
, HEAD
, or OPTIONS
. All safe
methods are also idempotent,
but not all idempotent methods are safe. For example, PUT
and DELETE
are
both idempotent but unsafe.
To get information about the methods safety you can use the isSafe
property.
use Boson\Component\Http\Request; $get = new Request(method: 'get'); if ($get->method->isSafe) { echo 'GET is safe'; } $post = new Request(method: 'post'); if ($post->method->isSafe) { echo 'POST is safe'; } // // Expected Output: // // GET is safe //
#User Defined Method
In addition to the basic HTTP methods, you may need to create your own non-HTTP method. This is a rare task, but it can happen.
To do this, you should create an instance of the Method
object.
use Boson\Component\Http\Component\Method; echo new Method('PROPFIND'); // PROPFIND
The
Method
s constructor is case-sensitive. Name will be uppercased.
Note that the safety and idempotency properties for such methods are undefined.
use Boson\Component\Http\Component\Method; $method = new Method('PROPFIND'); $method->isSafe; // null $method->isIdempotent; // null
You should specify them explicitly if you want to provide this information.
use Boson\Component\Http\Component\Method; $method = new Method( name: 'PROPFIND', isIdempotent: true, isSafe: false, ); $method->isSafe; // false $method->isIdempotent; // true
#Method Comparison
You can use the strict comparison Method
objects, but if the objects are
different, the result will be a false
although technically both objects
implement an identical HTTP method.
use Boson\Component\Http\Component\Method; Method::Get === new Method('GET'); // false
To compare methods by meaning, even if the implementations may differ,
there is the equals()
method, which guarantees true if the HTTP methods
are identical, even if they implement different objects.
use Boson\Component\Http\Component\Method; new Method('GET')->equals(Method::Get); // true new Method('POST')->equals(Method::Get); // false
#Status Code
HTTP response status codes indicate whether a specific HTTP request has been successfully completed.
To get information about the status code, use the status
property of the
response object.
use Boson\Component\Http\Response; echo new Response(status: 200) ->status; // // Expected Output: // // 200 OK // echo new Response(status: 418) ->status; // // Expected Output: // // 418 I’m A Teapot //
#Status Code Class
Status codes are part of the response, but
you can use them separately. For this, Boson component provides an enum-like
Boson\Component\Http\Component\StatusCode
class containing a set of known
HTTP cases:
StatusCode::Continue
– 100 ContinueStatusCode::SwitchingProtocols
– 101 Switching ProtocolsStatusCode::Processing
– 102 ProcessingStatusCode::EarlyHints
– 103 Early HintsStatusCode::ResponseIsStale
– 110 Response Is StaleStatusCode::RevalidationFailed
– 111 Revalidation FailedStatusCode::DisconnectedOperation
– 112 Disconnected OperationStatusCode::HeuristicExpiration
– 113 Heuristic ExpirationStatusCode::MiscellaneousWarning
– 199 Miscellaneous WarningStatusCode::Ok
– 200 OKStatusCode::Created
– 201 CreatedStatusCode::Accepted
– 202 AcceptedStatusCode::NonAuthoritativeInformation
– 203 Non-Authoritative InformationStatusCode::NoContent
– 204 No ContentStatusCode::ResetContent
– 205 Reset ContentStatusCode::PartialContent
– 206 Partial ContentStatusCode::MultiStatus
– 207 Multi-StatusStatusCode::AlreadyReported
– 208 Already ReportedStatusCode::TransformationApplied
– 214 Transformation AppliedStatusCode::ImUsed
– 226 IM UsedStatusCode::MiscellaneousPersistentWarning
– 299 Miscellaneous Persistent WarninStatusCode::MultipleChoices
– 300 Multiple ChoicesStatusCode::MovedPermanently
– 301 Moved PermanentlyStatusCode::Found
– 302 FoundStatusCode::SeeOther
– 303 See OtherStatusCode::NotModified
– 304 Not ModifiedStatusCode::UseProxy
– 305 Use ProxyStatusCode::Unused
– 306 UnusedStatusCode::TemporaryRedirect
– 307 Temporary RedirectStatusCode::PermanentRedirect
– 308 Permanent RedirectStatusCode::BadRequest
– 400 Bad RequestStatusCode::Unauthorized
– 401 UnauthorizedStatusCode::PaymentRequired
– 402 Payment RequiredStatusCode::Forbidden
– 403 ForbiddenStatusCode::NotFound
– 404 Not FoundStatusCode::MethodNotAllowed
– 405 Method Not AllowedStatusCode::NotAcceptable
– 406 Not AcceptableStatusCode::ProxyAuthenticationRequired
– 407 Proxy Authentication RequiredStatusCode::RequestTimeout
– 408 Request TimeoutStatusCode::Conflict
– 409 ConflictStatusCode::Gone
– 410 GoneStatusCode::LengthRequired
– 411 Length RequiredStatusCode::PreconditionFailed
– 412 Precondition FailedStatusCode::PayloadTooLarge
– 413 Payload Too LargeStatusCode::UriTooLong
– 414 URI Too LongStatusCode::UnsupportedMediaType
– 415 Unsupported Media TypeStatusCode::RangeNotSatisfiable
– 416 Range Not SatisfiableStatusCode::ExpectationFailed
– 417 Expectation FailedStatusCode::ImATeapot
– 418 I’m A TeapotStatusCode::MisdirectedRequest
– 421 Misdirected RequestStatusCode::UnprocessableEntity
– 422 Unprocessable EntityStatusCode::EntityLocked
– 423 LockedStatusCode::FailedDependency
– 424 Failed DependencyStatusCode::HttpTooEarly
– 425 Too EarlyStatusCode::UpgradeRequired
– 426 Upgrade RequiredStatusCode::PreconditionRequired
– 428 Precondition RequiredStatusCode::TooManyRequests
– 429 Too Many RequestsStatusCode::RequestHeaderFieldsTooLarge
– 431 Request Header Fields Too LargeStatusCode::Close
– 444 No ResponseStatusCode::UnavailableForLegalReasons
– 451 Unavailable For Legal ReasonsStatusCode::ClientClosedRequest
– 499 Client Closed RequestStatusCode::InternalServerError
– 500 Internal Server ErrorStatusCode::NotImplemented
– 501 Not ImplementedStatusCode::BadGateway
– 502 Bad GatewayStatusCode::ServiceUnavailable
– 503 Service UnavailableStatusCode::GatewayTimeout
– 504 Gateway TimeoutStatusCode::HttpVersionNotSupported
– 505 HTTP Version Not SupportedStatusCode::HttpVariantAlsoNegotiates
– 506 Variant Also NegotiatesStatusCode::HttpInsufficientStorage
– 507 Insufficient StorageStatusCode::HttpLoopDetected
– 508 Loop DetectedStatusCode::HttpNotExtended
– 510 Not ExtendedStatusCode::HttpNetworkAuthenticationRequired
– 511 Network Authentication RequiredStatusCode::NetworkConnectTimeout
– 599 Network Connect Timeout Error
Since the StatusCode
class implements behavior similar to
PHP enums,
you also have access to the from()
, tryFrom()
and cases()
methods.
Due to many technical limitations of the PHP (for example, unable to use properties, unable to define the
__toString()
method, unable to overridefrom()
&tryFrom()
methods, etc.), this class cannot be implemented using the classic PHP enum.
use Boson\Component\Http\Component\Method; echo StatusCode::from(418); // 418 I’m A Teapot echo StatusCode::from(42); // Uncaught ValueError: "42" is not a valid backing value for // enum-like Boson\Component\Http\Component\StatusCode echo StatusCode::tryFrom(42); // null
To obtain a list of known HTTP codes, you can use the cases()
method.
use Boson\Component\Http\Component\StatusCode; foreach (StatusCode::cases() as $status) { echo $status . "\n"; } // // Expected Output: // // 100 Continue // 101 Switching Protocols // 102 Processing // 103 Early Hints // 110 Response Is Stale // 111 Revalidation Failed // 112 Disconnected Operation // 113 Heuristic Expiration // ...etc. //
#Status Codes and Messages
As with the method, the status code is a value object. The status code instance contains information about the response code and message.
use Boson\Component\Http\Response; $response = new Response(status: 418); echo 'Code: ' . $response->status->code; echo 'Message: ' . $response->status->reason; // // Expected Output: // // Code: 418 // Message: I’m A Teapot //
#Status Categories
Responses are grouped in five categories:
- Informational responses (
100
–199
) - Successful responses (
200
–299
) - Redirection messages (
300
–399
) - Client error responses (
400
–499
) - Server error responses (
500
–599
)
To get information about category of the status, you can use
the category
property.
$response = new Response(status: 418); echo $response->status->category; // // Expected Output: // // Client Error //
#User Defined Status
In some cases you may need to define your own status code. To do this,
you should create an instance of the StatusCode
class.
The status code message is optional.
use Boson\Component\Http\Component\StatusCode; echo new StatusCode(1001, 'DNS Resolution Error'); // // Expected Output: // // 1001 DNS Resolution Error //
#Status Comparison
You can use the strict comparison StatusCode
objects, but if the objects are
different, the result will be a false
although technically both objects
implement an identical HTTP status code.
use Boson\Component\Http\Component\StatusCode; StatusCode::NotFound === new StatusCode(404); // false
To compare status codes by meaning, even if the implementations may differ,
there is the equals()
method, which guarantees true if the HTTP status codes
are identical, even if they implement different objects.
use Boson\Component\Http\Component\StatusCode; new StatusCode(404)->equals(StatusCode::NotFound); // true new StatusCode(500)->equals(StatusCode::NotFound); // false
#JSON Response
The JsonResponse
class extends Response
to provide JSON-specific
functionality:
use Boson\Component\Http\JsonResponse; // Create a JSON response with default values $response = new JsonResponse(); // Create a custom JSON response $response = new JsonResponse( data: ['name' => 'John', 'age' => 30], headers: ['x-custom-header' => 'value'], status: 200 ); // Create a JSON response with custom encoding flags $response = new JsonResponse( data: ['name' => 'John', 'age' => 30], jsonEncodingFlags: JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );