Skip to content

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

  1. Pořadí je kritické - middleware se spouští v pořadí registrace
  2. Chyby v middleware - pokud middleware vyhodí výjimku, pipeline se zastaví
  3. Request je immutable - PSR-7 objekty jsou immutable, musíš usar withAttribute(), withHeader(), atd.
  4. Handler je volitelný - middleware může vrátit response bez volání $handler($request)
  5. 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)