/nav/?q=<短语>`(也兼容 `?<短语>`,见下面两种都收) * · 短语由用户自己起(可一键换随机码),全局唯一,就是这页的"凭据" * · 右上角恒有一个「登录」入口,点它进家书的登录页 * · 页面顶部的头像用**他自己**的头像 * * ⚠️ 安全上的三条硬约束(这是全站唯一免登录页,必须想清楚): * ① **只暴露这一个用户自己的导航数据** —— 所有查询都按短语解析出的 person_id 走, * 不接任何来自 URL 的 id(否则 `?q=xxx&person=2` 就能翻别人家); * ② **不返回任何身份/家庭信息** —— 只有导航本身 + 头像 + 站点名。 * 连"这是谁"都不主动说(头像已经够了); * ③ 短语按固定形式校验(4~32 位字母数字_-),拿不到它就是 404(不提示"短语不存在" * 之外的信息,避免成为枚举靶子)。 */ declare(strict_types=1); require __DIR__ . '/app/bootstrap.php'; use App\Nav; // ---- 1. 取短语(两种写法都收:?q=xxx 与 ?xxx)---- // // ⚠️ 为什么两种都收:用户描述的是「地址后面跟着问号带上这个短语」, // 也就是 `/nav/?yangyq` 这种**不带参数名**的形式;而现代浏览器/分享卡片 // 更习惯 `?q=`。两种都认,用户怎么写都能进,省得他记规则。 $phrase = trim((string)($_GET['q'] ?? '')); if ($phrase === '' && !empty($_SERVER['QUERY_STRING'])) { // `?yangyq` → QUERY_STRING 就是 `yangyq`(没有 `=` 号) $qs = (string)$_SERVER['QUERY_STRING']; if (strpos($qs, '=') === false) { $phrase = urldecode($qs); } } $personId = $phrase === '' ? 0 : Nav::ownerByPhrase($phrase); // ---- 2. 找不到就是 404(不给多余信息)---- if ($personId <= 0) { http_response_code(404); header('Content-Type: text/html; charset=utf-8'); ?>
= e($navSubtitle) ?>