搜索全站
插件 13 分钟阅读

WordPress 多站点插件授权(License)管理与续费策略

多站点网络里插件到底算几个授权?本文讲清网络激活与逐站激活的区别,给出 get_site_option 统一存储授权、子站点同步的代码,以及授权范围核对与续费策略。

U
UFO_PF 2016-05-17 · 阅读 13 分钟
Wo 插件
阅读提示
本文约 5,103 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:买了 5 个站点授权,第 6 个子站激活失败

一位客户买了商业插件的「5 站点授权」,在 WordPress 多站点网络里建了 8 个子站。网络后台一键「网络激活」后,前 5 个子站正常收到更新,第 6 个开始提示「授权已达上限」。他很不理解:我明明只有一个 WordPress 安装、一个数据库,为什么算 8 个站点?

答案取决于插件怎么统计站点数,而这又取决于它在哪里读授权、在哪里校验。多站点(Multisite)下,「站点」这个概念有两层:网络(Network)和子站点(Site / Blog)。插件可以在其中任意一层存授权、任意一层校验,四种组合的结果完全不同。

原理分析:两层存储与两种激活

概念 数据表 读取函数
网络级设置 wp_sitemeta get_site_option() / update_site_option()
子站点设置 wp_options、wp_2_options… get_option() / update_option()

网络激活(Network Activate):插件代码对所有子站点生效,但插件的设置页通常只在「网络管理后台」出现一次,授权信息写进 wp_sitemeta,全网络共用一份。

逐站激活(Per-site Activate):在每个子站点的后台分别启用,每个子站点各自存一份授权、各自校验。这种情况下插件服务商通常按子站点数计费。

四种组合的现实后果:

  1. 网络激活 + 网络级授权 → 全网络一份授权,通常算 1 个站点(部分厂商算「无限子站」);
  2. 网络激活 + 子站点校验 → 每个子站各自去服务端校验,消耗 N 个授权额度(最容易踩坑);
  3. 逐站激活 + 子站点授权 → 明确按子站数计费;
  4. 逐站激活 + 网络级读取 → 子站点拿不到授权,提示未激活。

授权信息具体存在哪,不同插件不一样(Easy Digital Downloads 的 Software Licensing、WooCommerce API Manager、Freemius 三家的存储位置都不同)。最可靠的查法是直接搜 option 名:

wp option list --search='*license*' --fields=option_name,autoload
wp option list --search='*licence*' --fields=option_name,autoload

授权过期会发生什么:多数商业插件在授权过期后不停止工作,只停止「接收更新」和「访问官方支持」。这是 GPL 类授权与订阅制的区别:你买的是软件使用权 + 一年更新服务,不是软件本身会失效。但少数插件(尤其是依赖云端 API 的,如图片压缩、翻译、防垃圾)会直接停用功能。

保姆级实操

第一步:清点网络里到底有多少个子站

wp site list --fields=blog_id,url,public,archived,deleted
# 只要激活中的
wp site list --field=url --public=1

# 逐个站点看某个插件的授权状态
for u in $(wp site list --field=url --public=1); do
  printf '%-40s ' "$u"
  wp option get myplugin_license_status --url="$u" 2>/dev/null || echo '(未设置)'
done

第二步:确认插件的激活层级

# 哪些插件是网络激活的
wp plugin list --status=active-network --fields=name,version

# 哪些是逐站激活的(看某个具体子站)
wp plugin list --status=active --url=subsite.example.com --fields=name,version

# 网络级设置里有没有授权相关条目
wp eval 'print_r( array_keys( wp_cache_get("notoptions","options") ? [] : [] ) );' 2>/dev/null
wp db query "SELECT meta_key FROM wp_sitemeta WHERE meta_key LIKE '%licen%';"

第三步:把授权统一提到网络级存储

如果插件只认 get_option(),而你希望全网络共用一份授权(常见于内部工具型插件),可以用一个 mu-plugin 做「回源」:子站点读不到时去网络级取。

<?php
// wp-content/mu-plugins/network-license-bridge.php

/**
 * 子站点读取授权时,若本地为空则回退到网络级
 */
function mmf_license( $key ) {
    $value = get_option( $key );
    if ( false === $value || '' === $value ) {
        if ( is_multisite() ) {
            $value = get_site_option( 'mmf_' . $key );
        }
    }
    return $value;
}

/**
 * 在网络后台保存授权后,同步到所有公开子站点
 */
add_action( 'update_site_option', function ( $option, $value, $old_value, $network_id ) {
    if ( ! str_starts_with( $option, 'mmf_' ) ) {
        return;
    }
    $real_key = substr( $option, 4 );   // 去掉 mmf_ 前缀

    foreach ( get_sites( [ 'public' => 1, 'fields' => 'ids', 'number' => 500 ] ) as $blog_id ) {
        switch_to_blog( $blog_id );
        update_option( $real_key, $value );
        restore_current_blog();
    }
}, 10, 4 );

switch_to_blog() 会把数据库上下文切到子站点(wp_2_options 这样的表),restore_current_blog() 必须成对调用,否则后续查询会串到错误的站点上——这是多站点代码最常见的 bug。

第四步:给授权加过期提醒

商业插件的授权过期往往悄无声息,等到出安全补丁才发现收不到更新。用一个 mu-plugin 在网络后台做提醒:

<?php
add_action( 'network_admin_notices', function () {
    if ( ! current_user_can( 'manage_network_options' ) ) {
        return;
    }
    $items = [
        'mmf_myplugin_license' => 'MyPlugin Pro',
        'mmf_backup_license'   => 'Backup Addon',
    ];
    $warn = [];
    foreach ( $items as $key => $label ) {
        $exp = (int) get_site_option( $key . '_expires' );   // 存 UNIX 时间戳
        if ( $exp && $exp - time() < 30 * DAY_IN_SECONDS ) {
            $days = max( 0, (int) ceil( ( $exp - time() ) / DAY_IN_SECONDS ) );
            $warn[] = sprintf( '%s:%d 天后到期(%s)', $label, $days, wp_date( 'Y-m-d', $exp ) );
        }
    }
    if ( $warn ) {
        echo '<div class="notice notice-warning"><p><strong>授权即将到期:</strong><br>' .
             implode( '<br>', array_map( 'esc_html', $warn ) ) . '</p></div>';
    }
} );

过期时间需要你手动维护(多数插件不提供读取接口),可以配合日历提醒。

第五步:制定续费策略

判断维度 建议
插件是否长期停止维护 是 → 不续费,找替代
站点是否还在用它的核心功能 否 → 停用并卸载,别留着占授权名额
旧授权是否还能继续用 多数可继续用,只是没更新;能接受就不急
续费折扣 多数厂商给 30%–60% 的续费折扣,比重新买划算
授权站点数 先停掉不用的旧站,释放额度再决定买几份

一个实用技巧:续费前先清点授权占用。很多厂商的账户后台能看到已激活域名列表,把废弃的 staging、测试站、已下线的旧站解绑,往往就能腾出额度,不用加购。

常见踩坑

  1. 把 staging 也算进授权:测试站、本地域名都占额度,一年下来额度全被测试环境吃掉。给 staging 站点单独买或不启用授权(功能受限不影响测试)。

  2. 案例: 某公司网络激活了一个表单插件,插件按「子站点」校验授权。新子站上线时没人通知 IT,第 6 个子站的授权校验失败,插件虽然仍然工作,但前台表单的提交数据不再写入(厂商把「授权无效」实现成「停止存储」)。等发现时已丢失三周的询盘。教训:购买前必须确认插件在授权失效时的降级行为,尤其是涉及数据写入的插件。

  3. 网络激活后设置页找不到:网络激活的插件设置入口在网络后台,不在子站点后台。很多人以为插件没装好。

  4. switch_to_blog() 忘记 restore_current_blog():后续所有 get_option 都读到错误站点的数据,包括写入。

  5. 子站点换域名后授权失效:多数厂商按域名绑定,用 wp search-replace 换域名后要重新激活授权。WordPress 换域名全流程

  6. 误以为 GPL 授权意味着可以随便分发:GPL 允许你使用和修改代码,但厂商的「更新服务 + 支持」是商品,用别人的授权 key 批量激活属于违约。

  7. 多站点下 wp option get 默认查主站:不加 --url= 参数拿到的是 blog_id=1 的数据,容易误判。

总结

判断标准:先确认插件在「哪一层存授权、哪一层校验」,再决定买几份、怎么激活。

行动清单:用 wp site list 清点子站数与公开状态;用 wp plugin list --status=active-network 确认激活层级;wp option list --search='*license*' 找出授权存储位置;需要共用授权时用 get_site_option + switch_to_blog 做桥接(注意成对切换);给网络后台加过期提醒;续费前先在厂商后台解绑废弃域名释放额度;购买前务必确认「授权失效时插件是停止更新还是停止工作」这一条。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。