waffle-commons / http
Http component for Waffle framework.
Requires
- php: ^8.5
- psr/http-factory: ^1.1
- psr/http-message: ^2.0
- waffle-commons/contracts: 0.1.0-beta2.1
Requires (Dev)
- carthage-software/mago: ^1.29
- cyclonedx/cyclonedx-php-composer: ^6.2
- php-mock/php-mock-phpunit: ^2.15
- phpunit/phpunit: ^12.5
- vimeo/psalm: ^6.16
This package is auto-updated.
Last update: 2026-05-30 19:47:07 UTC
README
Waffle HTTP Component
Release:
v0.1.0-beta2Β |ΒCHANGELOG.mdPSR Compliance: PSR-7 (HTTP Messages), PSR-17 (HTTP Factories)
A strict, immutable PSR-7/17 implementation tuned for FrankenPHP worker mode. No singletons, no superglobal touching outside the explicit GlobalsFactory. Streams are seekable-aware; the ResponseEmitter chunks bodies to avoid loading large payloads into memory.
π¦ Installation
composer require waffle-commons/http
π§± Surface
| Class | PSR | Role |
|---|---|---|
Waffle\Commons\Http\Request |
PSR-7 | Outbound HTTP request message. |
Waffle\Commons\Http\ServerRequest |
PSR-7 | Inbound HTTP request (the kernel input). |
Waffle\Commons\Http\Response |
PSR-7 | HTTP response message. |
Waffle\Commons\Http\Stream |
PSR-7 | Resource-backed StreamInterface. |
Waffle\Commons\Http\Uri |
PSR-7 | Immutable URI. |
Waffle\Commons\Http\UploadedFile |
PSR-7 | File-upload representation. |
Waffle\Commons\Http\Abstract\AbstractMessage |
β | Shared base for Request / ServerRequest / Response. |
Waffle\Commons\Http\Factory\RequestFactory |
PSR-17 | createRequest(). |
Waffle\Commons\Http\Factory\ServerRequestFactory |
PSR-17 | createServerRequest(). |
Waffle\Commons\Http\Factory\ResponseFactory |
PSR-17 | createResponse(). |
Waffle\Commons\Http\Factory\StreamFactory |
PSR-17 | createStream(), createStreamFromFile(), createStreamFromResource(). |
Waffle\Commons\Http\Factory\UriFactory |
PSR-17 | createUri(). |
Waffle\Commons\Http\Factory\UploadedFileFactory |
PSR-17 | createUploadedFile(). |
Waffle\Commons\Http\Factory\GlobalsFactory |
β | Framework-specific: builds a PSR-7 ServerRequest from $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES, and php://input. |
Waffle\Commons\Http\Emitter\ResponseEmitter |
β | Implements ResponseEmitterInterface: sends status line, headers and chunked body. |
π Bootstrap a server request
use Waffle\Commons\Http\Factory\GlobalsFactory; $factory = new GlobalsFactory(); $request = $factory->createFromGlobals(); // PSR-7 ServerRequestInterface
The factory takes an optional (callable(): StreamInterface) $bodyStreamFactory so tests can inject a synthetic body without touching php://input:
public function __construct(?callable $bodyStreamFactory = null)
Security note.
GlobalsFactorydoes not enforce trusted hosts. Host-header anti-poisoning is handled one layer up byWaffle\Commons\Pipeline\Middleware\TrustedHostMiddleware, which sits betweenErrorHandlerMiddlewareandCoreRoutingMiddlewarein the PSR-15 stack.
π€ Emit a response
use Waffle\Commons\Http\Emitter\ResponseEmitter; use Waffle\Commons\Http\Factory\ResponseFactory; $response = (new ResponseFactory()) ->createResponse(200) ->withHeader('Content-Type', 'application/json'); (new ResponseEmitter())->emit($response);
The emitter:
- Throws
\RuntimeExceptionif headers are already sent. - Sends one
header()per header value (combining for non-Set-Cookieheaders). - Reads the response body in 8 KiB chunks via
StreamInterface::read(), so streaming large payloads is memory-bounded.
π PHP 8.5 features used
- Immutable, named-argument-friendly factories β every
withβ¦()accessor returns a clone. - Typed properties + constructor promotion across messages.
- Strict types in every file (
declare(strict_types=1);). - The
ResponseEmitter::emit()signature uses#[\Override]to assert the contract.
π§ Architectural boundary (mago guard)
An active dependency perimeter is enforced on every CI run by vendor/bin/mago guard (bundled into composer mago; zero baselines). The rules live in mago.toml under [guard.perimeter] β a forbidden use statement fails the build, not a reviewer.
Production code under Waffle\Commons\Http may depend only on:
Waffle\Commons\Http\**β itselfWaffle\Commons\Contracts\**β the shared contracts package, the only Waffle dependency permittedPsr\**β PSR interfaces (PSR-7 / PSR-17)@global+Psl\**β PHP core and the PHP Standard Library
Test code under WaffleTests\Commons\Http is unrestricted (@all); the php-mock bootstrap fixtures noted under Testing are listed in [guard].excludes because they intentionally re-declare the production namespace. Structural rules are guarded too: interfaces must be named *Interface, Exception\** classes must end in *Exception, and any Enum\** namespace may hold only enum declarations.
Contract-first, component-agnostic by construction: components compose through waffle-commons/contracts, never directly through one another.
π§ͺ Testing
docker exec -w /waffle-commons/http waffle-dev composer tests
Mock-bootstrap files under tests/src/StreamTest.php, tests/src/UploadedFileTest.php, tests/src/Factory/StreamFactoryTest.php, and tests/src/Emitter/ResponseEmitterTest.php intentionally declare the production namespace to override built-in PHP functions via php-mock-phpunit. They are listed in mago.toml [guard].excludes for that reason.
π License
MIT β see LICENSE.md.