Files
SimpleNav/site-icon.php
T
laoyang 92eb9d90be 后台上传站点图标:首页品牌图 + favicon + PWA 图标一起换
需求:后台新增设置,允许管理员上传自己的图标;上传后首页品牌图标与浏览器
标签页 favicon(以及「添加到手机主屏」图标)一起换成它。

要点:
- 一张上传图服务端居中裁剪成正方形,生成 16/32/48/64/180/192/512 +
  maskable 192/512 整套尺寸,并手工拼装多帧 favicon.ico。
- 单一来源 site_icon_url() 统一取址;site_icon_head() 被首页/登录页/后台
  三处共用;PWA manifest 改由 manifest.php 动态输出、maskable 也跟随 ——
  三处防线共同杜绝「首页换了、标签页还是旧的」这类半生效。
- 目录可写则落静态文件(最快);不可写的保守环境整套压进设置、由
  site-icon.php 按需输出,功能不因权限限制整块不可用。
- 与「网站列表里每个网站自己的图标」是两个维度:目录与设置键都分开,
  清理时互不误伤。

顺手修掉两个真问题:
- login.php / admin/_auth.php 各留了一行写死的 favicon,与新的
  site_icon_head() 并存 → head 里两条 rel="icon",是「上传了却没换」的
  温床。已删掉写死那两行。
- 自定义 favicon.ico 原先只塞 16+32 两帧,而内置是 16/32/48/64 四帧 ——
  换图标反而退步。已补齐 icon-64 并统一为 4 帧。
- setting() 的请求内缓存导致「同请求写后再读」拿到旧值(真实坑):
  settings_cache() 支持强制重载 + settings_reload(),写入后立即失效。

验证:单测 44/44(含只读目录动态回退)、HTTP 56/56、e2e 174/0、
反向验证 5 变体全部被抓住(含「写死残留」与「ico 只塞两帧」两个新哨兵)、
真机几何 360/390/768/1440 无溢出、线上直连桌面+移动端几何全绿、
线上诊断页走通「上传→前台全换→复原」。
2026-09-28 22:28:23 +08:00

51 lines
1.7 KiB
PHP

<?php
/**
* 简航 SimpleNav —— 动态输出「站点整体图标」
*
* 为什么需要这个文件:
* 上传自定义图标后要生成一整套尺寸(favicon 多尺寸 + PWA 图标)。正常情况会把它们
* 写成静态文件(uploads/site/<键>/...),浏览器直接取静态文件,最快。
* 但在**站点目录不可写**的保守环境里写不了文件,此时整套图片被压进 settings,
* 由这个端点按需吐出 —— 功能不至于因为权限限制而整个不能用。
*
* 用法:
* ?n=logo → 首页品牌图标
* ?n=icon-192 → PWA 图标
* ?n=icon-192-maskable
* ?n= (不带 n) → favicon.ico(多帧)
* 任何情况下都只吐「当前设置里那一套」,取不到就 404,绝不暴露路径。
*/
declare(strict_types=1);
require_once __DIR__ . '/functions.php';
$name = isset($_GET['n']) ? (string)$_GET['n'] : '';
// 白名单:只允许清单里的名字(外加 favicon.ico),杜绝路径穿越
$plan = site_icon_plan();
$allow = [];
foreach (array_keys($plan) as $k) {
$allow[$k . '.png'] = 'image/png';
}
$allow['favicon.ico'] = 'image/x-icon';
if ($name === '') {
$file = 'favicon.ico';
} else {
$file = $name . '.png';
}
if (!isset($allow[$file])) {
http_response_code(404);
exit;
}
$all = site_icon_all();
if (!isset($all[$file]) || $all[$file] === '') {
http_response_code(404);
exit;
}
$bin = (string)$all[$file];
header('Content-Type: ' . $allow[$file]);
header('Content-Length: ' . strlen($bin));
// 图标内容只随「上传」变化,键(?v=)一变就会换 URL —— 可以放心长缓存
header('Cache-Control: public, max-age=31536000, immutable');
echo $bin;