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 pojmenovaná namespace funkce (App\Middlewares\*) se
signaturou ($request, $handler): ResponseInterface:
// app/Middlewares/Log/loggingMiddleware.php
declare(strict_types=1);
namespace App\Middlewares;
use Psr\Http\Message\ResponseInterface;
function loggingMiddleware($request, $handler): ResponseInterface
{
// 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', (string) microtime(true));
// Vrátí response zpět
return $response;
}
Poznámky:
- Middleware dostávají
RequestHandlerInterface($handler), ne prostou funkci. Voláš$handler->handle($request). - Návratový typ je vždy
ResponseInterface(PSR-7 rozhraní), ne konkrétníHttpSoft\Message\Response—$handler->handle()vrací rozhraní a middleware musí zůstat kompatibilní. - Každý middleware soubor se načítá přes composer
files(vizcomposer.json) — tím se funkce zaregistruje globálně. - Historicky (Siler) byly middleware closury registrované přes
define('x', $x). Dnes jsou to normální funkce.
Registrace do pipeline¶
Middleware se registruje přes pipe(). Protože jsou to pojmenované funkce, importuješ je
a předáváš jako first-class callable middleware(...):
use function Core\Middleware\pipe;
use function App\Middlewares\{proxyMiddleware, csrfMiddleware, headersMiddleware};
pipe(proxyMiddleware(...), 'website'); // (...) vyrobí Closure z funkce, NEvolá ji
pipe(csrfMiddleware(...), 'website');
pipe(headersMiddleware(...), 'website');
Zápis middleware(...) nic nespustí — vytvoří callable, který pipe() uloží a zavolá
teprve při zpracování requestu.
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/website.php
pipe(proxyMiddleware(...), 'website');
pipe(csrfMiddleware(...), 'website');
pipe(authMiddleware(...), 'website');
pipe(ipBlacklistMiddleware(...), 'website');
pipe(headersMiddleware(...), 'website');
Admin Pipeline¶
Platí pro /ADMIN a admin panel:
// app/routes/admin.php
pipe(proxyMiddleware(...), 'admin');
pipe(sessionExpirationAdminMiddleware(...), 'admin');
pipe(csrfAdminMiddleware(...), 'admin');
pipe(authAdminMiddleware(...), 'admin');
pipe(permissionAdminMiddleware(...), 'admin');
pipe(loggingAdminMiddleware(...), 'admin');
pipe(ipBlacklistMiddleware(...), 'admin');
pipe(headersMiddleware(...), 'admin');
Admin Tools Pipeline¶
Lehčí pro soubory a utility:
pipe(proxyMiddleware(...), 'adminTools');
pipe(sessionExpirationAdminMiddleware(...), 'adminTools');
pipe(authAdminMiddleware(...), 'adminTools');
pipe(permissionAdminMiddleware(...), 'adminTools');
pipe(loggingAdminMiddleware(...), 'adminTools');
pipe(ipBlacklistMiddleware(...), 'adminTools');
RUN Pipeline¶
Pro dynamické tagy a akce (/RUN):
// app/routes/run.php
pipe(proxyMiddleware(...), 'run');
pipe(csrfMiddleware(...), 'run');
pipe(corsMiddleware(...), 'run');
pipe(ipBlacklistMiddleware(...), 'run');
RUN admin pipeline¶
pipe(proxyMiddleware(...), 'run-admin');
pipe(sessionExpirationAdminMiddleware(...), 'run-admin');
pipe(csrfAdminMiddleware(...), 'run-admin');
pipe(authAdminMiddleware(...), 'run-admin');
pipe(corsMiddleware(...), 'run-admin');
pipe(ipBlacklistMiddleware(...), 'run-admin');
Psaní vlastního middleware¶
Každý příklad je soubor v app/Middlewares/, namespace App\Middlewares;, a musí být
uveden v composer.json v poli files, aby se funkce načetla.
Jednoduché middleware - logging¶
// app/Middlewares/Log/loggingMiddleware.php
namespace App\Middlewares;
use Psr\Http\Message\ResponseInterface;
function loggingMiddleware($request, $handler): ResponseInterface
{
$method = $request->getMethod();
$uri = $request->getUri();
error_log("Request: $method $uri");
$response = $handler->handle($request);
error_log("Response: " . $response->getStatusCode());
return $response;
}
Middleware s podmínkou - autentifikace¶
// app/Middlewares/Auth/authMiddleware.php
namespace App\Middlewares;
use Psr\Http\Message\ResponseInterface;
use function Core\Http\text;
function authMiddleware($request, $handler): ResponseInterface
{
// 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/Session/userMiddleware.php
namespace App\Middlewares;
use Psr\Http\Message\ResponseInterface;
function userMiddleware($request, $handler): ResponseInterface
{
$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->handle($request);
}
Middleware s úpravou response - přidá header¶
// app/Middlewares/Response/securityHeadersMiddleware.php
namespace App\Middlewares;
use Psr\Http\Message\ResponseInterface;
function securityHeadersMiddleware($request, $handler): ResponseInterface
{
$response = $handler->handle($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
↓
csrfMiddleware - Ověří CSRF token
↓
authMiddleware - Načte uživatele / auth kontext
↓
ipBlacklistMiddleware - Zablokuje blacklistované IP
↓
headersMiddleware - Nastaví response headers
↓
app/website/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->handle($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)