Skip to content

MiddlewarePipeline

MiddlewarePipeline builds and caches the PSR-15 middleware chain; safe for worker reuse.

Worker reuse is the part with teeth for anyone writing a middleware: the stack is built once per worker process and every instance in it then serves every request that worker handles, however many users those come from. A middleware is therefore process-scoped, not request-scoped, whatever the usual PSR-15 mental model suggests — so a $this->currentUser = $request-> getAttribute(...), or a $this->cached ??= lookup() memo of anything user-specific, is read back by the next request on that worker and hands one caller another caller’s data.

Keep request-scoped values on the request’s attribute bag or resolve them per call from the container; reserve instance properties for what is genuinely process-wide (config, a shared connection, a compiled table). A middleware that must hold per-request state anyway can implement ResetInterface, and MiddlewarePipeline::resetInstances() clears it at every request boundary.

class MiddlewarePipeline implements RequestHandlerInterface

ImplementsRequestHandlerInterface
SourceMiddleware/MiddlewarePipeline.php

public function __construct(Context $context): mixed

ParameterTypeDescription
$contextContext

Returns mixed

MethodDescription
coreMiddlewareClasses(): list<class-string<MiddlewareInterface>>The framework’s own shipped middleware classes.
debugStack(): list<string>
guardedMiddlewareClasses(): list<string>The full set MiddlewareConfigRegistry guards against silent config-driven reordering or disabling.
handle(ServerRequestInterface $request): ResponseInterfaceRuns the request through the middleware stack, building the stack first if needed.
protectedPackageMiddlewareClasses(): list<string>First-party middleware that ships in its own package rather than being built by core.
reset(): voidDiscards the cached stack so the next MiddlewarePipeline::handle() rebuilds it.
resetInstances(): voidClears the per-request state of every middleware in the built stack that declares any, by calling ResetInterface::reset() on it.

public static function coreMiddlewareClasses(): list<class-string<MiddlewareInterface>>

The framework’s own shipped middleware classes.

Returns list``<``class-string``<MiddlewareInterface>``>

public function debugStack(): list<string>

Returns list``<``string``>

public static function guardedMiddlewareClasses(): list<string>

The full set MiddlewareConfigRegistry guards against silent config-driven reordering or disabling.

Returns list``<``string``>

public function handle(ServerRequestInterface $request): ResponseInterface

Runs the request through the middleware stack, building the stack first if needed.

The built stack is cached on the instance for the life of the worker, so only the first request pays for attribute scanning and order resolution; call MiddlewarePipeline::reset() to force a rebuild.

ParameterTypeDescription
$requestServerRequestInterface

Returns ResponseInterface

ThrowsWhen
QuioteExceptionIf building the stack produced no handler.

public static function protectedPackageMiddlewareClasses(): list<string>

First-party middleware that ships in its own package rather than being built by core.

Returns list``<``string``>

public function reset(): void

Discards the cached stack so the next MiddlewarePipeline::handle() rebuilds it.

Needed whenever the inputs to the build have changed — a catalog registration, a middleware config entry, an enable/disable override — since the stack is otherwise kept for the worker’s lifetime. The middleware instances themselves are dropped, not reset.

public function resetInstances(): void

Clears the per-request state of every middleware in the built stack that declares any, by calling ResetInterface::reset() on it.

The stack itself is kept — this is the request boundary, not a rebuild.

Run for each request in a persistent worker, where the instances outlive the request that populated them. A middleware that keeps nothing between calls (the norm, and what MiddlewarePipeline asks for) implements nothing and is skipped.