https://github.com/decodelabs/harvest
PSR-15 HTTP stack without the mess
https://github.com/decodelabs/harvest
http php psr-15 psr-7
Last synced: 12 months ago
JSON representation
PSR-15 HTTP stack without the mess
- Host: GitHub
- URL: https://github.com/decodelabs/harvest
- Owner: decodelabs
- License: mit
- Created: 2023-10-30T13:21:43.000Z (over 2 years ago)
- Default Branch: develop
- Last Pushed: 2025-04-14T08:47:57.000Z (12 months ago)
- Last Synced: 2025-04-15T14:07:28.363Z (12 months ago)
- Topics: http, php, psr-15, psr-7
- Language: PHP
- Homepage:
- Size: 165 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Harvest
[](https://packagist.org/packages/decodelabs/harvest)
[](https://packagist.org/packages/decodelabs/harvest)
[](https://packagist.org/packages/decodelabs/harvest)
[](https://github.com/decodelabs/harvest/actions/workflows/integrate.yml)
[](https://github.com/phpstan/phpstan)
[](https://packagist.org/packages/decodelabs/harvest)
### 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.
---
## Installation
Install via Composer:
```bash
composer require decodelabs/harvest
```
## Usage
Harvest provides the full PSR-15 stack, including Request, Response, Middleware and Handler interfaces.
```php
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:
```php
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.
```php
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:
```php
use DecodeLabs\Harvest;
$text = Harvest::text('Hello World!'); // Text
$customText = Harvest::text('Hello World!', 201, [
'Custom-Header' => 'header-value'
]);
$html = Harvest::html('
Hello World!
'); // 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](./LICENSE) for the full license text.