Unlimited Elements 2.0.9 Pro 标签异常排查全记录
说出来你可能不信,为了搞清楚为什么升级个插件之后小部件的标签从”Web”变成了”Free”,我花了一整天的时间,绕了好大一个圈子,最后发现原因简单到让人哭笑不得。
这篇文章把我的整个心路历程原原本本记下来,既是给自己留个档,也给遇到同样问题的朋友当个参考。准备好瓜子,故事开始了。
发现症状
事情是这样的。我站上用的 Unlimited Elements for Elementor(以下简称 UE),买的是 Pro 版本,Freemius 授权一直正常。之前版本(2.0.6)的时候,所有 Pro 小部件在 Elementor 编辑器里显示的是漂亮的 “Web” 标签,看起来挺高级。
某天我手贱点了个升级,从 2.0.6 升到 2.0.9。再打开 Elementor 编辑器一看——好家伙,所有 Pro 小部件的标签全变成了 “Free” 或 “Pro”。啥情况?授权没到期啊?
去 Freemius 后台看了一眼,授权状态正常,是 Active 的。那问题出在插件代码里了。
第一步:翻 Changelog,发现安全更新
首先想到的是看 changelog。2.0.6 → 2.0.7 → 2.0.8 → 2.0.9,这几个版本都更新了啥?
结果发现 2.0.7 有个很关键的信息——这是一个安全更新。去 Patchstack 查了一下,好家伙,CVE-2026-27041,CVSS 9.9 分,任意文件上传漏洞,严重影响。受影响版本 ≤ 2.0.6,在 2.0.7 中修复。
当时我就想:“哦,一定是这个安全补丁改了什么东西,把 Pro 判断逻辑搞坏了。”
第二步:下源码对比,发现判断错了
说干就干。我把 2.0.6 和 2.0.7 两个版本的完整源码拉下来,diff -rq 扫了一遍。结果 diff 出来 29 个文件有差异。
我一个个翻这些文件:
provider_helper.class.php— 加了一些权限检查assets_work.class.php— 加强了文件上传验证functions_wordpress.class.php— 修了 SQL 注入- ……
这些都是安全相关的改动,合理。但当我翻到跟标签显示相关的文件时——browser.class.php、web_api.class.php、globals.class.php——逐个比对,发现这几个文件在 2.0.6 和 2.0.7 之间完全一样,一个字节都没变。
也就是说,我最初的推测是错的。安全补丁并没有碰标签显示逻辑。问题不在这里。
第三步:开始瞎猜——是不是版本号有关?
排除了安全补丁,我开始天马行空地猜了。
猜测一:API 服务器根据版本号返回不同的数据?
UE 有一个远程 API,插件会跟服务器通信获取小部件的元数据。我怀疑是不是服务器端检测到版本号变了,返回了不同的标签数据。毕竟 2.0.9 是新版本,说不定远程 API 做了兼容性检查?
验证这个猜测很简单——改版本号。
UE 的版本号常量定义在 includes.php 里:
// unlimited-elements-for-elementor-premium/includes.php
define("UNLIMITED_ELEMENTS_VERSION", "2.0.9");
我在 wp-config.php 里加了这么一行来覆盖它:
// 加到 wp-config.php - 尝试覆盖版本号欺骗 API 服务器
define("UNLIMITED_ELEMENTS_VERSION", "2.0.6");
结果呢?没卵用。标签该是 “Free” 还是 “Free”。
猜测二:是不是有隐藏的测试开关?
翻代码的时候发现 unitecreator_globals.class.php 里有一段有意思的逻辑:
if (defined("UC_TEST_FREE_VERSION"))
self::$isProVersion = false;
有个 UC_TEST_FREE_VERSION 常量,定义了就强制把 Pro 版降成 Free 版。我心想,是不是新版本里某处悄悄地 define 了这个常量?于是我在 wp-config.php 里加:
// 加到 wp-config.php - 确保这个常量没被定义
// 也试过改成 true 看有啥效果
define("UC_TEST_FREE_VERSION", false);
结果还是一样——和这个常量毫无关系。
第四步:上检测代码,逐个版本对比
猜来猜去解决不了问题,还是得上检测手段。我先搞清楚一件事:标签显示由哪个函数决定。
顺藤摸瓜:
- Elementor 面板的标签 →
browser.class.php:isWebAddonFree()第 529 行 - ↓ 调用
provider_web_api.class.php:isProductActive()- ↓ 调用
HelperProviderUC::isActivatedByFreemius()- ↓ 调用
- Freemius SDK 的
$uefe_fs->is_paying()
于是我在主题的 functions.php 里加了这段检测代码,把每一步的返回值都打出来:
// 加到主题 functions.php - 调试检测代码
add_action('init', function() {
if (!class_exists('UniteCreatorWebAPI') || !class_exists('HelperProviderUC')) {
return;
}
global $uefe_fs;
$webApi = new UniteCreatorWebAPI();
$webApi->setProduct('unlimitedelements');
$debug = [
'isProductActive' => $webApi->isProductActive() ? 'true' : 'false',
'isFreemiusActive' => HelperProviderUC::isActivatedByFreemius() ? 'true' : 'false',
'UE Version' => UNLIMITED_ELEMENTS_VERSION,
'Freemius version' => $uefe_fs->get_plugin_version(),
'is_paying' => $uefe_fs->is_paying() ? 'true' : 'false',
'GlobalsUC::$isProVersion' => (GlobalsUC::$isProVersion ?? false) ? 'true' : 'false',
'pro_path_exists' => file_exists(GlobalsUC::$pathPro ?? '') ? 'true' : 'false',
];
error_log('[UE_DEBUG] ' . print_r($debug, true));
});
然后在 wp-config.php 里确保打开了调试日志:
// 加到 wp-config.php - 开启调试日志
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
保存后刷新页面,去看 /wp-content/debug.log,结果出来了:
[UE_DEBUG] Array
(
[isProductActive] => false
[isFreemiusActive] => true
[UE Version] => 2.0.9
[Freemius version] => 2.6.2
[is_paying] => true
[GlobalsUC::$isProVersion] => false
[pro_path_exists] => false
)
有意思了。is_paying = true 但 isProductActive = false。
再看 provider_web_api.class.php 里的 isProductActive() 源码就明白了:
public function isProductActive() {
if(GlobalsUC::$isProVersion == false)
return(false); // <-- 在这里就返回了!
if($this->isFreemiusActive() == false)
return(false);
return(true);
}
函数第一行就先检查 GlobalsUC::$isProVersion,如果为 false 就直接 return false,根本走不到 Freemius 检查那里。怪不得 is_paying = true 但 isProductActive = false——Freemius 根本没被问到!
第五步:二分法锁定问题版本
既然知道了检测路径,接下来用二分法找到底是哪个版本引入的问题。
我把这个检测代码保留了,然后反复切换插件版本:2.0.6 → 2.0.7 → 2.0.8 → 2.0.9。再贴个 debug.log 看看:
// === 2.0.6 ===
[UE_DEBUG] isProductActive: true, isFreemiusActive: true, is_paying: true, $isProVersion: true
// === 2.0.7 ===
[UE_DEBUG] isProductActive: true, isFreemiusActive: true, is_paying: true, $isProVersion: true
// === 2.0.8 ===
[UE_DEBUG] isProductActive: true, isFreemiusActive: true, is_paying: true, $isProVersion: true
// === 2.0.9 ===
[UE_DEBUG] isProductActive: false, isFreemiusActive: true, is_paying: true, $isProVersion: false
破案了!2.0.8 一切正常,2.0.9 $isProVersion 变成了 false。这个变量是在 unitecreator_globals.class.php 里设置的,让我去看看:
// unitecreator_globals.class.php 第 244 行
self::$pathPro = self::$pathPlugin . "pro/";
if(file_exists(self::$pathPro))
self::$isProVersion = true;
if(defined("UC_TEST_FREE_VERSION"))
self::$isProVersion = false;
逻辑再简单不过了——检查插件目录下有没有 pro/ 目录。有就是 Pro 版,没有就是 Free 版。
检测代码已经告诉我 pro_path_exists = false。也就是说2.0.9 安装目录下根本没有 pro/ 这个文件夹。
第六步:对比压缩包,找到真凶
为了确认,我把 2.0.8 和 2.0.9 的 zip 包都下下来,对比目录结构:
$ diff -rq unlimited-elements-2.0.8/ unlimited-elements-2.0.9/
只在 2.0.8 中找到: pro/childparams_pro.class.php
只在 2.0.8 中找到: pro/globals_pro.class.php
只在 2.0.8 中找到: pro/includes_pro.php
只在 2.0.8 中找到: pro/provider_settings_multisource_pro.class.php
只在 2.0.8 中找到: pro/template_engine_pro.class.php
...(还有其他 15 个文件差异)
2.0.9 的压缩包里整个 pro/ 目录凭空消失了。这 5 个文件一个都没打包进去。
这能怪谁呢?一定是发布 2.0.9 的时候打包脚本出了问题,把 pro/ 目录漏了。毕竟 2.0.7、2.0.8 都好好的,唯独 2.0.9 少了这个目录。
完整的根因链路
捋一遍整个连锁反应:
- 2.0.9 zip 打包时 pro/ 目录被遗漏了
file_exists(self::$pathPro)→ false(目录不存在)GlobalsUC::$isProVersion保持默认值 falseisProductActive()第 1 行检查if($isProVersion == false) return(false)→ 直接返回 false- Freemius 的
is_paying()虽然返回 true,但根本没被执行到 isWebAddonFree()收到 false → 所有小部件显示 “Free” 而非 “Web”
讽刺不?一个上传安全漏洞修得严严实实的,最后栽在一个打包漏文件的问题上。
修复方法
修复简单得不能再简单了——从 2.0.8 目录把 pro/ 复制过来:
cd /path/to/wp-content/plugins/unlimited-elements-for-elementor-premium/
cp -r /path/to/backup/unlimited-elements-2.0.8/pro/ ./pro/
刷新,一切恢复正常。所有小部件又老老实实显示 “Web” 标签了。
后记
这次排查最大的感触就是——你以为的问题往往不是真正的问题。
- 一开始以为是 Freemius 授权问题 → 不是
- 后来以为是安全补丁改了代码 → 也不是
- 再猜版本号检测 → 还是不对
- 最后发现是打包漏了个目录
如果一开始就直接对比各版本的 zip 包文件列表,而不是在代码逻辑里绕来绕去,能省下一大半时间。但话说回来,如果不是一层层追踪代码逻辑、加调试输出、逐个版本对比验证,也不会对 UE 的授权判断机制理解得这么透彻。
另外,用 file_exists() 来判断 Pro 版本这个设计本身也挺脆弱的——文件系统状态和授权状态一旦不一致,就会出现这种诡异的症状。当然,如果打包没出问题,这本来也不会有事。
建议遇到同样问题的朋友:如果你的 Unlimited Elements 升到 2.0.9 后发现 Pro 小部件不认了,先看看插件目录下有没有 pro/ 这个文件夹。八成就是少了它。
排查日期:2026-05-14 | 首发于 tudoudaily.com