cURLpit

Curlpit is a PSR-15 middleware orchestrator for PHP – with branching, looping, and declarative flow control built in.

Most middleware stacks are pipelines: request goes in, response comes out, linearly. Curlpit treats the middleware stack as an instruction sequence – with a program counter, named labels, conditional jumps, and loops. Business logic that would otherwise be hardcoded in handlers can be expressed as configuration.

Installation

composer require curlpit/curlpit

Then install a PSR-7/17 implementation of your choice:

# nyholm/psr7 (recommended – lightweight, zero dependencies)
composer require nyholm/psr7 nyholm/psr7-server

# or guzzlehttp/psr7
composer require guzzlehttp/psr7

How it differs

  Standard PSR-15 Curlpit
Execution Linear chain Instruction sequence (program counter)
Branching None JumpMiddleware (conditional goto)
Looping None LoopMiddleware + LoopContext
State Informal Explicit Set/Get/Inc/Dec middleware
Error handling Manual Built-in ErrorHandlerMiddleware
Config Code only JSON-declarable

Quick start

Extend Application, override instantiate() to wire up your dependencies, point it at a middleware.json:

use Curlpit\App\Application;
use Curlpit\Core\Emitter;

class MyApp extends Application
{
    protected function instantiate(string $class, array $options): MiddlewareInterface
    {
        return match ($class) {
            MyMiddleware::class => new MyMiddleware($this->responseFactory),
            default             => parent::instantiate($class, $options),
        };
    }
}

$app      = new MyApp($responseFactory, $streamFactory);
$response = $app->handle($serverRequest);
(new Emitter())->emit($response);

Flow config (middleware.json)

{
  "middleware": [
    { "Curlpit\\Core\\Middleware\\ErrorHandlerMiddleware": { "debug": false } },
    { "My\\AuthMiddleware": {} },
    {
      "Curlpit\\Core\\Middleware\\JumpMiddleware": {
        "condition": { "type": "attr", "name": "user_role", "eq": "admin" },
        "jump_to_label": "admin"
      }
    },
    { "My\\PublicDispatch": {} },
    { "My\\AdminDispatch": { "label": "admin" } }
  ]
}

Condition DSL

{ "type": "always" }
{ "type": "never" }
{ "type": "attr",    "name": "status",   "eq":  "active" }
{ "type": "attr",    "name": "retries",  "lte": 3        }
{ "type": "context", "name": "has_more"                  }
{ "type": "context", "name": "count",    "gt":  0        }

Operators: eq, neq, gt, gte, lt, lte. Without an operator, truthy check.

Built-in middleware

Example project

DBCommander – a Norton Commander-style MySQL manager built on Curlpit.

License

MIT