插件兼容性扫描器
概述
插件兼容性扫描器用于检测已安装插件中与 Xiuno BBS 4.5+ 现代化改造不兼容的代码,涵盖 PHP 8.0+ 语法、Bootstrap 5 迁移、jQuery 去除、安全规范等方面。
入口 :后台 → 插件管理 → 插件兼容性检查(/admin/?plugin-scanner.htm)
相关文件
| 文件 |
说明 |
lib/PluginScanner.php |
扫描器核心,定义规则与扫描逻辑 |
admin/route/plugin_scanner.php |
后台路由,处理扫描/导出请求 |
admin/view/htm/plugin_scanner.htm |
扫描器页面模板 |
lang/zh-cn/bbs_admin.php |
中文语言项(plugin_scanner_*) |
lang/en-us/bbs_admin.php |
英文语言项 |
使用方式
- 进入后台「插件兼容性检查」页面
- 点击「开始扫描」按钮
- 前端通过
fetch() 请求 ?plugin-scanner-do.htm,后端返回 JSON 格式扫描结果
- 结果按插件分组展示,支持按严重级别筛选
- 可导出 CSV 报告
扫描范围
扫描 plugin/ 目录下所有包含 conf.json 的插件目录,递归检查以下扩展名的文件:
.php --- PHP 代码
.htm / .html --- 模板文件
.js --- JavaScript 脚本
.css --- 样式文件
自动跳过 img/、images/、fonts/ 目录。
规则类别
严重级别说明
| 级别 |
含义 |
| fatal |
致命:代码在 PHP 8.0+ 下会直接报错或崩溃 |
| warning |
警告:存在安全风险或功能异常 |
| medium |
中等:不兼容但不会直接崩溃,需要迁移 |
| info |
信息:建议优化,非必须 |
PHP 兼容性规则
废弃函数(PHP 7.x 移除)--- fatal
检测 mysql_* 系列函数及其他在 PHP 7.x 已移除的函数:
| 匹配模式 |
说明 |
mysql_connect 等 19 个 |
mysql 扩展已在 PHP 7.0 移除 |
each( |
PHP 7.2 废弃,8.0 移除 |
create_function( |
PHP 7.2 废弃,8.0 移除,用闭包替代 |
split( / spliti( |
PHP 7.0 移除,用 preg_split() 或 explode() |
ereg( / ereg_replace( 等 |
PHP 7.0 移除,用 preg_match() / preg_replace() |
call_user_method( |
PHP 7.0 移除,用 call_user_func() |
PHP 8 不兼容语法 --- fatal
| 匹配模式 |
说明 |
&new |
按引用创建对象已移除,改用 new |
preg_replace.*\/e |
/e 修饰符已移除,用 preg_replace_callback() |
花括号数组访问 --- fatal
| 匹配模式 |
说明 |
{$ |
{$arr['key']} 语法已移除,改用 [$arr['key']] |
HTTP_*_VARS 变量 --- fatal
| 匹配模式 |
说明 |
HTTP_POST_VARS 等 |
已移除,用 $_POST / $_GET / $_SESSION |
PHP 8.0+ 废弃函数 --- fatal
| 匹配模式 |
说明 |
get_magic_quotes_gpc |
PHP 7.4 废弃、8.0 移除,始终返回 false |
get_magic_quotes_runtime |
PHP 7.4 废弃、8.0 移除 |
utf8_encode( |
PHP 8.2 废弃,用 mb_convert_encoding() |
utf8_decode( |
PHP 8.2 废弃,用 mb_convert_encoding() |
money_format( |
PHP 7.4 废弃、8.0 移除,用 NumberFormatter |
is_resource( |
对 PDO/MySQLi 对象返回 false(PHP 8.0+),改用 instanceof |
安全规则
危险函数 --- warning
| 匹配模式 |
说明 |
eval( / system( / exec( 等 |
代码注入/命令执行风险,应避免使用 |
权限安全(敏感字段修改)--- warning
| 匹配模式 |
说明 |
user_update.*password |
应使用 user_change_password() |
user_update.*gid |
应使用 user_change_group() |
user_update.*salt |
salt 由系统自动管理,不应直接修改 |
user_update.*password_hash |
应使用 user_change_password() |
POST 表单缺少 CSRF 令牌 --- warning
| 匹配模式 |
说明 |
method="post" 但无 CsrfService / csrf_token |
所有 POST 表单必须包含 CSRF 令牌 |
此规则采用文件级检测:读取整个文件内容,检查是否同时包含 method="post" 和 CsrfService/csrf_token,若缺少则报告。
Bootstrap 迁移规则
BS4 旧类名 --- medium
| 匹配模式 |
替换建议 |
ml- |
ms- |
mr- |
me- |
pl- |
ps- |
pr- |
pe- |
form-group |
mb-3 |
custom-select |
form-select |
custom-control |
form-check |
btn-block |
w-100 |
input-group-prepend / input-group-append |
直接 input-group-text |
BS4 旧 data 属性 --- medium
| 匹配模式 |
替换建议 |
data-toggle |
data-bs-toggle |
data-dismiss |
data-bs-dismiss |
data-target |
data-bs-target |
data-slide-to |
data-bs-slide-to |
data-slide |
data-bs-slide |
BS3 旧类名 --- medium
| 匹配模式 |
替换建议 |
panel-heading |
card-header |
panel-body |
card-body |
panel-footer |
card-footer |
panel-default 等 |
card + 颜色 |
well |
card .card-body 或自定义样式 |
glyphicon |
Tabler Icons ti-* |
pull-left |
float-start |
pull-right |
float-end |
hidden-xs |
d-none .d-sm-block |
visible-xs |
d-sm-none |
label-default 等 |
badge .bg-* |
img-responsive |
img-fluid |
img-circle |
rounded-circle |
img-rounded |
rounded |
col-xs- |
col-(xs 断点已移除) |
Bootstrap jQuery 插件调用 --- warning
| 匹配模式 |
替换建议 |
.modal( |
new bootstrap.Modal() |
.dropdown( |
new bootstrap.Dropdown() |
.tooltip( |
new bootstrap.Tooltip() |
.popover( |
new bootstrap.Popover() |
.collapse( |
new bootstrap.Collapse() |
.carousel( |
new bootstrap.Carousel() |
.alert( |
new bootstrap.Alert() |
.button( |
new bootstrap.Button() |
.tab( |
new bootstrap.Tab() |
前端迁移规则
jQuery 使用 --- medium
| 匹配模式 |
替换建议 |
$.ajax( |
htmx 或原生 fetch |
$.post( |
htmx hx-post 或原生 fetch |
$.get( |
htmx hx-get 或原生 fetch |
$.each( |
Array.forEach() |
$.fn. |
Alpine.js 组件 |
$.extend( |
Object.assign() |
$.trim( |
String.trim() |
$.parseJSON( |
JSON.parse() |
$.isArray( |
Array.isArray() |
$.isFunction( |
typeof fn === "function" |
$.browser |
特性检测 |
$(document).ready |
DOMContentLoaded |
$(function( |
DOMContentLoaded |
jQuery( |
htmx + Alpine.js |
Fontello 旧图标 --- medium
| 匹配模式 |
替换建议 |
icon-lock |
ti-lock |
icon-home |
ti-home |
icon-edit |
ti-pencil |
icon-remove |
ti-trash |
icon-eye |
ti-eye |
icon-ok |
ti-check |
icon-cog / icon-cogs |
ti-settings |
icon-comment |
ti-message |
icon-user |
ti-user |
icon-envelope |
ti-mail |
icon-key |
ti-key |
icon-star |
ti-star |
非 Tabler Icons 图标库 --- medium
| 匹配模式 |
说明 |
fa- |
Font Awesome 图标 → Tabler Icons ti-* |
bi- |
Bootstrap Icons → Tabler Icons ti-* |
glyphicon- |
Glyphicon 图标 → Tabler Icons ti-* |
代码规范规则
直接数据库操作 --- info
| 匹配模式 |
说明 |
db_exec( / db_update( / db_insert( / db_delete( / db_find( / db_create( / db_drop( |
建议使用 model 层封装 |
此规则排除 install.php、uninstall.php、unstall.php、upgrade.php 文件(安装/升级脚本中直接操作数据库是合理的)。
其他规则
conf.json 版本检查 --- info
检查 conf.json 中 bbs_version 字段,若 < 4.5 则提示更新。
Hook 文件名检查 --- info
检查 hook/ 目录下文件名是否符合 [a-z_][a-z0-9_]* 命名规范。
特殊处理逻辑
missing_csrf(跨行检测)
POST 表单的 CSRF 检测不能逐行匹配,因为 method="post" 和 CsrfService::input() 可能分布在文件不同位置。因此采用文件级检测:
- 读取整个文件内容
- 检查是否包含
method="post"
- 若包含,再检查是否包含
CsrfService 或 csrf_token
- 仅当 POST 表单存在且缺少 CSRF 令牌时才报告
direct_db(文件名排除)
安装/升级脚本中直接操作数据库是合理的,因此排除以下文件:
install.php
uninstall.php
unstall.php(Xiuno 旧版拼写)
upgrade.php
API 接口
扫描请求
GET /admin/?plugin-scanner-do.htm
返回 JSON 数组,每个元素代表一个插件的扫描结果:
[
{
"dir": "xn_tag",
"name": "标签",
"version": "1.0",
"bbs_version": "4.0",
"installed": true,
"enable": true,
"issues": [
{
"file": "plugin/xn_tag/hook/post_js.htm",
"line": 15,
"category": "jquery_usage",
"match": "$.ajax(",
"suggestion": "使用 htmx 或原生 fetch 替代 $.ajax",
"severity": "medium",
"context": "$.ajax({url: '...'})"
}
],
"total": 3,
"fatal": 0,
"warning": 1
}
]
CSV 导出
GET /admin/?plugin-scanner-export.htm
下载 CSV 文件,包含所有插件的扫描结果。
指定插件导出
GET /admin/?plugin-scanner-export.htm&dir=xn_tag
仅导出指定插件的扫描结果。
扩展规则
如需添加新的扫描规则,编辑 lib/PluginScanner.php:
- 在
$rules 数组中添加新类别和匹配模式
- 在
$severityLevels 数组中添加严重级别映射
- 在
getCategoryName() 方法的 $names 数组中添加中文名称
- 在
lang/zh-cn/bbs_admin.php 和 lang/en-us/bbs_admin.php 中添加 plugin_scanner_cat_* 语言项
如需特殊处理逻辑(如跨行检测、文件名排除),在 scanPluginDir() 方法中添加相应代码。