'; } function verify_csrf(): void { $submitted = $_POST['_csrf'] ?? ''; if (!is_string($submitted) || !hash_equals(csrf_token(), $submitted)) { http_response_code(403); render('errors/419', ['title' => '요청을 다시 확인해 주세요']); exit; } } function redirect(string $path, int $status = 302): never { header('Location: ' . url($path), true, $status); exit; } function render(string $view, array $data = [], string $layout = 'layouts/site'): void { $viewFile = dirname(__DIR__) . '/Views/' . $view . '.php'; $layoutFile = dirname(__DIR__) . '/Views/' . $layout . '.php'; if (!is_file($viewFile) || !is_file($layoutFile)) { throw new RuntimeException('View not found.'); } extract($data, EXTR_SKIP); ob_start(); require $viewFile; $content = (string) ob_get_clean(); ob_start(); require $layoutFile; $html = (string) ob_get_clean(); $basePath = (string) config('app.base_path', ''); if ($basePath !== '') { $html = (string) preg_replace_callback( '/\b(href|src|action)="(\/(?!\/)[^"]*)"/', static function (array $matches) use ($basePath): string { $path = $matches[2]; if ($path === $basePath || str_starts_with($path, $basePath . '/')) return $matches[0]; return $matches[1] . '="' . $basePath . $path . '"'; }, $html ); } echo $html; } function old(string $key, string $default = ''): string { return e($_POST[$key] ?? $default); } function is_active_path(string $path): bool { $current = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/'; $basePath = (string) config('app.base_path', ''); if ($basePath !== '' && ($current === $basePath || str_starts_with($current, $basePath . '/'))) { $current = substr($current, strlen($basePath)) ?: '/'; } return $path === '/' ? $current === '/' : str_starts_with($current, $path); } function site_setting(string $key, mixed $default = ''): mixed { static $settings = null; if ($settings === null) { try { $settings = (new \App\Repositories\SiteSettingRepository())->all(); } catch (\Throwable) { $settings = []; } } return $settings[$key] ?? $default; }