1为什么需要这份文档
XIUNOX 应用市场欢迎开发者贡献插件,但很多插件提交前没有经过完整测试,导致:
致命 安装后白屏、fatal error,影响整个站点
严重 卸载不干净,残留表/配置污染数据库
严重 hook 内
return导致宿主函数逻辑被跳过一般 缓存不刷新,修改后不生效
一般 伪静态模式下 URL 错误
本文档是上架硬性门槛,不是建议。未通过自检的插件将被审核员驳回。
文档分两部分:第一部分是开发者提交前必须完成的测试流程;第二部分是审核员依据的代码规范标准。两者结合,确保上架插件质量。
2上架前必做测试(开发者自检)
2.1 安装 / 卸载 / 升级全流程
这是最高频出问题的环节。很多开发者只测了"能装上",没测"能卸干净"和"能升级"。
全新安装测试
在
DEBUG=1(开发环境)下全新安装,无任何报错在
DEBUG=0(生产环境)下全新安装,无 fatal error 白屏安装后插件目录的
conf.json中installed=1且enable=1数据库表已创建,字符集为
utf8mb4(不是 utf8)默认配置已写入
setting_get('你的插件名')安装后
tmp/model.min.php已被清理(install.php 末尾必须清理)
卸载干净测试
卸载脚本文件名是
uninstall.php(不是unstall.php)卸载后所有插件表已
DROP TABLE卸载后
setting_delete('你的插件名')清除配置卸载后
kv_delete('你的插件名')清除 KV 数据卸载后
XnEvent::off(null, '你的插件名')清理事件监听(如有)卸载后
CacheHelper::pluginDeletePrefix('你的插件名')清除缓存
覆盖升级测试
关键场景:存量用户已经安装旧版本,覆盖新代码后不会重新执行 install.php,只走 upgrade.php。
数据库结构变更放在
upgrade.php(不是 install.php 或 setting.php)upgrade.php 用
SHOW COLUMNS+ALTER TABLE幂等逻辑(可重复执行不报错)结构变更后递增
conf.json.version(如 1.0 → 1.1),触发核心层「需升级」提示覆盖代码后不重新安装,直接在后台点「升级」,功能正常
启用 / 禁用切换测试
禁用插件后,前台功能完全消失(无残留 JS/CSS/卡片)
重新启用后,功能恢复,无数据丢失
禁用/启用不影响其他插件运行
2.2 功能测试矩阵
必须在以下场景中测试核心功能,不能只测"管理员发帖"这一条路径。
测试场景 | 游客 | 普通用户 | 版主 | 管理员 |
|---|---|---|---|---|
前台查看插件功能 | 按权限 | 必测 | 必测 | 必测 |
发帖/回帖触发插件 | — | 必测 | 必测 | 必测 |
后台设置页保存 | — | — | 拒绝 | 必测 |
删帖级联清理 | — | — | 必测 | 必测 |
前台核心场景
首页加载无 JS 报错(F12 控制台)
帖子列表页加载插件 hook 无性能问题(不 N+1 查询)
帖子详情页插件功能正常显示
发帖页插件功能(如有)正常工作
手机端布局正常(响应式)
后台设置页
setting.php开头有$gid != 1 && $gid != 2 AND message(-1, '无权限');POST 表单包含
CsrfService::input()+CsrfService::check()保存后配置正确写入,页面跳转回设置页
敏感字段(密码/Token)用
param($key, $default, FALSE)关闭转义
多语言测试
新增语言键同时写入
hook/lang_zh_cn_bbs.php(不只写 lang/ 目录)切换到
zh-tw/en-us语言包,插件文案正常显示未翻译的键显示为
lang[key]字面量时,说明语言键未生效
语言键只写 lang/zh-cn.php 不写 hook/lang_zh_cn_bbs.php 是高频错误。前台 lang() 实际读 tmp/lang_zh_cn_bbs.php 编译缓存,不读 lang/ 目录。
2.3 缓存与编译测试
XIUNOX 的 _include() 编译缓存不比较源文件 mtime,这是最容易踩的坑。
编译缓存测试
修改
route/*.php/model/*.func.php/view/htm/*.htm后,删除tmp/下对应编译缓存批量清理命令:
rm -f tmp/route_*.php tmp/model_*.func.php tmp/view_htm_*.htm tmp/lang_*_bbs.php清
tmp/cache/无效,必须清tmp/根目录下的编译文件
数据缓存测试
数据变更后缓存自动刷新(用版本号机制或主动删除)
清除插件缓存用
CacheHelper::pluginDeletePrefix('插件名'),不枚举 limit 值缓存键通过
CacheHelper::pluginKey()生成带p_{plugin}_前缀Service 类构造函数调用
CacheHelper::registerKeys()注册缓存键
CacheHelper::set() 写裸值陷阱:remember() 内部用 array('__v'=>$data) 哨兵包装,set() 写裸值(如版本号整数)会被 remember() 判为 MISS,导致版本号 bump 永久失效。必须统一用 array('__v'=>$value) 哨兵格式。
2.4 兼容性测试
运行环境
PHP 8.0+ 无弃用警告(deprecated notice)
MySQL 5.7+ / MariaDB 10.3+ 兼容
数据库表用
utf8mb4字符集(支持 emoji)Redis 缓存驱动下功能正常(不只是文件缓存)
伪静态模式
XIUNOX 支持 6 种伪静态模式(url_rewrite_on 0~5)。所有 URL 必须用 url() 函数生成,禁止硬编码 .htm 后缀。
所有 URL 用
url()或命名快捷函数(如thread_url($tid))生成禁用
url("thread-$tid")字符串拼接,改用thread_url($tid)缓存刷新/跳转用
$site_url . url("xxx"),不用$site_url . '/xxx.htm'插件自定义路由通过
hook/model_route_table_end.php注册到$routes数组切换伪静态模式后,插件所有链接正常跳转
htmx 4 兼容
htmx 事件名用 4 格式(冒号分隔):
htmx:after:swap/htmx:response:error禁用 2.x 旧名:
htmx:afterSettle/htmx:configRequest(会静默失效)htmx 请求返回纯 HTML 片段(不是完整页面/JSON/重定向)
3代码规范审核标准(审核员)
以下是审核员逐项检查的硬性标准。每条标注严重级别,致命 级别一票否决。
3.1 结构规范
检查项 | 级别 | 要求 |
|---|---|---|
conf.json 必填字段 | 致命 |
|
bbs_version 取值 | 致命 | 必须为 |
hooks_rank 格式 | 严重 | 对象 |
overwrites_rank 格式 | 严重 | 对象 |
dependencies 格式 | 严重 | 对象 |
install.php 幂等 | 致命 |
|
install.php 末尾清缓存 | 严重 | 清理 |
uninstall.php 文件名 | 致命 | 标准拼写 |
upgrade.php 机制 | 严重 | 结构变更走 upgrade.php(幂等),不在 install.php/setting.php 加字段自愈代码 |
命名前缀 | 严重 | 表/变量/语言键/JS/CSS/setting 都带插件前缀;禁用 |
3.2 安全规范
检查项 | 级别 | 要求 |
|---|---|---|
CSRF 防护 | 致命 | 所有 POST 表单含 |
输出转义 | 致命 | 用 |
敏感字段 param() | 致命 | 密码/Token 用 |
后台权限检查 | 致命 |
|
SQL 注入防护 | 致命 | 优先 |
用户输入 intval | 严重 | SQL 拼接用户输入必须 |
入口级安全检查覆盖 | 严重 | IP 黑名单/封禁检查覆盖所有入口(login/create/resetpw/发帖/回帖/编辑) |
3.3 前端规范
检查项 | 级别 | 要求 |
|---|---|---|
禁用 jQuery | 致命 | 新代码禁用 |
禁用 Alpine.js | 致命 | 禁用 |
htmx 4 事件名 | 致命 | 冒号分隔: |
Bootstrap 5 + Tabler Icons | 严重 | 用 BS5 组件 + |
x-card 卡片样式 | 严重 | 卡片加 |
JS/CSS 存放位置 | 致命 | 放 |
资源引用路径 | 严重 | 用 |
后台禁用 htmx | 严重 |
|
状态不放 window | 严重 | 禁用 |
3.4 数据与 SQL 规范
display_name 是虚拟字段:由 user_format() 在 PHP 运行时从 nickname + username 派生,不是数据库字段。SQL 中 SELECT display_name 必触发 1054 Unknown column。
检查项 | 级别 | 要求 |
|---|---|---|
用户信息获取 | 致命 | 用 |
显示用户名 | 致命 | 模板统一取 |
UNIQUE 字段批量写入 | 严重 |
|
counter 递减保护 | 严重 | 用 |
分页过滤下沉 SQL | 严重 | 过滤条件下沉到 SQL 层,防止分页后过滤导致条数不一致 |
db_insert 返回值检查 | 严重 |
|
3.5 hook 规范
所有 hook 文件禁止 return(已违反 4 次,最高优先级):hook 编译期内联到宿主,return; 会从宿主返回,跳过后续逻辑。违规示例 if (!class_exists('X')) return; → 改为 if (class_exists('X')) { ... } 包裹。
检查项 | 级别 | 要求 |
|---|---|---|
hook 禁止 return | 致命 | 所有 hook 文件(路由+model+view 层)禁用 |
终止性 exit 注释 | 严重 | API 响应/重定向后允许 |
PHP hook 开头 | 致命 | PHP hook 以 |
HTM hook 开头 | 致命 | HTM hook 以 |
hook 文件名匹配 | 致命 | 文件名(含扩展名)必须和源码标记一模一样: |
发帖/回复场景区分 | 严重 | 共用 |
发帖页功能挂载点 | 严重 | 挂到 |
列表页 hook 性能 | 严重 | 用 |
删帖级联清理 | 严重 | 注册 |
Service 类加载 | 严重 | 调用核心 Service(CreditsService 等)前 |
前台判断插件启用 | 严重 | 用 |
3.6 缓存规范
检查项 | 级别 | 要求 |
|---|---|---|
新增缓存 API | 严重 | 用 |
清理缓存 API | 严重 | 用 |
缓存键前缀 | 严重 | 用 |
缓存键注册 | 严重 | Service 构造函数调用 |
set() 哨兵格式 | 致命 |
|
长键处理 | 注意 | 长键(>32 字符)会被 md5 哈希, |
cache_truncate | 致命 | 禁用 |
4高频踩坑清单
以下是 XIUNOX 项目实战中已发生事故的规则,按违反次数降序排列。必读 修改前必须逐项核对。
4 次以上(最高优先级)
hook 文件禁止 return — 已违反 4 次(xnx_feeds/xnx_hidden 路由层 + xnx_feeds model 层 6 个 + xnx_maintenance runlevel 检查被跳过严重 bug)。终止性操作允许 exit 但必须 if 包裹 + // ponytail: 注释说明。
2 次(高优先级)
修改 PHP 核心函数时同步
min.php— 已违反 2 次表单提交前禁止
jform.reset()(清空用户输入)— 已违反 2 次(admin_login + 6 个后台模板)密码等敏感配置
param()必须传第 3 参FALSE— 已违反 2 次(登录失败 + admin user_create/update)display_name是虚拟字段,SQL SELECT 必报 1054 — 已违反 2 次,影响 6 个插件分页过滤前移遗漏 post 表 JOIN — 已违反 2 次
xn.*函数库迁移漏迁(xn.url/xn.is_number)— 已违反 2 次$.ajax/$.paramjQuery 静态方法迁移漏迁 — 已违反 2 次
1 次(需关注)
更新日志误写到子目录或 Glob 递归搜索
lib 类顶部
include _include()函数库导致重复声明 fatalJS 迁移漏迁全局符号(
setBtnLoading+ 死模板)htmx 事件名用 2.x 旧名(
htmx:configRequest)导致静默失效修改
_include()加载的文件后未删tmp/编译缓存URL 函数双重包裹(
url(admin_plugin_setting_url($dir)))插件语言键未同步写入
hook/lang_*_bbs.phpconf.php的static_version未在 JS/CSS 修改后递增overwrites_rank覆盖模板放在view/htm/而非overwrite/核心 Service 静态成员访问前缺少
class_exists守卫入口级安全检查未覆盖全部路由
CacheHelper::set()写裸值与remember()哨兵格式不匹配lib类不会自动加载(路由层必须显式include)
5提交材料清单
5.1 必需文件
conf.json— 字段完整,version 用 X.Y.Z 三位制,bbs_version 用 X.Y 两位制install.php— 建表 + 默认配置 + 末尾清model.min.phpuninstall.php— 标准拼写,删表 + 删 KV + 删 settingupgrade.php— 如有数据库结构变更必须提供,幂等setting.php— 后台设置页(如有配置项)README.md— 插件说明、功能介绍、使用方法、截图
5.2 代码文件
model/XxxService.php— Service 类(静态方法模式)hook/model_inc_file.php— 注册 Service 类路径hook/下所有 hook 文件(文件名和源码标记一致)route/路由文件(如插件有独立页面)view/htm/模板文件static/js//static/css/静态资源
5.3 测试报告
提交时请附上测试报告,包含:测试环境(PHP 版本/MySQL 版本/伪静态模式)、测试流程(安装→功能→卸载)、测试结果截图。
DEBUG=0 生产环境安装截图
核心功能正常截图(前台+后台)
卸载后数据库干净截图(
SHOW TABLES无插件表)多语言切换截图(zh-cn / zh-tw / en-us)
手机端布局截图
5.4 打包规范
zip 包根目录是插件目录名(如
my_plugin/)不包含
.git/.DS_Store/tmp/等无关文件不包含
conf.json的installed/enable字段为 1(必须是 0)文件名用下划线
_,不用连字符-
