插件上线前「四域联检」:我是怎么把权限、路由、菜单、配置串成一条可执行的验收链的
之前写过一份逐项确认表,用久了发现有个毛病——权限、路由、菜单、配置四个维度各自为战,真到上线前夜还是漏。后来我把它们串成了一条依赖链,按顺序走一遍,后一步自动验证前一步有没有埋雷。
下面这条链是我现在每个插件上线前必跑的,贴出来供拍砖。
第一步:配置项先「自证清白」
别急着看权限,先确认你的配置在未安装态、刚安装态、已运行态三种场景下表现一致。很多人死在「默认值漂移」——安装时写死一个值,后台保存时用另一个 key,升级脚本里又冒出第三个。
我现在强制要求配置项满足这个最小契约:
// 注册时:默认值、sanitize 回调、是否自动加载,三处必须同源
add_action( 'init', function() {
$schema = require __DIR__ . '/config-schema.php'; // 唯一真相源
foreach ( $schema as $key => $def ) {
if ( false === get_option( $key, false ) ) {
add_option( $key, $def['default'], '', $def['autoload'] );
}
}
} );
上线前检查点:直接删库重装,看 wp_options 里是不是只出现 schema 里声明的键,有没有幽灵值。
第二步:权限能力「反向溯源」
配置稳了再看权限。我的习惯是从菜单倒推能力,而不是正向声明能力再挂菜单。
具体做法:把后台所有菜单页列出来,每个页面标注它依赖的 capability,然后跑一个「角色穿透」脚本:
// 对每个自定义 capability,验证它是否真实挂载到了某个角色
$all_caps = array_unique( array_merge( ...array_map(
fn( $role ) => array_keys( $role->capabilities ),
get_editable_roles()
) ) );
$my_caps = array_filter( $all_caps, fn( $cap ) => str_starts_with( $cap, 'myplugin_' ) );
foreach ( $my_caps as $cap ) {
$holders = array_filter(
get_editable_roles(),
fn( $role ) => in_array( $cap, array_keys( $role['capabilities'] ) )
);
if ( empty( $holders ) ) {
error_log( "ORPHAN CAP: {$cap} 没有角色持有,菜单将永远 403" );
}
}
这个脚本帮我抓到过两次「能力注册了但忘了 add_cap」的事故。
第三步:菜单注册「对表验身」
权限有了,菜单挂上。这里只记一个坑:add_menu_page 的 $position 参数如果和别人撞了,你的菜单可能被挤掉而不报错。
我的土办法是上线前跑这个「位置占用普查」:
global $_registered_pages; // 其实看 $menu 全局更直观
$menu = $GLOBALS['menu'];
$positions = array_column( $menu, 0, 2 ); // slug => title
// 检查我的 slug 是否在预期位置,且没有被挤到小数点位置
$my_slug = 'myplugin-dashboard';
$expected_pos = 26; // 夹在评论和外观之间
$actual = array_search( $my_slug, array_column( $menu, 2 ) );
if ( false === $actual || abs( $actual - $expected_pos ) > 2 ) {
// 被挤了,或者根本没出现
}
更隐蔽的是子菜单:add_submenu_page 的 $parent_slug 如果和别人的子菜单撞了,WordPress 会静默合并,你的页面变成别人的子项,权限检查用的却是别人的 capability。
第四步:路由「闭环自测」
最后到 REST 路由或前端 rewrite。我的验收标准不是「注册成功」,而是「未登录拒、无权限拒、有权限通、参数脏拒」四连击。
写了个最小 CLI 测试套,上线前直接跑:
// 假设端点是 /wp-json/myplugin/v1/item/42
$tests = [
[ 'no_auth', null, 401 ],
[ 'bad_cap', 'subscriber', 403 ],
[ 'good_cap', 'editor', 200 ],
[ 'xss_id', 'editor', 400, [ 'id' => '42<script>' ] ],
];
foreach ( $tests as [ $label, $role, $expect, $args = [] ] ) {
$user = $role ? get_user_by( 'login', "test_{$role}" ) : null;
$request = new WP_REST_Request( 'GET', '/myplugin/v1/item/42' );
$request->set_param( 'id', $args['id'] ?? 42 );
if ( $user ) {
wp_set_current_user( $user->ID );
}
$response = rest_do_request( $request );
$got = $response->get_status();
assert( $got === $expect, "FAIL {$label}: expect {$expect}, got {$got}" );
}
这套测试不依赖外部 HTTP,直接调内部 dispatcher,CI 里也能跑。
链式验收的隐藏价值
四步串起来之后,我发现了一个规律:80% 的上线事故是跨域传染。配置默认值错了 → 权限检查读不到预期值 → 菜单根据权限隐藏 → 用户永远点不进那个页面 → 你以为路由 404 是 rewrite 问题,其实是配置层早就崩了。
现在我的流程卡得很死:配置 schema 不改完,权限脚本不跑;权限有 orphan cap,菜单普查跳过;菜单位置异常,路由测试暂停。强迫自己在正确的层解决正确的问题。
你们上线前有没有类似的「跨域联检」套路?或者第四步的 CLI 测试套,有没有更轻量的写法?

