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:
/**
* @param object $request PSR-7 ServerRequest
* @param callable $handler Next middleware v pipeline
* @return object PSR-7 Response
*/
$middleware = function($request, $handler) {
// Logika PŘED dalším middleware
$response = $handler($request); // Zavolá další middleware
// Logika PO dalším middleware
return $response;
};
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(staticBlockerMiddleware, 'frontend');
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');
pipe(finalHandler, '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');
pipe(finalHandler, '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
$authMiddleware = function($request, $handler) {
$token = $_GET['token'] ?? null;
if (!$token || !validateToken($token)) {
// Vrať error response
return Diactoros\text('Unauthorized', 401);
}
// Autentifikace OK, pokračuj
return $handler($request);
};
Middleware s atributy - přidá data do request
// app/Middlewares/userMiddleware.php
$userMiddleware = function($request, $handler) {
$userId = $_SESSION['user_id'] ?? null;
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
↓
staticBlockerMiddleware - Blokuje přístup k /vendor, atd.
↓
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
↓
finalHandler - Vrátí Response object
↓
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)