decodelabs / harvest
PSR-15 HTTP stack without the mess
Installs: 1 367
Dependents: 3
Suggesters: 0
Security: 0
Stars: 0
Watchers: 2
Forks: 0
Open Issues: 0
Requires
- php: ^8.1
- decodelabs/archetype: ^0.3
- decodelabs/coercion: ^0.2.7
- decodelabs/collections: ^0.9
- decodelabs/compass: ^0.2
- decodelabs/deliverance: ^0.2.11
- decodelabs/exceptional: ^0.4.4
- decodelabs/glitch-support: ^0.4.4
- decodelabs/singularity: ^0.2.2
- decodelabs/slingshot: ^0.1.1
- decodelabs/veneer: ^0.11.6
- psr/container: ^2.0
- psr/http-factory: ^1.0
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- decodelabs/atlas: ^0.12
- decodelabs/genesis: ^0.9
- decodelabs/pandora: ^0.2.11
- decodelabs/phpstan-decodelabs: ^0.6.7
- decodelabs/sanctum: ^0.2
Suggests
- decodelabs/sanctum: Content security policy support
This package is auto-updated.
Last update: 2024-10-04 21:27:39 UTC
README
PSR-15 HTTP stack without the mess
Harvest provides a unified PSR-15 HTTP stack with a simple, expressive API on top of PHP Fibers to avoid common pitfalls of other PSR-15 implementations such as call stack size, memory usage and Middleware traversal.
Get news and updates on the DecodeLabs blog.
Installation
Install via Composer:
composer require decodelabs/harvest
Usage
Harvest provides the full PSR-15 stack, including Request, Response, Middleware and Handler interfaces.
use DecodeLabs\Harvest; use DecodeLabs\Harvest\Dispatcher; use DecodeLabs\Harvest\Middleware\ContentSecurityPolicy; // Create a Dispatcher $dispatcher = new Dispatcher( $myPsrContainer // Ideally initialize with a PSR-11 container ); // Add middleware $dispatcher->add( 'ErrorHandler', // Resolve by name via container / Archetype new ContentSecurityPolicy(), // Add middleware instance function($request, $handler) { // Add middleware callback // $handler is the next middleware in the stack // $request is the current request // Return a response return Harvest::text('Hello World!'); } ); $request = Harvest::createRequestFromEnvironment(); $response = $dispatcher->dispatch($request);
String names passed to the Dispatcher will resolve via the optional PSR Container and then Archetype which has a default mapping for DecodeLabs\Harvest\Middleware
but can easily be extended with:
use DecodeLabs\Archetype; use DecodeLabs\Harvest\Middleware; Archetype::map(Middleware::class, MyMiddlewareNamespace::class);
Fibers
Harvest uses PHP Fibers to flatten the call stack within the dispatch loop - this makes for considerably less noise when debugging and understanding Exception call stacks.
Instead of a call stack that grows by at least 2 frames for every Middleware instance in the queue (which gets problematic very quickly), Harvest utilises the flexbility of Fibers to break out of the stack at each call to the next HTTP handler and effectively run each Middleware as if it were in a flat list, but without breaking Exception handling or any of the semantics of stacking the Middleware contexts.
Transports
Once a Response has been generated, you can then use an instance of a Harvest Transport
to send it to the client.
Harvest currently provides a Generic Transport implementation that uses PHP's built in header and output stream functions.
use DecodeLabs\Harvest; $transport = Harvest::createTransport( // $name - a null name will default to the Generic transport ); $transport->sendResponse( $request, $response ); exit;
Responses
Harvest provides easy shortcuts for creating Response instances:
use DecodeLabs\Harvest; $text = Harvest::text('Hello World!'); // Text $customText = Harvest::text('Hello World!', 201, [ 'Custom-Header' => 'header-value' ]); $html = Harvest::html('<h1>Hello World!</h1>'); // HTML $json = Harvest::json([ 'whatever-data' => 'Hello World!' ]); // JSON $xml = Harvest::xml($xmlString); // XML $redirect = Harvest::redirect('/some/other/path'); // Redirect $file = Harvest::stream('/path/to/file'); // Stream $resource = Harvest::stream(Harvest::createStreamFromResource($resource)); // Stream $generator = Harvest::generator(function() { yield 'Custom content'; yield ' can be written'; yield ' from a generator'; }, 200, [ 'Content-Type' => 'text/plain' ]);
Licensing
Harvest is licensed under the MIT License. See LICENSE for the full license text.