From 7aace2a5517f4a717267a5b89238b13cf5063695 Mon Sep 17 00:00:00 2001 From: Pontoporeia Date: Tue, 31 Mar 2026 23:06:45 +0200 Subject: [PATCH] Add refactoring recommendations for controller/template/routing separation --- REFACTORING_RECOMMENDATIONS.md | 615 +++++++++++++++++++++++++++++++++ TODO.md | 1 + 2 files changed, 616 insertions(+) create mode 100644 REFACTORING_RECOMMENDATIONS.md diff --git a/REFACTORING_RECOMMENDATIONS.md b/REFACTORING_RECOMMENDATIONS.md new file mode 100644 index 0000000..9fb73b8 --- /dev/null +++ b/REFACTORING_RECOMMENDATIONS.md @@ -0,0 +1,615 @@ +# 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 `