本文约 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 个站点(部分厂商算「无限子站」);
- 网络激活 + 子站点校验 → 每个子站各自去服务端校验,消耗 N 个授权额度(最容易踩坑);
- 逐站激活 + 子站点授权 → 明确按子站数计费;
- 逐站激活 + 网络级读取 → 子站点拿不到授权,提示未激活。
授权信息具体存在哪,不同插件不一样(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、测试站、已下线的旧站解绑,往往就能腾出额度,不用加购。
常见踩坑
-
把 staging 也算进授权:测试站、本地域名都占额度,一年下来额度全被测试环境吃掉。给 staging 站点单独买或不启用授权(功能受限不影响测试)。
-
案例: 某公司网络激活了一个表单插件,插件按「子站点」校验授权。新子站上线时没人通知 IT,第 6 个子站的授权校验失败,插件虽然仍然工作,但前台表单的提交数据不再写入(厂商把「授权无效」实现成「停止存储」)。等发现时已丢失三周的询盘。教训:购买前必须确认插件在授权失效时的降级行为,尤其是涉及数据写入的插件。
-
网络激活后设置页找不到:网络激活的插件设置入口在网络后台,不在子站点后台。很多人以为插件没装好。
-
switch_to_blog()忘记restore_current_blog():后续所有get_option都读到错误站点的数据,包括写入。 -
子站点换域名后授权失效:多数厂商按域名绑定,用
wp search-replace换域名后要重新激活授权。WordPress 换域名全流程 -
误以为 GPL 授权意味着可以随便分发:GPL 允许你使用和修改代码,但厂商的「更新服务 + 支持」是商品,用别人的授权 key 批量激活属于违约。
-
多站点下
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 做桥接(注意成对切换);给网络后台加过期提醒;续费前先在厂商后台解绑废弃域名释放额度;购买前务必确认「授权失效时插件是停止更新还是停止工作」这一条。
