PHPackages                             decodelabs/harvest - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. decodelabs/harvest

ActiveLibrary[HTTP &amp; Networking](/categories/http)

decodelabs/harvest
==================

PSR-15 HTTP stack without the mess

v0.7.3(6mo ago)02.8k16MITPHPPHP ^8.4CI passing

Since Oct 31Pushed 5mo ago2 watchersCompare

[ Source](https://github.com/decodelabs/harvest)[ Packagist](https://packagist.org/packages/decodelabs/harvest)[ RSS](/packages/decodelabs-harvest/feed)WikiDiscussions develop Synced 1mo ago

READMEChangelog (10)Dependencies (23)Versions (59)Used By (6)

Harvest
=======

[](#harvest)

[![PHP from Packagist](https://camo.githubusercontent.com/5f6971f373be9f2a12b8f78cf1518ba45737a7ace0d6e82dfcb98dd4352983d7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6465636f64656c6162732f686172766573743f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/harvest)[![Latest Version](https://camo.githubusercontent.com/a0a2d19810cd95ca692aa2902577c96618bdb279122ba385afe189fb2084f22e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6465636f64656c6162732f686172766573742e7376673f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/harvest)[![Total Downloads](https://camo.githubusercontent.com/73d4db2af024b372dd3b5604fec719d781fd4cb2e22bba6eb12e50e202a49358/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6465636f64656c6162732f686172766573742e7376673f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/harvest)[![GitHub Workflow Status](https://camo.githubusercontent.com/9e0c55cc8c319133b50e644aeba7bf64d28cd98ac35abc014700d66519dd2558/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6465636f64656c6162732f686172766573742f696e746567726174652e796d6c3f6272616e63683d646576656c6f70)](https://github.com/decodelabs/harvest/actions/workflows/integrate.yml)[![PHPStan](https://camo.githubusercontent.com/e25c14ce011edabdd0fbd2e10415b41cc5d66ed11ef3e5b7edd074c5bdd35a2d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d656e61626c65642d3434434331312e7376673f6c6f6e6743616368653d74727565267374796c653d666c6174)](https://github.com/phpstan/phpstan)[![License](https://camo.githubusercontent.com/9ff8173858d94db2cdf6419a61d61ff6148882506c43c76f4d6aace3a32c5e56/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6465636f64656c6162732f686172766573743f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/harvest)

### PSR-15 HTTP stack without the mess

[](#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
------------

[](#installation)

This package requires PHP 8.4 or higher.

Install via Composer:

```
composer require decodelabs/harvest
```

Usage
-----

[](#usage)

Harvest provides the full PSR-15 stack, including Request, Response, Middleware and Handler interfaces. The root of the system is the `Dispatcher` which takes a `Request` and a Middleware `Profile` and returns a `Response`.

The `Profile` defines what Middleware is used to process the request and how it is ordered.

```
use DecodeLabs\Harvest;
use DecodeLabs\Harvest\Dispatcher;
use DecodeLabs\Harvest\Middleware\ContentSecurityPolicy;
use DecodeLabs\Harvest\Profile;
use DecodeLabs\Harvest\Response\Text as TextResponse;
use DecodeLabs\Monarch;

// Create a Middleware Profile
$profile = new Profile(
    '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 new TextResponse('Hello World!');
    }
);

// Create a Dispatcher
$dispatcher = new Dispatcher($profile);
$harvest = Monarch::getService(Harvest::class);

$request = $harvest->createRequestFromEnvironment();
$response = $dispatcher->dispatch($request);
```

String names passed to a `Profile` will resolve via `Slingshot`, allowing for easy dependency injection and container resolution.

#### Ordering

[](#ordering)

Middleware is sorted by a dual level priority system, first grouped by the *intent* of the Middleware, in this order:

- **ErrorHandler** - catches and handles errors within the stack
- **Inbound** - processes the request before it is passed to the next Middleware
- **Outbound** - processes the response before it is sent to the client
- **Generic** - generic Middleware that does not fit into the above categories
- **Generator** - generates or loads the primary response content

Then each group is sorted by the *priority* of the Middleware, with lower numbers being higher in the group. Harvest Middleware implement an extension to the Psr Middleware interface, defining defaults for group and priority.

These can be overridden when defining your `Profile`:

```
use DecodeLabs\Harvest\Profile;

$profile = new Profile()
    ->add('Cors', priority: 5, group: 'Generic')
    ->add(new SomeOtherVendorMiddleware(), priority: 10, group: 'Inbound');
```

### Fibers

[](#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 unwieldy 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.

### ResponseHandler and Transports

[](#responsehandler-and-transports)

Once a Response has been generated, you can then use an instance of a Harvest `ResponseHandler` and `Transport` to prepare and send it to the client.

Harvest currently provides a generic `Native` Transport implementation that uses PHP's built in header and output stream functions.

```
use DecodeLabs\Harvest\ResponseHandler;
use DecodeLabs\Harvest\Transport\Native as NativeTransport;

$handler = new ResponseHandler(
    new NativeTransport()
);

$handler->sendResponse(
    $request,
    $response
);

exit;
```

### Responses

[](#responses)

Harvest provides easy shortcuts for creating Responses:

```
use DecodeLabs\Harvest\Response\Text as TextResponse;
use DecodeLabs\Harvest\Response\Html as HtmlResponse;
use DecodeLabs\Harvest\Response\Json as JsonResponse;
use DecodeLabs\Harvest\Response\Xml as XmlResponse;
use DecodeLabs\Harvest\Response\Redirect as RedirectResponse;
use DecodeLabs\Harvest\Response\Stream as StreamResponse;
use DecodeLabs\Harvest\Response\Generator as GeneratorResponse;

$text = new TextResponse('Hello World!'); // Text

$customText = new TextResponse('Hello World!', 201, [
    'Custom-Header' => 'header-value'
]);

$html = new HtmlResponse('Hello World!'); // HTML

$json = new JsonResponse([
    'whatever-data' => 'Hello World!'
]); // JSON

$xml = new XmlResponse($xmlString); // XML

$redirect = new RedirectResponse('/some/other/path'); // Redirect

$file = new StreamResponse('/path/to/file'); // Stream

$resource = new StreamResponse(
    $harvest->createStreamFromResource($resource)
); // Stream

$generator = new GeneratorResponse(function() {
    yield 'Custom content';
    yield ' can be written';
    yield ' and streamed';
    yield ' from a generator';
}, 200, [
    'Content-Type' => 'text/plain'
]);
```

### Cookies

[](#cookies)

Harvest provides a `Cookies` Middleware and a global `Cookie Collection` that allows you to define request-level cookies separately from the response generation process and merges them into the response. Just make sure the Cookie Middleware is added to your `Profile`.

```
$profile->add('Cookies');

$harvest->cookies->set(
    name: 'cookie-name',
    value: 'cookie-value',
    domain: 'example.com',
    path: '/',
    expires: '10 minutes',
    maxAge: 600,
    httpOnly: true,
    secure: true,
    sameSite: 'Strict',
    partitioned: true
);

$harvest->cookies->expire(
    name: 'cookie-name',
    domain: 'example.com',
    path: '/',
);
```

Licensing
---------

[](#licensing)

Harvest is licensed under the MIT License. See [LICENSE](./LICENSE) for the full license text.

###  Health Score

45

—

FairBetter than 93% of packages

Maintenance69

Regular maintenance activity

Popularity19

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 99.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~13 days

Recently: every ~26 days

Total

57

Last Release

186d ago

PHP version history (3 changes)v0.1.0PHP ^8.0

v0.2.0PHP ^8.1

v0.4.0PHP ^8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/8a241d64d12b3b5ee94197862ec1ec30b82ed2efa34a0cd7f4c3565a021daddd?d=identicon)[betterthanclay](/maintainers/betterthanclay)

---

Top Contributors

[![betterthanclay](https://avatars.githubusercontent.com/u/1273586?v=4)](https://github.com/betterthanclay "betterthanclay (247 commits)")[![szepeviktor](https://avatars.githubusercontent.com/u/952007?v=4)](https://github.com/szepeviktor "szepeviktor (2 commits)")

---

Tags

httpphppsr-15psr-7

### Embed Badge

![Health badge](/badges/decodelabs-harvest/health.svg)

```
[![Health](https://phpackages.com/badges/decodelabs-harvest/health.svg)](https://phpackages.com/packages/decodelabs-harvest)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[thecodingmachine/graphqlite

Write your GraphQL queries in simple to write controllers (using webonyx/graphql-php).

5723.1M30](/packages/thecodingmachine-graphqlite)[neos/flow

Flow Application Framework

862.0M451](/packages/neos-flow)[neos/flow-development-collection

Flow packages in a joined repository for pull requests.

144179.3k3](/packages/neos-flow-development-collection)[mezzio/mezzio-authentication-oauth2

OAuth2 (server) authentication middleware for Mezzio and PSR-7 applications.

28483.0k2](/packages/mezzio-mezzio-authentication-oauth2)[windwalker/framework

The next generation PHP framework.

25639.1k1](/packages/windwalker-framework)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
