Middleware Stack
Phexium uses PSR-15 compliant middleware for request/response processing. Middleware is registered in bootstrap.php and executes in LIFO order (last added = first executed).
PSR-15 Compatibility
All middleware implements Psr\Http\Server\MiddlewareInterface with its process() method signature. This ensures interoperability with any PSR-15 compliant framework or library.
Middleware Order
The order of middleware registration determines execution flow. Slim uses LIFO (Last In, First Out), meaning the last middleware added executes first on incoming requests.
Request/Response Flow
HTTP Request
↓
1. ErrorMiddleware (outer - catches all exceptions)
↓
2. ContentLengthMiddleware (adds Content-Length header on response)
↓
3. UserContextMiddleware (demo only - loads user context)
↓
4. SessionMiddleware (starts/saves session)
↓
5. BodyParsingMiddleware (parses JSON/form/XML)
↓
Route Handler + Route-specific middleware (RBAC)
↓
Response bubbles back up through the stack
Registration Order in Code
In bootstrap.php, middleware is added in reverse execution order:
$app->addBodyParsingMiddleware(); // 5. Innermost (last to execute on request)
$app->add(SessionMiddleware::class); // 4.
$app->add(UserContextMiddleware::class); // 3. Demo application only
$app->add(Whoops::class); // 2. Error handler
$app->add(ContentLengthMiddleware::class); // 1. Outermost
Why Order Matters
| Middleware | Position | Reason |
|---|---|---|
| ErrorMiddleware | Outermost | Catches exceptions from all inner middleware |
| ContentLengthMiddleware | Early | Needs complete response body to calculate length |
| UserContextMiddleware | Before routes | Routes need user context for RBAC |
| SessionMiddleware | Before user context | User context reads from session |
| BodyParsingMiddleware | Innermost | Handlers need parsed body data |
Demo vs Starter Configuration
The demo application includes UserContextMiddleware for authenticated user context, while the starter template omits it:
Demo application (config/demo/bootstrap.php):
$app->add(SessionMiddleware::class);
$app->add(UserContextMiddleware::class); // Loads authenticated user
Starter application (config/starter/bootstrap.php):
$app->add(SessionMiddleware::class);
// No UserContextMiddleware - add when implementing authentication
Creating Middleware
final readonly class MyMiddleware implements MiddlewareInterface
{
#[Override]
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// Before: modify request
$request = $request->withAttribute('custom', 'value');
// Call next middleware
$response = $handler->handle($request);
// After: modify response
return $response->withHeader('X-Custom', 'value');
}
}
Global vs Route Middleware
Global Middleware
Applied to all routes via $app->add():
Route Middleware
Applied to specific routes:
Short-Circuiting
Middleware can return a response without calling the next handler:
if (!$this->isAuthorized($request)) {
return $this->responseFactory->createResponse(403);
}
return $handler->handle($request);
See Also
- RBAC Permissions - Route protection middleware
- Authentication - UserContextMiddleware
- Acceptance Testing (Behat) - Middleware testing
- PSR-15 HTTP Server Middleware - Interface specification