effectra / http-message
The Effectra HTTP Message package.
v2.0.0
2026-07-26 20:48 UTC
Requires
- php: ^8.0.2
- psr/http-message: ^1.1 || ^2.0
Requires (Dev)
- barryvdh/laravel-ide-helper: ^3.7
- illuminate/support: ^13.22
- mrpunyapal/peststan: ^0.2.12
- pestphp/pest: ^3.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.2
- spatie/laravel-package-tools: ^1.93
README
A PSR-7 HTTP message library for PHP 8.1+.
Installation
composer require effectra/http-message
Requirements
- PHP ^8.1
- psr/http-message ^1.1 || ^2.0
Package Features
- PSR-7 compliant —
Request,Response,ServerRequest,Uri,Stream,UploadedFile,Message - Case-insensitive headers —
hasHeader(),getHeader(),withHeader(), etc. all normalize to lowercase - Immutable with* methods — all PSR-7
with*methods return new instances; mutableset*methods are also available - Response factories —
withJson(),static json(),static html(),static emptyResponse() - ServerRequest utilities —
fromGlobals()factory,replaceHeader()helper - Stream from file path — constructor accepts a file path string and opens it as a resource
- UploadedFile —
getStream()reads from the actual uploaded file - PHP 8 features —
finalclasses,readonlyproperties,#[Immutable]attribute - Comprehensive test suite — 196+ tests, PHPStan level 6 clean
Usage
Creating a Request
use Effectra\Http\Message\Request; use Effectra\Http\Message\Uri; $uri = new Uri('https://api.example.com/users'); $request = new Request('POST', $uri, ['Content-Type' => 'application/json'], '{"name":"John"}'); echo $request->getMethod(); // POST echo $request->getHeaderLine('Content-Type'); // application/json
Immutable with* methods:
$new = $request->withMethod('PUT')->withUri(new Uri('https://other.com')); // $request is unchanged
Mutable set* methods:
$request->setMethod('PATCH')->setUri(new Uri('/local')); // $request is modified in place
Creating a Response
use Effectra\Http\Message\Response; $response = new Response(200, ['X-Custom' => 'value'], 'OK'); echo $response->getStatusCode(); // 200 echo $response->getReasonPhrase(); // OK
Response Factories
// JSON response $res = Response::json(['status' => 'ok']); $res = (new Response())->withJson(['error' => 'bad'], 400); // HTML response $res = Response::html('<h1>Hello</h1>', 200); // Empty response (e.g. 204 No Content) $res = Response::emptyResponse(204);
All standard HTTP status codes (100–511) are available as class constants:
Response::HTTP_OK; // 200 Response::HTTP_NOT_FOUND; // 404 Response::HTTP_I_AM_A_TEAPOT; // 418
ServerRequest (from globals)
use Effectra\Http\Message\ServerRequest; $request = ServerRequest::fromGlobals(); echo $request->getMethod(); // GET, POST, etc. echo $request->getServerParams()['REMOTE_ADDR']; $input = $request->getParsedBody(); // POST / JSON body
Working with URIs
use Effectra\Http\Message\Uri; $uri = new Uri('https://example.com/path?q=hello#section'); echo $uri->getScheme(); // https echo $uri->getHost(); // example.com echo $uri->getPath(); // /path echo $uri->getQuery(); // q=hello $new = $uri->withScheme('http')->withHost('other.org');
Stream
use Effectra\Http\Message\Stream; // From a string $stream = new Stream('content'); echo $stream->getContents(); // content // From a file path (opens automatically) $stream = new Stream('/path/to/file.txt'); // From a resource $stream = new Stream(fopen('php://memory', 'r+')); $stream->write('data');
Uploaded Files
use Effectra\Http\Message\UploadedFile; $file = new UploadedFile( $_FILES['avatar']['tmp_name'], $_FILES['avatar']['size'], $_FILES['avatar']['error'], $_FILES['avatar']['name'], $_FILES['avatar']['type'] ); $stream = $file->getStream(); // read file contents echo $file->getClientFilename(); // original name $file->moveTo('/storage/avatars/1.jpg');
Case-Insensitive Headers
All header operations are case-insensitive:
$request = new Request('GET', '/', ['Content-Type' => 'text/html']); $request->hasHeader('content-type'); // true $request->hasHeader('CONTENT-TYPE'); // true $request->hasHeader('Content-Type'); // true $request->getHeader('CONTENT-TYPE'); // ['text/html'] $cloned = $request->withoutHeader('content-type'); $cloned = $request->withAddedHeader('X-Custom', 'val');
Attributes (PHP 8)
use Effectra\Http\Message\Attribute\Immutable; #[Immutable('This class follows the PSR-7 immutable pattern')] class MyMessage { // ... }
#[Immutable] and #[Deprecated] attributes are available for documentation and static analysis.
PHPStan
composer run phpstan
Level 6 with baseline. The _ide_helper.php file provides additional type information for better IDE support.
Testing
composer test
Contributing
Contributions are welcome. Please open an issue or submit a pull request.
License
MIT