我维护一套 50+ 插件的社区站点插件集,覆盖内容管道、主题导航、互动积分、后台管理、通知私信、榜单统计等类型。按 v11 beta 的代码逐项核对后,给几条 beta 窗口内改成本最低、上线后再改成本很高的建议。

一、links 是否必要:建议保留,但不要按“性能优化”定位

同意 @bbs1org 结论:它的核心价值是入口数据化、统一校验、故障隔离,性能只是顺带。我们的场景里,确实存在两类入口:

  • 插件在前台只负责一条链接(迁 links 收益高);
  • 插件本来就会因其它 Hook 加载,入口回调只是一行 append(收益几乎为零,只剩校验价值);
  • 带未读角标、动态文案、plugin_asset_require() 的入口(必须继续 Hook)。

因此建议明确写入规范:links 是“纯静态入口”的声明方式,不做全量迁移;动态入口永远留 Hook。否则第三方插件容易为了“数据化”把动态逻辑塞进 links,最后既丢功能又难排查。

二、若保留 links,beta 前建议补齐 6 个契约(按优先级)

  1. admin_tabs 的“导航 + 内容”同源

现在 links.admin_tabs 只生成导航,内容仍由 admin_tabs 回调渲染。插件作者按“声明 links 后回调会被跳过”的字面理解删掉内容回调,就会出现“入口在、点开空白/404”。建议二选一:

  • admin_tabs 支持对象形式:'admin_tabs' => ['key' => ['label'=>'...','callback'=>'...','order'=>...]];
  • 或允许 links.admin_tabs 条目带 content 回调,由核心自动配对;
  • 至少要在 PLUGIN.md 明确:links.admin_tabs 与 admin_tabs 内容回调必须同时保留。
  1. 入口校验 fail-closed

when.config / when.ctx 中非法规则目前被静默跳过;如果所有条件都非法,when 变空,链接变成无条件显示。params 的非法键/值也是逐个跳过但整条链接仍渲染。对“仅本人/仅管理员/依赖 id”的入口,这是实际风险。建议:

  • 任一 when 或 params 非法 → 丢弃整条 link;
  • 同步/更新接口返回被丢弃的条目与原因,后台插件列表给出提示;20 条上限超限也一样。
  1. 每个入口的 ctx 契约要写清并校验

现在 {forum_id}、{self} 等占位符在不同入口上下文并不都存在;写了不存在的键,整条链接会静默不渲染,作者很难定位。建议:

  • PLUGIN.md 按入口列出可用 ctx 键;
  • 同步期把“该入口不存在的占位符”判为非法并给出原因。
  1. Tab key 命名空间与稳定顺序

当前 Tab 链接直接覆盖同名 key,可能顶掉核心或其它插件的 Tab;before/after 锚点不存在时追加,多个插件追加的顺序也未定义。建议:

  • 保留字清单(settings/forums/groups/plugins/profile/topics/replies/notifications/admin 等);
  • Tab key 强制插件前缀,或核心自动命名空间;
  • 明确排序规则(插件 ID + 声明顺序),避免同一页 Tab 顺序漂移。
  1. URL / route 校验
  • url 目前允许协议相对地址 //host,与“只允许相对或 http/https”的口径不一致,建议显式拒绝;
  • route 不校验是否存在,写错只在点击时 404,建议同步期校验路由或在文档标注。
  1. icon_html 白名单

当前只允许 svg/path/circle/rect/line/polyline/polygon/g,会误杀 <ellipse>/<defs>/<linearGradient> 等常用图标元素;同时又未拦 fill="url(...)" 这类外部引用。建议给一份官方安全 SVG 白名单,或明确“数据入口只用核心 icon 名”。

三、按需资源:建议补 4 个能力

  1. 路由级资源声明

页面型插件路由(榜单、签到、推广页、日志页等)在 routes 里只有 callback;改为按需模式后,每个路由都要手动 plugin_asset_require(),漏一个就是裸页面。建议 routes 支持对象形式并声明资源(assets => 'all'|['css','js']),核心在路由分发前自动登记,旧字符串格式兼容。

  1. assets_mode 支持按类型

assets_mode 现在是插件级,CSS/JS 只能一起公共或一起按需;实际大量插件是“CSS 内容页常驻、JS 只在编辑/弹层页用”。建议支持 assets_mode => ['css'=>'common','js'=>'ondemand'](或 css_mode/js_mode),这样规则里的“列表页只用样式、不输出脚本”才能真正落地。

  1. AJAX 片段的资源句柄

现在 AJAX 局部 HTML 明确不输出资源。弹卡、投票局部刷新、编辑器预览这类 AJAX UI,只能靠“首次整页提前登记”,页面组合一多就会漏。建议:

  • json_response 可附带本次plugin_asset_require() 收集到的 handles;
  • 核心 JS 提供去重加载(如 bbs1.loadAssets(handles));
  • 或让 plugin_asset_require() 返回 handles,由插件自行回传。
  1. 页面级 Hook 的 ctx 标准化

资源按需与页面注入都要求“按路由/页面类型判断”,但当前插件只能用 $_GET['a'] 自己解析,美化 URL、后台、AJAX 等路径容易误判。建议 page.before_render/page.head/page.footer/page.template 的 ctx 统一带 route、page_type、is_ajax、plugin_route。

四、兼容与迁移成本

  • 同意“双份维护是 links 最大的代价”。建议明确:旧内核兼容期内不删旧回调,并在同步诊断里标出“该入口已由 links 接管、回调在新内核为死代码”,方便后续清理。
  • v11 移除了 v10 的全局 plugin_registry_row() / plugin_update_row()(已内化到 Plugin);建议升级说明列出这类全局函数清单,避免第三方插件 fatal。
  • PLUGIN.md 中“老内核的 plugin_asset_require() 不认识第二个参数”的表述不准确:老内核根本没有该函数,裸调会直接 fatal,必须 function_exists() 兜底。建议修正,避免作者照抄出错。

五、建议的 beta 验收用例

  1. 仅声明 links.admin_tabs、不保留内容回调 → 应明确报错/文档阻止,而不是空白页。
  2. when.config 写错键名 → 整条入口不出现,同步返回“条件非法”。
  3. params 缺 id → 整条入口不出现,而不是链到无参页面。
  4. 插件 Tab key 与核心 settings 同名 → 不覆盖核心 Tab。
  5. feature_links 使用 {forum_id} → 同步期提示该入口不支持。
  6. 按需插件在 AJAX 弹层返回 HTML → 资源能按 handles 加载,或文档明确要求首次整页登记。
  7. CSS 公共 + JS 按需 → 列表页只输出 CSS,编辑页才输出 JS。
  8. 老内核加载带 links/assets_mode 的插件 → 仍走旧回调/公共合集,不报错。

结论:links 建议保留,但按“纯静态入口的数据化 + 统一校验 + 故障隔离”定位,不做全量迁移;若 beta 期能先补齐 admin_tabs 内容契约、fail-closed 校验、Tab 命名空间这三项,后续第三方插件迁移的成本和事故率会低很多。按需资源则建议优先补“路由级声明 + 按类型模式 + AJAX handles”,否则这套机制对大多数页面型插件只能停留在“整包搬走”的半成品状态。

主楼