$route], $query); return 'index.php?' . http_build_query($q); } function redirect(string $route = 'home', array $query = []): void { header('Location: ' . url($route, $query)); exit; } function is_post(): bool { return ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST'; } function fb_config(string $key = null, $default = null) { $c = $GLOBALS['config']; if ($key === null) { return $c; } return array_key_exists($key, $c) ? $c[$key] : $default; } // ---------------- CSRF ---------------- function csrf_token(): string { if (empty($_SESSION['_csrf'])) { $_SESSION['_csrf'] = bin2hex(random_bytes(16)); } return $_SESSION['_csrf']; } function csrf_field(): string { return ''; } /** * 校验 CSRF。失败直接结束请求 —— 不做这种假返回: * 「返回 false 让调用层自己处理」的写法,迟早有人忘了判断。 */ function csrf_check(): void { $ok = isset($_POST['_csrf'], $_SESSION['_csrf']) && hash_equals($_SESSION['_csrf'], (string)$_POST['_csrf']); if (!$ok) { http_response_code(419); echo '页面已过期,请刷新后重试(CSRF 校验失败)'; exit; } } // ---------------- 一次性提示 ---------------- function flash(string $type, string $message): void { $_SESSION['_flash'][] = ['type' => $type, 'message' => $message]; } function take_flash(): array { $f = $_SESSION['_flash'] ?? []; unset($_SESSION['_flash']); return $f; } /** 表单回填(校验失败时保留用户输入) */ function old(string $key, $default = '') { return $_SESSION['_old'][$key] ?? $default; } function set_old(array $data): void { $_SESSION['_old'] = $data; } function clear_old(): void { unset($_SESSION['_old']); } /** 生日相关:返回年龄与距下次生日天数 */ function age_from(string $birthday): ?int { if ($birthday === '' || $birthday === null) { return null; } try { $b = new DateTimeImmutable($birthday); } catch (Exception $ex) { return null; } return (int)$b->diff(new DateTimeImmutable('today'))->y; } function days_until_birthday(string $birthday): ?int { if (!$birthday) { return null; } try { $b = new DateTimeImmutable($birthday); $now = new DateTimeImmutable('today'); $next = new DateTimeImmutable($now->format('Y') . '-' . $b->format('m-d')); if ($next < $now) { $next = $next->modify('+1 year'); } return (int)$now->diff($next)->days; } catch (Exception $ex) { return null; } }