pivotphp / http
HTTP foundation for PivotPHP - PSR-7/PSR-17 messages built on nyholm/psr7 with Express.js-inspired helpers
Requires
- php: ^8.1
- nyholm/psr7: ^1.8
- nyholm/psr7-server: ^1.1
- psr/http-factory: ^1.0
- psr/http-message: ^1.1|^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^10.5|^11.0
- squizlabs/php_codesniffer: ^3.7
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-10 23:26:52 UTC
README
HTTP layer for PivotPHP: a thin Express.js-style facade on top of PSR-7 messages. The underlying representation is nyholm/psr7; PivotPHP adds the ergonomic API that a framework inspired by Express.js needs.
This package is pure HTTP: it depends only on nyholm/psr7, nyholm/psr7-server and the
PSR interfaces — no dependency on the PivotPHP kernel, container or router.
Why a facade?
PSR-7 is excellent at representing HTTP messages but deliberately minimal — it has no
param() or json() helpers. PivotPHP keeps its Express.js syntax by wrapping the PSR-7
objects:
$app->get('/api/users/:id', function ($req, $res) { return $res->json([ 'id' => $req->param('id'), 'name' => 'John Doe', ]); });
Features
ExpressRequest— read facade overServerRequestInterface.ExpressResponse— fluent facade overResponseInterface.BodyParser/Middleware\BodyParsingMiddleware— parse JSON and form bodies (includingPUT/PATCH/DELETE).Emitter\SapiEmitter— emit the response to the SAPI (status/headers once, streamed body, no body onHEAD).Factory\Psr17Factory/Factory\ServerRequestFactory— PSR-17 andfromGlobals()/fromArrays().- Type safety: strict typing, PHPStan level 9.
Installation
composer require pivotphp/http
Usage
Request
The router stores the matched route parameters as the route_params attribute on the
PSR-7 request; ExpressRequest reads them.
use PivotPHP\Http\ExpressRequest; $id = $req->param('id'); // route parameter $all = $req->params(); // all route parameters $page = $req->query('page', '1'); // query string $name = $req->input('name'); // parsed body, then query string $data = $req->json(); // decoded JSON body (array) $token = $req->header('Authorization'); $secure = $req->isSecure(); $html = $req->accepts('text/html'); // content negotiation (wildcards + q)
Response
Setters are fluent; terminal methods also update the wrapped response, so psr7() reflects the
final state even without capturing the return value.
use PivotPHP\Http\ExpressResponse; return $res->status(201)->header('X-App', 'PivotPHP')->json($data); // or: return $res->redirect('/login'); return $res->error(404, 'not found');
Body parsing
PHP only fills $_POST for POST requests, so JSON and form bodies on PUT/PATCH/DELETE
are otherwise lost. Add BodyParsingMiddleware to the pipeline — it decodes JSON
(application/json, application/*+json) and parses form bodies, populating getParsedBody():
use PivotPHP\Http\Middleware\BodyParsingMiddleware; $app->use(new BodyParsingMiddleware());
A malformed JSON body throws MalformedJsonException, which implements
HttpExceptionInterface (getStatusCode() returns 400) — catch it and return 400.
Parsing stays in the pipeline on purpose: ServerRequestFactory only builds the request, so
the exception is raised where an error handler can turn it into a response.
Emitting the response
use PivotPHP\Http\Emitter\SapiEmitter; (new SapiEmitter())->emit($response, $request->getMethod());
SapiEmitter sends the status line and headers once, streams the body in chunks, omits the body
for HEAD and body-less statuses (1xx, 204, 304), and sets Content-Length when known.
API
PivotPHP\Http\ExpressRequest (wraps ServerRequestInterface)
| Method | Description |
|---|---|
params(): array |
All matched route parameters. |
param(string $name, mixed $default = null) |
Route parameter. |
query(string $name, mixed $default = null) |
Query-string value. |
input(string $name, mixed $default = null) |
Parsed body, then query string. |
json(): array |
Decoded JSON body; throws MalformedJsonException on malformed input. |
header(string $name, ?string $default = null) |
Header line. |
cookie(string $name, ?string $default = null) |
Cookie value. |
file(string $name) |
Uploaded file. |
ip(), userAgent(), method(), path() |
Request metadata. |
isAjax(), isJson(), isSecure(), accepts(string $type) |
Checks. |
psr7(): ServerRequestInterface |
The underlying PSR-7 request. |
PivotPHP\Http\ExpressResponse (wraps ResponseInterface)
Fluent setters return $this; terminal senders update the wrapped response and return it.
| Method | Kind | Description |
|---|---|---|
status(int $code) |
setter | Set the status code. |
header(string $name, string $value) |
setter | Set a header. |
cookie(string $name, string $value, array $options = []) |
setter | Append Set-Cookie. |
json(mixed $data, ?int $status = null) |
sender | JSON response. |
text(string $text, ?int $status = null) |
sender | Plain-text response. |
html(string $html, ?int $status = null) |
sender | HTML response. |
redirect(string $url, int $status = 302) |
sender | Redirect. |
noContent(int $status = 204) |
sender | Empty response. |
send(mixed $data, ?int $status = null) |
sender | JSON for arrays/objects, text otherwise. |
error(int $status, string $message = '') |
sender | {"error": "..."} JSON. |
psr7(): ResponseInterface |
— | The wrapped PSR-7 response. |
PivotPHP\Http\Factory\ServerRequestFactory
use PivotPHP\Http\ExpressRequest; use PivotPHP\Http\Factory\ServerRequestFactory; $request = ServerRequestFactory::fromGlobals(); // reads $_SERVER, $_GET, $_POST, $_FILES, ... $request = ServerRequestFactory::fromArrays($server, $headers, $cookies, $get, $post, $files); $req = new ExpressRequest($request);
The factory only builds the request; body parsing is done by BodyParsingMiddleware.
Requirements
- PHP 8.1 or higher
nyholm/psr7^1.8,nyholm/psr7-server^1.1- PSR-7 / PSR-15 / PSR-17 interfaces
Testing
composer test # PHPUnit composer phpstan # Static analysis (level 9) composer cs:check # PSR-12 style check composer quality:check # phpstan + cs:check + test
License
MIT License - see LICENSE file for details.
Community
Credits
Created by Caio Alberto Fernandes and the PivotPHP community.