Middleware
Middleware contracts, pořadí zpracování, zásahy do request/response flow a pravidla kompozice.
PSR-15 runtime
Middleware runtime is PSR-15 based. Every middleware must implement Psr\Http\Server\MiddlewareInterface and either return a response or delegate to the next handler.
How the runtime executes
Framework::run() gets global middleware class names from MiddlewareStack, resolves them through MiddlewareResolver, builds MiddlewarePipeline, and uses DispatchRequestHandler as fallback request handler.
$stack = $this->container->get(MiddlewareStack::class);
$middleware = $this->container
->get(MiddlewareResolver::class)
->resolve($stack->all());
$pipeline = MiddlewarePipeline::create(
$middleware,
$this->container->get(DispatchRequestHandler::class),
);
Global middleware stack
HttpServiceProvider registers a default global stack. Current default order: RequestLoggingMiddleware, BenchmarkMiddleware, ErrorHandlingMiddleware, CorsMiddleware, PoweredByMiddleware, HtmlMinifyMiddleware, OptionsMiddleware.
Configuring global middleware
Application code can extend or reorder global middleware through Framework::middleware() and MiddlewareStack operations (add, prepend, insertBefore, insertAfter, remove).
$framework->middleware(function (MiddlewareStack $stack): void {
$stack->prepend(App\Http\Middleware\AuthMiddleware::class);
$stack->insertAfter(
Lemonade\Framework\Http\Middleware\CorsMiddleware::class,
App\Http\Middleware\RequestIdMiddleware::class,
);
});
Middleware resolver
MiddlewareResolver converts middleware class definitions to container instances and enforces MiddlewareInterface. Invalid entries throw RuntimeException.
$resolved = [];
foreach ($middlewareClasses as $middlewareClass) {
$middleware = $this->container->get($middlewareClass);
if (!$middleware instanceof MiddlewareInterface) {
throw new RuntimeException(sprintf(
'Target class "%s" is not a valid middleware.',
$middlewareClass,
));
}
$resolved[] = $middleware;
}
Route-specific middleware
After route match, DispatchRequestHandler resolves middleware classes from RouteMatch::middleware() via container, checks MiddlewareInterface, then wraps ControllerRequestHandler with route middleware pipeline.
$match = $this->router->match($request);
$handler = new ControllerRequestHandler(
resolver: $this->resolver,
match: $match,
);
return MiddlewarePipeline::create($middleware, $handler)
->handle($request);
Middleware pipeline behavior
MiddlewarePipeline is immutable and index-based. Each call to process() gets a new handler pointing to the next middleware. When the stack ends, fallback handler continues into request dispatch flow.
Example middleware
Custom middleware should be small and focused. Authentication/authorization is a common route or global concern.
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
final class AuthMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler,
): ResponseInterface {
// Perform authentication / authorization here.
return $handler->handle($request);
}
}
Typed middleware definitions
Route and group middleware definitions are class-string references in routing API. Runtime resolves them from container; no dynamic middleware method lookup is used.
Runtime boundary
Global middleware wraps the whole request lifecycle first. Route middleware then wraps matched controller dispatch. Final response flows back through both pipelines in reverse order.