插件上线前「四域联检」:我是怎么把权限、路由、菜单、配置串成一条可执行的验收链的

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

之前写过一份逐项确认表,用久了发现有个毛病——权限路由菜单配置四个维度各自为战,真到上线前夜还是漏。后来我把它们串成了一条依赖链,按顺序走一遍,后一步自动验证前一步有没有埋雷。

下面这条链是我现在每个插件上线前必跑的,贴出来供拍砖。

第一步:配置项先「自证清白」

别急着看权限,先确认你的配置在未安装态刚安装态已运行态三种场景下表现一致。很多人死在「默认值漂移」——安装时写死一个值,后台保存时用另一个 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 测试套,有没有更轻量的写法?

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