Skip to content

Usage

Constructor

final class GuardMiddleware implements MiddlewareInterface
{
    public function __construct(
        GuardEngine $engine,
        ResponseFactoryInterface $responseFactory,
        StreamFactoryInterface $streamFactory
    ) { ... }
}

GuardMiddleware implements PSR-15 MiddlewareInterface. The constructor calls $engine->initialize(); a GuardRedisException is swallowed only when SecurityConfig.redisFailOpen is true, otherwise construction fails closed by rethrowing. The two PSR-17 factories build the PSR-7 block responses; pass your own implementation (Nyholm, Guzzle, Laminas, Slim) so no PSR-7 package is hardwired.

Attachment

Any PSR-15 stack:

$app->add($guard);                       // Slim 4
$pipeline->pipe($guard);                 // Mezzio /PSR-15 pipeline
$response = $guard->process($request, $handler);

On a block verdict the downstream handler is never called. On a clean pass the ORIGINAL PSR-7 request is forwarded untouched.

Verdicts

When the engine returns a block verdict, ResponseTranslator copies the verdict status code, body, and headers onto a PSR-7 response, and never calls the wrapped handler:

Situation Status Body
Banned IP 403 IP address banned
Suspicious content 400 Suspicious activity detected
Rate limit exceeded 429 Too many requests plus Retry-After
Engine malfunction 500 Security check failed

Bodies can be overridden globally through SecurityConfig.customErrorResponses.

Fail-closed behavior

If the engine check throws, the middleware catches it, and responds with the engine's fail-closed response (500 Security check failed) rather than letting the request through. A custom 500 body comes from customErrorResponses[500], not from adapter code.

Route-scoped configuration

The engine supports per-route RouteConfig (required headers, per-route rate limits, bypassed checks) keyed by a route ID on the request state. This adapter builds the engine request internally, so there is no route-ID hook; the engine-sanctioned equivalent is the customRequestCheck config closure, which runs as the last pipeline check and can return a block verdict for any request shape. See the advanced example app for an admin gate built that way.

Testing your integration

The package ships its own suite (composer test); for your app, assert that a blacklisted IP gets a 403 with the engine body and that clean requests reach your handler with the original PSR-7 instance.