插件打包前夜,我列了份"生死清单":权限、路由、菜单、配置四项全查过才睡踏实

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
插件开发 30 浏览 0 回复

上周凌晨两点准备发版,手已经悬在"上传至仓库"按钮上了,突然脊背发凉——上周那个因为漏查capability导致全站用户都能进财务后台的事故还历历在目。从那以后,我养成了一个强迫症:每次打包前必须过一遍四张清单,少勾一项绝不发版。今天把这套流程摊开,各位可以直接抄去用。

一、权限清单:不是加了current_user_can就万事大吉

我最惨痛的一次,是在 REST API 路由里写了权限回调,却忘了自定义的 capability 压根没分配给任何角色。结果?前端 403,但直接 curl 带 cookie 就能绕过去——因为路由的permission_callback只拦了没登录的,没拦"登录了但不该看这儿的"。

现在我的权限清单长这样:

□ 自定义 capability 已通过 add_cap() 注入管理员/目标角色
□ 每个 admin_menu / add_submenu_page 的 $capability 参数与业务匹配
□ REST 路由 permission_callback 返回的是 bool,不是字符串或 void
□ AJAX 动作里手动做了 nonce + capability 双校验
□ 前端"隐藏按钮"不等于后端"拒绝执行"——接口层再验一次

特别提醒:如果用了map_meta_cap做细粒度控制,记得在清单里加一条"自定义 capability 的映射逻辑是否覆盖了所有边缘角色",访客、订阅者、被封禁用户都要走一遍。

二、路由清单:rewrite 规则不是注册了就会生效

有回我本地路由跑得欢,到生产环境 404。排查两小时,发现是插件激活时忘了flush_rewrite_rules(),而本地因为反复切换主题早就刷过了。更阴的是,如果规则里用了正则捕获组,Nginx 的try_files和 WordPress 的parse_request会互相甩锅,错误日志里一个字没有。

我的路由 checklist:

□ 自定义端点注册在 init 钩子,优先级 >= 10
□ 插件激活/停用钩子里显式 flush_rewrite_rules()
□ 规则包含 ^ 和 $ 锚点,防止前缀误匹配
□ 若用了 template_redirect 接管,确认 query_var 已注册且 public=true
□ 多站点环境测试:子目录模式和子域名模式行为不同
□ Nginx 配置里 $is_args$args 有没有漏传

一个小技巧:在开发环境装个"Rewrite Rules Inspector"插件,发布前肉眼扫一遍规则表,确认你的那条不在"未命中"列表里。

三、菜单清单:slug 对不上,页面就"孤儿"

WordPress 后台菜单的$parent_slug是个玄学字段。我吃过两次亏:一次填了 file 路径导致子菜单不高亮,一次填了 slug 却和另一个插件撞名,结果两个插件的子菜单叠在一起,像叠罗汉。

现在菜单项我按这个顺序核对:

□ 顶级菜单 slug 全局唯一,建议带插件前缀如 myplugin_dashboard
□ add_submenu_page 的 $parent_slug 与 add_menu_page 返回的 $menu_slug 逐字符比对
□ $position 参数有没有和系统菜单或其他插件冲突(特别是 20-30 区间)
□ 菜单图标是 dashicons 还是 base64?后者在 WP 5.8+ 有渲染差异
□ 子菜单第一项 slug 若与父级相同,WordPress 会吞掉,需故意错开
□ 多语言站点:菜单 label 是否走了 __() 且文本域已加载

遇到过最诡异的情况是:菜单显示了,点进去空白。最后发现是add_menu_page的 callback 里提前exit了,但前面已经输出了 BOM 头,导致 WordPress 的 admin-header.php 炸掉。

四、配置清单:option 存了,但缓存没认

配置页保存成功、提示绿色对勾、数据库里也有值,前端读出来却是旧的——这种"幽灵配置"我撞过三次。根源各不相同:对象缓存没失效、autoload='no' 导致每次额外查询、或者用了wp_cache_set但没给 group 导致和get_option的缓存池对不上。

配置项的临终检查:

□ register_setting 的 sanitize_callback 不会把合法值洗成空
□ 敏感配置(密钥、API token)是否走了 encryption,还是裸存 options
□ 多站点下用 update_blog_option 还是 switch_to_blog + update_option?
□ 配置变更后是否触发 clean_blog_cache() 或特定 group 的 wp_cache_delete
□ 前端读取时用的是 get_option 还是直接从 $GLOBALS['wp_object_cache'] 偷数据
□ 卸载流程里:单站点 delete_option / 多站点 delete_site_option 是否区分

建议给每个配置组加个版本号 option,比如myplugin_config_v = 3。前端请求时带版本,后端发现缓存里的配置版本落后就强制刷新,比盲目清缓存省资源。

五、最后一道:打包前的"黑暗仪式"

四张清单勾完,我还会做三件事:

  1. grep -r "TODO\|FIXME\|var_dump\|die(" 扫一遍代码,防止调试代码漏网
  2. 在全新 Docker 容器里装 WordPress,只激活我的插件,走一遍安装向导
  3. 把 WP_DEBUG 关掉再开一遍,确认没有 NOTICE 级错误被日志吞掉

有次就是第三步救了我:WP_DEBUG开着时某个undefined index被屏蔽了,生产环境报错直接白屏。

这套清单我从 v1.0 用到 v3.4,每次迭代会补新条目。各位如果有"发布前夜惊魂"的踩坑经历,欢迎往清单里添砖加瓦——毕竟凌晨两点的冷汗,能少流一滴是一滴。

评论0
回复 · 0
还没有回复
微信客服 微信客服