变更时间:2026-07-24 影响范围:核心架构 + 所有插件 版本要求:XIUNOX v1.1.4+
一、背景
Xiuno 旧机制(v1.1.3 及之前)
v1.1.3 及之前版本使用 tmp/model.min.php 合并加载机制:
Plain Text
所有 model/*.func.php + 插件 model/*.php
↓
合并成单个文件 tmp/model.min.php
↓
一次性 include 加载xiuno bbs设计初衷:减少磁盘 I/O 次数,提升 PHP 5.x 时代的加载性能。
存在的问题
随着 PHP 8 + OPcache 的普及,合并加载的收益可忽略,但带来了大量稳定性问题:
问题 | 影响 | 严重程度 |
|---|---|---|
加载顺序不确定 | 类/函数依赖关系可能因合并顺序不同而断裂,生产环境与开发环境行为不一致 | ⚠️ 高 |
单插件语法错误全站白屏 | 一个插件的 model 文件有语法错误,导致整个 model.min.php 解析失败,全站 500 | 🔴 致命 |
并发重建文件损坏 | 多请求同时触发重建时,可能读到截断的空文件,导致随机 fatal | 🟠 中 |
缓存陈旧难以排查 | 插件升级后 model.min.php 未及时重建,旧代码继续运行,问题隐蔽 | 🟠 中 |
调试困难 | 错误行号对应合并后的文件,无法直接定位到源文件 | 🟡 低 |
OPcache 命中率低 | 单个大文件修改后整个文件重新编译,不如小文件粒度细 | 🟡 低 |
触发事件
连续出现多起因 model.min.php 引发的故障:
字段名修正后缓存未更新,导致 SQL 错误反复出现
xnx_hidden 插件因 Service 类未加载连续崩溃 3 次,触发自动禁用
开发环境正常、生产环境报错的"灵异"问题频繁出现
二、新机制(v1.1.4+)
2.1 逐文件加载
v1.1.4 起彻底废弃 model.min.php 合并机制,改为逐文件 include:
PHP
// model.inc.php
foreach ($include_model_files as $model_files) {
include _include($model_files);
}所有环境(DEBUG / 生产)统一走相同逻辑,行为一致。
2.2 性能保障
PHP 8 + OPcache 热身后,逐文件 include 的性能与合并加载无感知差异,原因:
OPcache 缓存字节码:首次加载后 PHP 把编译后的字节码存在共享内存中,后续请求直接执行字节码,不需要重新读取磁盘文件、重新解析、重新编译
内存中执行:OPcache 热身后,include 的开销从"磁盘 I/O + 语法解析 + 编译"降低为"哈希表查找 + 指针跳转"
27 个核心 model + N 个插件 model:即使 100 个文件,include 的总开销也在微秒级,远小于一次 DB 查询的毫秒级开销
2.3 收益
改进 | 说明 |
|---|---|
✅ 错误隔离 | 单个插件语法错误只影响该插件自身,不会导致全站白屏 |
✅ 加载顺序确定 | 按数组顺序逐个加载,开发/生产环境行为完全一致 |
✅ 无并发重建问题 | 每个文件独立编译缓存,互不影响 |
✅ 调试友好 | 错误行号直接对应源文件,堆栈清晰 |
✅ OPcache 粒度更细 | 修改一个文件只重新编译该文件,其他文件缓存继续有效 |
✅ 代码更简洁 | 删除了合并/重建/清理的大量样板代码 |
三、插件开发者注意事项
3.1 已移除的操作
以下操作不再需要,可从插件代码中删除:
install.php 中清理
tmp/model.min.phpPHP
// ❌ 旧代码(已废弃,可删除) if (isset($conf['tmp_path']) && function_exists('xn_unlink')) { @xn_unlink($conf['tmp_path'].'model.min.php'); }upgrade.php 中清理
tmp/model.min.php(同上)任何手动清理 model.min.php 的代码
核心已在插件 install / upgrade / enable / disable 时自动调用 plugin_clear_tmp_dir() 清理全部编译缓存。
3.2 仍需遵守的规则
_include()编译缓存仍需清理修改
route/*.php、model/*.func.php、view/htm/*.htm后,需清理tmp/下对应的编译缓存_include()只检查缓存文件是否存在,不比较源文件修改时间批量清理:
rm -f tmp/route_*.php tmp/model_*.func.php tmp/view_htm_*.htm
lib 类不会自动加载
项目无 spl_autoload,
lib/下的类不会自动加载调用核心 Service(如
CreditsService、UserBanService)前必须手动 include:PHP
if (!class_exists('CreditsService')) { include_once APP_PATH . 'lib/CreditsService.php'; }访问静态属性/常量前必须先确保类已加载
model 文件仍走
_include()model 文件通过
_include()加载,支持 hook 注入和编译缓存插件 Service 类通过
hook/model_inc_file.php注册
3.3 代码迁移清单
检查项 | 操作 |
|---|---|
install.php 中有 | 删除 |
upgrade.php 中有 | 删除 |
setting.php 中有 | 删除 |
文档/注释中提到「生产环境走 model.min.php」 | 更新为「项目无 spl_autoload,lib 类不会自动加载」 |
调用 lib 类前有 | 保持(仍需遵守) |
四、核心修改概览
4.1 修改的核心文件
文件 | 修改内容 |
|---|---|
| 删除 model.min.php 合并分支,统一走逐文件 include |
| 删除 |
| 删除错误自愈逻辑中清理 model.min.php 的代码 |
| 更新注释,删除对 model.min.php 的引用 |
4.2 修改的插件文件
共清理 61 个插件文件中的 model.min.php 引用:
所有插件的
install.php:删除清理代码所有插件的
upgrade.php:删除清理代码所有插件的
uninstall.php:删除清理代码部分插件的
setting.php/model/*.php:删除相关注释和代码
五、向后兼容性
5.1 插件兼容性
旧插件仍可正常运行:install/upgrade 中残留的清理代码只是无效操作,不会报错
建议逐步清理:插件升级时顺手删除无用的清理代码即可,不强制要求
5.2 数据库兼容性
无数据库结构变更
无需执行升级脚本
5.3 配置兼容性
无配置项变更
无需修改
conf.php
六、FAQ
Q1:逐文件 include 会不会比合并加载慢很多?
不会。 PHP 8 + OPcache 热身后,include 的开销在微秒级,远小于一次 DB 查询(毫秒级)。合并加载在 PHP 5.x 时代有意义,在 PHP 8 + OPcache 时代收益可忽略。
Q2:为什么不直接用 composer 的 autoload?
Xiuno 项目历史包袱较重,整体迁移到 composer autoload 成本较高。本次优化先解决最痛的稳定性问题,autoload 改造留待后续版本。
Q3:插件升级后还是需要清 tmp 缓存吗?
是的。 _include() 的编译缓存机制没变(不比较源文件 mtime),修改源文件后仍需清理对应缓存。但核心已在插件 install/upgrade/enable/disable 时自动调用 plugin_clear_tmp_dir(),正常流程下无需手动清理。
Q4:以后还会有类似的全站白屏问题吗?
概率大幅降低。 新机制下单个插件的语法错误只会影响该插件自身的编译缓存,不会导致全站 model 加载失败。配合已有的「插件崩溃自动禁用」机制,单个插件出问题最多是该插件功能失效,不会影响全站。
