$error, 'success' => $success]; } // ── Redirect ────────────────────────────────────────────────────────────── /** * Flash a message and redirect. Terminates the script. */ public static function redirect(string $url, ?string $success = null, ?string $error = null): never { if ($success !== null) { self::flash('success', $success); } if ($error !== null) { self::flash('error', $error); } header('Location: ' . $url); exit; } // ── Template rendering ──────────────────────────────────────────────────── /** * Render a full page: head → header → content template → footer. * * Expects $vars to contain the same keys the templates already rely on * ($pageTitle, $bodyClass, $isAdmin, $extraCss, $ogTags, etc.). * The footer variant (public vs admin) is chosen automatically based * on $isAdmin. * * @param string $template Path relative to APP_ROOT/templates/ * @param array $vars Variables to expose inside the templates */ public static function render(string $template, array $vars = []): void { // Make all vars available in the template scope. extract($vars); include APP_ROOT . '/templates/head.php'; include APP_ROOT . '/templates/header.php'; include APP_ROOT . '/templates/' . $template; if (!empty($isAdmin)) { include APP_ROOT . '/templates/admin/footer.php'; } else { include APP_ROOT . '/templates/footer.php'; } } }