initphp / http
Standards-compliant PSR-7 / PSR-17 / PSR-18 HTTP message, factory, client and response emitter implementation for PHP 7.4+.
Requires
- php: >=7.4
- ext-json: *
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0 || ^2.0
Requires (Dev)
- ext-curl: *
- http-interop/http-factory-tests: ^1.1 || ^2.0
- php-http/psr7-integration-tests: ^1.3
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.6 || ^10.5
Suggests
- ext-curl: Required by InitPHP\HTTP\Client\Client (PSR-18) for outbound HTTP requests.
README
Standards-compliant PSR-7 message, PSR-17 factory, PSR-18 client and SAPI response emitter for PHP 7.4+.
A single dependency-light package that ships the four building blocks every PHP project ends up wiring by hand: immutable HTTP messages, a unified factory, a cURL-backed client, and an emitter that converts a ResponseInterface into bytes on the wire.
composer require initphp/http
use InitPHP\HTTP\Facade\Factory; use InitPHP\HTTP\Facade\Emitter; $response = Factory::createResponse(200, 'OK') ->withHeader('Content-Type', 'text/plain; charset=utf-8'); $response->getBody()->write('Hello, world!'); Emitter::emit($response);
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- Documentation
- PSR Compliance
- Migration from 2.x
- Contributing
- Security
- License
Features
- PSR-7 v1 & v2 compatible — works with the entire PSR-7 ecosystem.
- PSR-17 factory — one class implements every factory interface (
Request,Response,ServerRequest,Stream,UploadedFile,Uri). - PSR-18 client backed by cURL with sane production defaults: 30 s request timeout, 10 s connect timeout, redirect following, raw
CURLOPT_*overrides without subclassing. - SAPI emitter that streams the response to the browser with optional chunked output and
Content-Rangesupport. - Lazy static facades for projects that prefer
Factory::createResponse()over instantiating helpers explicitly. - Strict PSR-7 immutability —
with*()returns deep-cloned messages; mutating the clone never touches the original (verified by a dedicated immutability test suite). - Zero runtime dependencies outside
psr/http-*.ext-curlis only required when you actually use the client. - Passes the upstream
php-http/psr7-integration-testsandhttp-interop/http-factory-testssuites.
Requirements
| Component | Minimum |
|---|---|
| PHP | 7.4 |
ext-json |
always required |
ext-curl |
required by InitPHP\HTTP\Client\Client (PSR-18 transport) |
psr/http-message |
`^1.0 |
psr/http-factory |
^1.0 |
psr/http-client |
^1.0 |
Tested on PHP 7.4, 8.0, 8.1, 8.2, 8.3 and 8.4 in CI.
Installation
composer require initphp/http
Quick Start
Building a PSR-7 Request
use InitPHP\HTTP\Message\Request; $request = new Request( 'POST', 'https://api.example.com/users', ['Accept' => 'application/json'], json_encode(['name' => 'Ada']), '1.1' ); $request = $request->withHeader('Content-Type', 'application/json; charset=utf-8');
Building a PSR-7 Response
use InitPHP\HTTP\Message\Response; $response = (new Response()) ->withStatus(201, 'Created') ->withHeader('Location', '/users/42'); $response->getBody()->write('{"id":42}');
Convenience producers on the concrete Response:
$response = (new Response())->json(['id' => 42], 201); $response = (new Response())->redirect('https://example.com/welcome', 302);
Sending an HTTP request (PSR-18)
use InitPHP\HTTP\Client\Client; use InitPHP\HTTP\Message\Request; $client = (new Client()) ->withTimeout(10) ->withConnectTimeout(3) ->withUserAgent('my-app/1.0'); $response = $client->sendRequest( new Request('GET', 'https://httpbin.org/get') ); echo $response->getStatusCode(); // 200 echo (string) $response->getBody(); // {"args":{},"headers":{...},...}
Higher-level helpers when you don't want to build a Request by hand:
$response = $client->get('https://api.example.com/users'); $response = $client->post('https://api.example.com/users', '{"name":"Ada"}', [ 'Content-Type' => 'application/json', ]);
PSR-18 contract is honoured: 4xx/5xx responses are returned, not thrown. Only transport failures raise Psr\Http\Client\NetworkExceptionInterface.
Emitting a response (SAPI)
use InitPHP\HTTP\Emitter\Emitter; $emitter = new Emitter(/* strictMode: */ true); $emitter->emit($response); // echoes body in one go // For large bodies, stream in chunks: $emitter->emit($response, 8192);
Range requests (Content-Range: bytes 0-1023/...) are honoured automatically.
Hydrating a ServerRequest from globals
use InitPHP\HTTP\Message\ServerRequest; $request = ServerRequest::createFromGlobals(); // or with explicit data (recommended for tests and long-running runtimes): $request = ServerRequest::createFromGlobals($server, $get, $post, $cookies, $files);
The factory is stateless — every call returns a fresh instance computed from the supplied arrays. Safe under Swoole, RoadRunner, Octane and FrankenPHP.
Using the static facades
For projects that prefer terseness over explicit DI:
use InitPHP\HTTP\Facade\Factory; use InitPHP\HTTP\Facade\Client; use InitPHP\HTTP\Facade\Emitter; $request = Factory::createRequest('GET', 'https://example.com'); $response = Client::sendRequest($request); Emitter::emit($response);
Each facade lazily resolves a singleton on first call; subsequent calls return the same instance. Facades are entirely optional — every facade is just a thin static wrapper over a concrete service class you can instantiate yourself.
Documentation
In-depth guides live under docs/:
- Getting Started
- PSR-7 — Messages, ServerRequest, Streams, Uri, Uploaded Files
- PSR-17 — Factory
- PSR-18 — Client, Exceptions, Configuration
- Emitter — Basics, Chunked bodies, Content-Range
- Facades — Overview, Customisation
- Recipes — JSON responses, Redirects, File uploads, Streaming large files, Proxying requests
- HTTP status code reference
- Upgrade guide (2.x → 3.x)
PSR Compliance
This package is verified against the official compliance suites:
| Suite | Result |
|---|---|
php-http/psr7-integration-tests |
passing |
http-interop/http-factory-tests (PSR-17) |
passing |
| In-house PSR-18 smoke suite against the PHP built-in test server | passing |
| In-house PSR-7 immutability suite | passing |
Relevant specs: PSR-7, PSR-17, PSR-18.
Migration from 2.x
Version 3.0 is a breaking release. The highlights:
- The custom
InitPHP\HTTP\Message\Interfaces\*interfaces have been removed. Type-hint against the PSR-7 interfaces directly (Psr\Http\Message\RequestInterface, etc.). Request::createFromGlobals()and the magic$request->name = $valueparameter bag have been removed. UseServerRequest::createFromGlobals()(now stateless) and$request->getParsedBody()instead.Client::sendRequest()no longer inspects the concreteRequestclass — it only sees the PSR-7 contract. Pre-encode array/DOM/XML payloads before calling.Clientnow defaults to a 30 s request timeout and 10 s connect timeout. Pass->withTimeout(0)if you need the legacy "no timeout" behaviour.- The misspelled facade trait/interface names
Facadeble[Interface]are nowFacadable[Interface]; the old names remain as@deprecatedaliases and will be removed in 4.0.
See docs/upgrade-guide.md for the full migration walk-through and a copy-pasteable code-mod list.
Contributing
Contributions are welcome — bug reports, doc improvements, performance patches, anything.
-
Open issues and pull requests against InitPHP/HTTP.
-
The project's organisation-wide Contributing Guide and Code of Conduct apply.
-
Local development:
composer install composer ci # phpstan + phpunit
Security
If you discover a security vulnerability please review the project's Security Policy and report it privately. Please do not file public GitHub issues for security problems.
License
MIT License — Copyright © Muhammet ŞAFAK and contributors.