viewDir = dirname(__DIR__) . '/src/Views'; // Session-based flash messages if (session_status() === PHP_SESSION_NONE) { session_start(); } if (isset($_SESSION['flash'])) { $this->flash = $_SESSION['flash']; unset($_SESSION['flash']); } } public function render(string $template, array $data = []): void { $data['flash'] = $this->flash; $data['currentPath'] = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); extract($data, EXTR_SKIP); include $this->viewDir . '/' . $template; } public static function redirect(string $path, string $message = '', string $type = 'success'): void { if (session_status() === PHP_SESSION_NONE) { session_start(); } if ($message !== '') { $_SESSION['flash'][] = ['type' => $type, 'message' => $message]; } header('Location: ' . $path); exit; } public static function h(mixed $value): string { return htmlspecialchars((string)$value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } public static function formatMoney(mixed $value): string { return number_format((float)$value, 2, ',', '.') . ' €'; } public static function formatDate(?string $date): string { if (!$date) return '–'; $d = \DateTime::createFromFormat('Y-m-d', $date); return $d ? $d->format('d.m.Y') : $date; } }