反馈修复 [已修复] 改动说明 — PHP 8.4 / 8.5 兼容性修复-1.0.3

2026-09-15 12:16:11
版本 V1.0.3version.php,末位数字 3 = 目录号 3)
主题 PHP 8.4 / 8.5 兼容性修复:消除「每请求级」弃用告警刷屏 + 8.5 新增弃用项
目标环境 手机端 WSToolbox 内置 PHP(实测 8.4+);IIS + 腾讯云函数(每 5 分钟触发 cron_trigger.php
涉及静态资源 (未改 CSS / JS,故 sw.jsCACHE_VERSION 不需要递增,本包不含 sw.js
数据库结构变更 (无表结构变更、无索引变更、无数据迁移)
部署关键点 无配置项需改动;纯文件覆盖即可。新增 1 个文件 app/Core/SessionHandler.php,勿遗漏
兼容性 向下兼容 PHP 8.0+(项目最低要求),向上兼容 8.5;已在 8.4.25 与 8.5.10 真机验证

一、变更文件清单

# 文件 变更类型 问题编号
1 app/Core/SessionHandler.php 新增(全新文件) H-2
2 app/init.php 第 41 行:6 个闭包 → 传对象 H-2
3 app/SplitDB/ViewCounter.php 第 30 / 57 行:补 ?(2 处) M-4
4 app/Helpers/CoverTemplate.php 第 155 行前加 3 行多字节前置判断 H-1
5 app/Controllers/ApiController.php curl_close() / imagedestroy() 各 1 处 M-1 / M-2
6 app/Helpers/Plugin.php curl_close() 1 处 M-1
7 app/Helpers/Upload.php finfo_close() 1 处、imagedestroy() 3 处 M-2 / M-3

合计:7 个文件(1 新增 + 6 修改)、11 个代码位置。全部为「删除一个调用」或「补一个字符」级别,无逻辑重构。


二、逐项改动说明

1. H-2 app/init.php + app/Core/SessionHandler.php最严重项(每请求 1 条)

PHP 8.4 起弃用session_set_save_handler() 若传入「独立回调」,必须改为传入一个 实现 SessionHandlerInterface 的对象

线上实际报错(手机服务器):

Deprecated: session_set_save_handler(): Providing individual callbacks instead of
an object implementing SessionHandlerInterface is deprecated
in .../flinthub/app/init.php on line 41

改动:原 app/init.php:41 的 6 个闭包(open/close/read/write/destroy/gc) 整体迁入新增的 app/Core/SessionHandler.php,入口改为:

session_set_save_handler(new \app\Core\SessionHandler());

★ 关键设计决策(防行为漂移,请勿"优化")

  1. 刻意【不】实现 SessionUpdateTimestampHandlerInterfacevalidateId / updateTimestamp)。 原写法只传了 6 个回调、未提供第 7/8 个;PHP 在 session.use_strict_mode=1(本项目默认开启) 下走的是「read() 返回空串即判定为新 ID」的默认路径。补上 validateId改变会话固定 攻击防护的判定路径,属隐性行为变更,故明确不补。
  2. 失败返回值与原实现逐一对齐read''writefalsedestroyfalsegc0
  3. TTL 保持 1800 秒expires = time() + 1800),与原闭包完全一致。
  4. 沿用 Database::getSessionConnection()(独立 PDO 连接,与业务连接物理隔离), 避免 SplitDB 的 2014 游标冲突。

兼容性:sessions 表结构与数据格式未变,新旧实现可互相读取已存会话, 故本次改动不会导致任何在线用户掉线,回滚也不会。


2. M-4 app/SplitDB/ViewCounter.php每请求 2 条(8.4 与 8.5 都触发)

PHP 8.4 起弃用:参数类型前缺 ? 的「隐式可空」写法。

// 改前
public static function inc(int $topicId, PDO $mainDb = null): void
public static function flush(PDO $mainDb = null): int

// 改后
public static function inc(int $topicId, ?PDO $mainDb = null): void
public static function flush(?PDO $mainDb = null): int

为什么它很严重index.php:280 注册的 register_shutdown_function无条件调用 ViewCounter::flush()index.php:285),因此类文件 ViewCounter.php 每次请求都会被编译, 两个隐式可空参数就在编译期各发一条告警 → 每请求固定 2 条, 与是否启用 APCu、是否浏览主题都无关

佐证:两处 docblock 早已写明 @param PDO|null $mainDb,即意图本就是可空, 只是语法漏了 ?。属纯补全,零行为变更


3. H-1 app/Helpers/CoverTemplate.php:155 — 中文标题必刷屏(仅 8.5)

PHP 8.5 起弃用ord() 传入非单字节字符串。

// 改前
private static function charUnits(string $ch): float
{
    $o = \ord($ch);          // ← 中文"中"是 3 字节,此处收到多字节字符串
    if ($o < 0x80) { ... }
    return 1.0;
}

// 改后(新增前置判断)
private static function charUnits(string $ch): float
{
    if (\strlen($ch) > 1) {
        return 1.0;          // 多字节字符原本就走下面 return 1.0 分支
    }
    $o = \ord($ch);
    if ($o < 0x80) { ... }
    return 1.0;
}

触发条件:入参来自 preg_split('//u', $title, ...)CoverTemplate.php:118), 即按 UTF-8 字符切分;中文/全角是 2–4 字节。任何含中文的标题在生成封面时都会触发

零行为变更证明\strlen($ch) === 1ord() 必为单字节;多字节分支原本也返回 1.0。 实测 30 个 SVG(5 类标题 × 6 模板)改动前后逐字节一致(内容哈希 5b377287a9121a0687be01a0546b61e6d7d9ff99db4d1393955dcb87d92b2772)。


4. M-1 / M-2 / M-3 — 清理 8.5 弃用函数调用(共 7 处)

PHP 8.5 起弃用,且三者自 PHP 8.0 起均已无实际作用(对象自动释放),故直接删除:

编号 文件:行 删除的代码 备注
M-1 app/Controllers/ApiController.php:371 \curl_close($ch); 句柄随 $ch 重赋值/作用域结束释放
M-1 app/Helpers/Plugin.php:201 curl_close($ch); curl_error() 已在上一行取完,不影响错误处理
M-2 app/Controllers/ApiController.php:500 imagedestroy($img); 其后紧跟 exit,对象必然被回收
M-2 app/Helpers/Upload.php:220 \imagedestroy($src); 仅删该调用,保留 return;
M-2 app/Helpers/Upload.php:229 \imagedestroy($src); 原图对象随作用域释放
M-2 app/Helpers/Upload.php:240 \imagedestroy($thumb); 缩略图对象随作用域释放
M-3 app/Helpers/Upload.php:58 \finfo_close($finfo); $mime 已在上一行取完,不影响判断

删除安全性:这些都是「释放资源」的显式调用,PHP 8.0 起底层改为对象引用计数自动释放, 调用本身已成为 no-op。删除后功能实测全部正常(见 §4)。


三、部署步骤

  1. 备份(建议):备份 app/init.phpapp/SplitDB/ViewCounter.phpapp/Helpers/CoverTemplate.phpapp/Helpers/Plugin.phpapp/Helpers/Upload.phpapp/Controllers/ApiController.php(6 个将被覆盖的文件)。
  2. 上传覆盖:把本包内 app/ 目录按原结构覆盖到站点根目录的 app/
  3. ★ 确认新增文件已上传app/Core/SessionHandler.php —— 这是本包唯一的新增文件,遗漏会导致站点致命错误(类不存在)
  4. 更新版本号:用本包 version.php 覆盖站点根目录的 version.php(→ V1.0.3)。
  5. 清缓存
    • 浏览器强刷(Ctrl+F5 / 手机端清站点数据)以刷新 sw.js 缓存;
    • 若站点启用了 OPcache,重启 PHP 或等待 OPcache 过期(opcache.revalidate_freq)。

      本包不含 CSS/JS 改动,sw.jsCACHE_VERSION 无需递增。

  6. 无需:无需重启任何进程(本环境无常驻 worker)、无需改配置、无需执行迁移脚本、 无需访问任何升级 URL。

四、验证结果(真机实测,非静态分析)

验证环境:PHP 官方便携版 8.4.258.5.10(Windows NTS x64, 已启用 pdo_sqlite / sqlite3 / mbstring / fileinfo / openssl / curl / gd)。

4.1 全库编译期扫描(568 个 PHP 文件)

PHP 版本 修复前 修复后
8.4.25 1 个文件 / 2 条告警 0 个文件 / 0 条
8.5.10 1 个文件 / 2 条告警 0 个文件 / 0 条

方法:对全库每个文件跑 php -l。隐式可空参数等弃用在编译期触发,php -l 会直接报出, 比正则扫描权威。

4.2 站点路由冒烟(19 条路由)

路由 8.4.25 8.5.10
//index.php/forum/login/register/forgot-password 200 200
/blog/tags/thread/1/blog/1/tag/1/forum/category/1 200 200
/search/message/notifications/profile/post/new/admin 302(未登录跳转,正确) 302
/assets/css/modern.css 200 200
  • 首页字节数:两版本均为 63,863 字节,与修复前完全一致
  • protected/error.log 在冒烟后为空 —— 修复前同样 19 条路由会产生 42 行告警。

4.3 逐项告警实测

修复前(8.4.25 / 8.5.10) 修复后
H-2 session_set_save_handler() 1 条 / 1 条 0 / 0
M-4 隐式可空参数 2 条每请求 / 2 条每请求 0 / 0
H-1 ord() 中文标题 0 / 17 条 0 / 0
M-1 curl_close() 0 / 1 条 0 / 0
M-2 imagedestroy() 0 / 1 条 0 / 0
M-3 finfo_close() 0 / 1 条 0 / 0

4.4 功能回归(确认删除调用未破坏功能)

功能路径 结果 弃用告警
Upload::makeThumbnail() 生成缩略图 ✅ 返回 ok,缩略图 200×150、格式正确 0
finfo MIME 识别 ✅ 正确识别为 image/png 0
Plugin::http() 插件远程请求 ok=true,取回 2701 字节 0
ApiController::fetchHopByHop() 拉图 ✅ 取回 2701 字节 0
/api/captcha 路由 ✅ 200,合法 PNG(魔数正确) 0
/api/cover-preview(中文标题) ✅ 200,合法 SVG,8.4 与 8.5 输出完全一致 0
CoverTemplate 30 个 SVG 输出 ✅ 改动前后逐字节一致 0

4.5 语法检查

7 个文件在 PHP 8.0.2 / 8.4.25 / 8.5.10 三个版本下 php -l 均无语法错误、无弃用告警。


五、回滚方案

场景 回滚方式
任意文件出问题 用备份的旧文件覆盖即可。无 schema 变更、无数据迁移,无需任何数据库操作
仅 H-2 想回滚 恢复 app/init.php 旧版(6 个闭包版);app/Core/SessionHandler.php 可留着(无引用即无副作用)。sessions 表未变,在线用户不会掉线
版本号回滚 恢复旧 version.php

回滚安全性:本包全部改动都是「消除告警」性质,不涉及业务逻辑、SQL、模板与静态资源, 回滚后行为与升级前完全一致。


六、附:为什么这些改动是安全的

  1. 零行为变更:H-1 与 M-4 属「语法补全」,输出已用哈希证明逐字节一致; M-1/M-2/M-3 删的是自 PHP 8.0 起就无效的 no-op 调用。
  2. H-2 是唯一的结构性改动,但已刻意保持 6 个方法与返回值语义 1:1 对齐, 且不补 validateId/updateTimestamp 以维持原 use_strict_mode 行为。
  3. 不涉及数据库结构、索引、配置项、CSS/JS、插件文件。
  4. 已在 PHP 8.0.2 / 8.4.25 / 8.5.10 三个版本上验证,覆盖项目声明的最低版本与目标版本。
最後由 flinthub 於 2026-09-15 12:19 編輯
我喜欢在我的自留地里瞎逛,FlintHub!
| 瀏覽 0 | 回覆 1

全部回覆 (1)

2026-09-15 12:18:48
建议先更新1.0.2,然后覆盖1.0.3,因为这是小版本更新,没有做全量增量包,只能一个一个来。
我喜欢在我的自留地里瞎逛,FlintHub!
| 引用 #1 樓

登入

×