# Posterg: Refactoring Recommendations Concrete improvements to the separation between templating, routing, and backend logic — staying in PHP, no framework required. --- ## 1. Extract a Micro-Router + Middleware Pipeline ### Problem Every file repeats the same preamble. The 7 action handlers and 17 page controllers all independently: ```php require_once __DIR__ . '/../../config/bootstrap.php'; require_once __DIR__ . '/../../src/AdminAuth.php'; AdminAuth::requireLogin(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } require_once __DIR__ . '/../../src/Database.php'; ``` This is ~6-8 identical lines per file × 24 files = ~170 lines of pure duplication. When the CSRF check pattern changes, every action handler must be updated in lockstep. ### Solution Create `src/App.php` — a thin request dispatcher with middleware hooks: ```php // src/App.php class App { private static bool $booted = false; /** Boot once per request: load Database, ensure CSRF token exists. */ public static function boot(): Database { if (!self::$booted) { require_once APP_ROOT . '/src/Database.php'; self::$booted = true; } if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return Database::getInstance(); } /** Gate for admin pages: auth + CSRF token. */ public static function adminGuard(): Database { require_once APP_ROOT . '/src/AdminAuth.php'; AdminAuth::requireLogin(); return self::boot(); } /** Validate CSRF on POST. Call at the top of every action handler. */ public static function verifyCsrf(): void { if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['csrf_token'], $_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { http_response_code(403); exit('CSRF token invalide.'); } } /** Regenerate CSRF after a successful mutation. */ public static function rotateCsrf(): void { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } /** Flash a message into the session and redirect. */ public static function redirect(string $url, ?string $success = null, ?string $error = null): never { if ($success) $_SESSION['success'] = $success; if ($error) $_SESSION['error'] = $error; header('Location: ' . $url); exit; } } ``` Every admin page becomes: ```php `, then 200+ lines of HTML, then 30+ lines of inline `