Middleware Pipeline a PSR-15
Detailnější průvodce middleware v Petrovo CMS a jak je psát.
Middleware Structure
Middleware v Petrovo CMS je PHP closure/function s signaturam:
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
/**
* @param ServerRequestInterface $request PSR-7 ServerRequest
* @param RequestHandlerInterface $handler Next middleware/handler interface
* @return ResponseInterface PSR-7 Response
*/
$middleware = function($request, $handler) {
// Logika PŘED dalším middleware
$method = $request->getMethod();
$path = $request->getUri()->getPath();
error_log("→ $method $path");
// Zavolá další middleware v pipeline
$response = $handler->handle($request);
// Logika PO dalším middleware (úprava response)
$response = $response->withHeader('X-Response-Time', microtime(true));
// Vrátí response zpět
return $response;
};
Poznámka: Middleware dostávají RequestHandlerInterface ($handler), ne prostou funkci. Voláš $handler->handle($request).
Middleware Pipelines
Petrovo CMS používá pojmenované pipeline pro různé typy požadavků:
Frontend Pipeline
Platí pro veřejné stránky (/ a /slug):
// app/routes/app-frontend.php
pipe(proxyMiddleware, 'frontend');
pipe(redirectionMiddleware, 'frontend');
pipe(defineWebMiddleware, 'frontend');
pipe(sanitizationMiddleware, 'frontend');
pipe(logoutMiddleware, 'frontend');
pipe(loginMiddleware, 'frontend');
pipe(csrfMiddleware, 'frontend');
pipe(settingMiddleware, 'frontend');
pipe(headersMiddleware, 'frontend');
Pořadí: Statické soubory → Proxy → Přesměrování → Kontex webu → Sanitizace → Logout → Login → CSRF → Nastavení → Headers
Admin Pipeline
Platí pro /ADMIN a admin panel:
// app/routes/app-backend.php
pipe(proxyMiddleware, 'admin');
pipe(sessionExpirationAdminMiddleware, 'admin');
pipe(defineWebMiddleware, 'admin');
pipe(adminAjaxDataMiddleware, 'admin');
pipe(adminCubeDataMiddleware, 'admin');
pipe(sanitizationMiddleware, 'admin');
pipe(logoutAdminMiddleware, 'admin');
pipe(loginAdminMiddleware, 'admin');
pipe(loggingAdminMiddleware, 'admin');
pipe(settingMiddleware, 'admin');
pipe(headersMiddleware, 'admin');
Admin Tools Pipeline
Lehčí pro soubory a utility:
pipe(proxyMiddleware, 'adminTools');
pipe(sessionExpirationAdminMiddleware, 'adminTools');
pipe(defineWebMiddleware, 'adminTools');
pipe(loginAdminMiddleware, 'adminTools');
pipe(loggingAdminMiddleware, 'adminTools');
pipe(settingMiddleware, 'adminTools');
Data API Pipeline
Pro vnitřní API (/getData):
// app/routes/data.php
pipe(proxyMiddleware, 'data');
pipe(defineWebMiddleware, 'data');
pipe(sanitizationMiddleware, 'data');
pipe(corsMiddleware, 'data');
Důvod: DATA API potřebuje CORS, sanitizaci, ale ne přihlašování.
RUN Pipeline
Pro dynamické tagy a akce (/RUN):
// app/routes/run.php
pipe(proxyMiddleware, 'run');
pipe(defineWebMiddleware, 'run');
pipe(sanitizationMiddleware, 'run');
pipe(csrfMiddleware, 'run');
pipe(corsMiddleware, 'run');
pipe(settingMiddleware, 'run');
Psaní vlastního middleware
Jednoduché middleware - logging
// app/Middlewares/loggingMiddleware.php
$loggingMiddleware = function($request, $handler) {
$method = $request->getMethod();
$uri = $request->getUri();
error_log("Request: $method $uri");
$response = $handler($request);
error_log("Response: " . $response->getStatusCode());
return $response;
};
Middleware s podmínkou - autentifikace
// app/Middlewares/authMiddleware.php
use function Petrovo\Http\text;
$authMiddleware = function($request, $handler) {
// Extrahuj token z Authorization headeru
$authHeader = $request->getHeaderLine('Authorization');
if (!$authHeader || !str_starts_with($authHeader, 'Bearer ')) {
return text('Unauthorized', 401);
}
$token = substr($authHeader, 7); // Remove 'Bearer '
if (!validateToken($token)) {
return text('Invalid token', 401);
}
// Autentifikace OK - pokračuj do dalšího middleware/handleru
return $handler->handle($request);
};
Middleware s atributy - přidá data do request
// app/Middlewares/userMiddleware.php
$userMiddleware = function($request, $handler) {
$userId = Session::get('user_id');
if ($userId) {
$user = DB::row("SELECT * FROM users WHERE id = ?", [$userId]);
// Přidá atribut do request (pro další middleware)
$request = $request->withAttribute('user', $user);
}
return $handler($request);
};
Middleware s úpravou response - přidá header
// app/Middlewares/securityHeadersMiddleware.php
$securityHeadersMiddleware = function($request, $handler) {
$response = $handler($request);
return $response
->withHeader('X-Content-Type-Options', 'nosniff')
->withHeader('X-Frame-Options', 'SAMEORIGIN')
->withHeader('X-XSS-Protection', '1; mode=block');
};
Pipeline execution
Middleware se spouští v pořadí registrace:
Request
↓
[Middleware 1] START
↓
[Middleware 2] START
↓
[Middleware 3] START
↓
[Handler] - vrátí Response
↓
[Middleware 3] END - upraví response
↓
[Middleware 2] END - upraví response
↓
[Middleware 1] END - upraví response
↓
Response
Konkreetní příklad: Frontend request
GET /some-page
↓
proxyMiddleware - Detekuje proxy, nastaví remote IP
↓
redirectionMiddleware - Přesměruje staré URLs
↓
defineWebMiddleware - Nastaví WEB ID a kontext
↓
sanitizationMiddleware - Očistí GET/POST data
↓
logoutMiddleware - Zpracuje logout session
↓
loginMiddleware - Zpracuje login přihlášení
↓
csrfMiddleware - Ověří CSRF token
↓
settingMiddleware - Načte nastavení z DB
↓
headersMiddleware - Nastaví response headers
↓
app/Controllers/slug.php (Handler)
↓
Response (s headers, body, status)
↓
Browser
Důležité notes
- Pořadí je kritické - middleware se spouští v pořadí registrace
- Chyby v middleware - pokud middleware vyhodí výjimku, pipeline se zastaví
- Request je immutable - PSR-7 objekty jsou immutable, musíš usar
withAttribute(),withHeader(), atd. - Handler je volitelný - middleware může vrátit response bez volání
$handler($request) - Response objekty - vždy vrať validní Response objekt (nebo
exit)
Kdy psát middleware?
Middleware je vhodný pro:
- Globální validace/sanitizace
- Autentifikaci a autorizaci
- Setování headers (CORS, security headers)
- Requestu modifikace (přidání atributů)
- Loggování/monitorování
Middleware NENÍ vhodný pro:
- Business logiku (je to v Controller)
- Databázový query (je to v Model/Endpoint)
- Template rendering (je to v Template layer)