如何在 MySql 中反序列化 WordPress File Manager 字符串?
结论
这段内容使用 PHP 的 serialize() 序列化格式。MySQL 5.7 没有内置的 PHP 反序列化函数,无法直接用 JSON_EXTRACT() 从中提取用户与文件夹的对应关系。
可以先通过 WordPress 或 PHP 读取数据、还原数组,确认插件如何保存用户与目录的关联。如果需要经常用 SQL 查询,再将提取出的关系写入单独的报表表。
目前的片段缺少用户与文件夹数组的完整结构,还无法据此写出准确的提取代码。
为什么 JSON 方法不起作用
序列化字符串中的标记有各自的含义:
| 片段 | 含义 |
|---|---|
a:39:{...} | 包含 39 个键值对的 PHP 数组 |
s:26:"wp_filemanager_nonce_field"; | 字节长度为 26 的字符串 |
a:1:{i:0;s:6:"editor";} | 一个数组,整数键 0 对应字符串 editor |
字符串长度按字节计算,不能直接当成字符数,含有中文时尤其要注意。
PHP 序列化数据需要用相应的解析器还原。它不是合法 JSON,MySQL 的 JSON 函数无法直接处理。即使将整串内容包装成 JSON 字符串,也不会解析其中的 PHP 数组结构。参见 PHP 序列化说明和 MySQL 5.7 JSON 函数说明。
先把完整配置读取出来
用 SQL 读取原始值:
SELECT option_value
FROM wp_options
WHERE option_name = 'wp_filemanager_options';
如果站点使用其他表前缀,需要替换 wp_options。同时确认数据库客户端没有截断长文本。问题中带有 ... 的片段不能直接反序列化。
使用 WP-CLI 查看
如果服务器已经安装 WP-CLI,在 WordPress 安装目录下执行:
wp option get wp_filemanager_options
需要 JSON 输出时,执行:
wp option get wp_filemanager_options --format=json
这里的 JSON 是读取选项后生成的输出格式,数据库中的原始值不会改变。WP-CLI 文档列出了这些输出选项。
使用 WordPress PHP API 查看
在已加载 WordPress 的 PHP 环境中执行:
$options = get_option('wp_filemanager_options');
if (!is_array($options)) {
throw new RuntimeException('配置不存在,或读取结果不是数组。');
}
print_r($options);
get_option() 会自动反序列化选项中的序列化值,正常情况下无需再次调用 unserialize()。参见 WordPress 的 get_option() 文档。
如果通过独立数据库连接取得原始字符串 $raw,可以在 PHP 中还原:
$options = unserialize($raw, ['allowed_classes' => false]);
if (!is_array($options)) {
throw new RuntimeException('反序列化失败,或数据不是预期的数组。');
}
示例中的 allowed_classes 参数需要 PHP 7.0 或更高版本。只应处理来源可信的配置,即使设置了 allowed_classes => false,任意不可信输入也不会因此变得安全。参见 PHP 的 unserialize() 文档。
如何提取用户与文件夹的对应关系
查看还原后的数组,找到用户字段和目录字段的实际键名,确认插件如何关联这两个字段。
两个数组在序列化文本中相邻,不能证明它们按位置一一对应。还需要检查它们是否使用相同的数组键,以及一个用户能否配置多个目录。仅凭问题中出现的 fm_user_roles 和 private_folder_access,也无法确定完整的逐用户权限规则。
确认两个数组使用相同的键,且每个键对应一个用户名和一个目录字符串后,才能使用下面的代码。代码中的键名是占位符,必须替换:
// $options 来自前面的 get_option()。
$usersKey = '替换为用户名数组的实际键名';
$foldersKey = '替换为目录数组的实际键名';
$users = $options[$usersKey] ?? null;
$folders = $options[$foldersKey] ?? null;
if (!is_array($users) || !is_array($folders)) {
throw new RuntimeException('用户字段或目录字段不是数组。');
}
if (count($users) !== count($folders)) {
throw new RuntimeException('两个数组的元素数量不一致。');
}
$rows = [];
foreach ($users as $key => $username) {
if (!array_key_exists($key, $folders)) {
throw new RuntimeException('目录数组缺少对应的键。');
}
if (!is_string($username) || !is_string($folders[$key])) {
throw new RuntimeException('数据不符合单个用户名对应单个目录的结构。');
}
$rows[] = [
'user_login' => $username,
'folder_path' => $folders[$key],
];
}
print_r($rows);
不要为了对齐两个数组而分别排序或重新编号,否则可能把用户与其他人的目录配在一起。如果目录字段是嵌套数组,需要按实际结构展开为多行。
需要持续用 SQL 查询时,建立报表表
确认映射规则后,可以用 PHP 将结果写入单独的表,每行记录一个用户与一个目录的关系。例如:
CREATE TABLE filemanager_access_report (
user_login VARCHAR(60) NOT NULL,
folder_path TEXT NOT NULL
) DEFAULT CHARSET = utf8mb4;
这张报表表需要自行建立,插件本身不提供。通过应用代码或导入工具填充数据后,就可以查询:
SELECT
u.ID AS user_id,
r.user_login,
r.folder_path
FROM filemanager_access_report AS r
LEFT JOIN wp_users AS u
ON u.user_login = r.user_login
ORDER BY r.user_login, r.folder_path;
这里假定提取出的用户值是 WordPress 的 user_login。如果插件存储的是用户 ID,应改用 ID 关联。插件配置改变后,报表数据也需要同步刷新。
注意配置与实际权限的区别
提取结果反映的是已保存的配置。要确定某个用户实际能访问哪些文件夹,还需要核对所安装 File Manager 插件版本的权限判断逻辑,确认角色规则、默认目录和用户专属设置是否共同参与判断。
不要用 SUBSTRING_INDEX()、按分号切割或简单替换来解析整串数据,嵌套数组和字符串内部的标点都会使这类方法失效。也不要直接把原选项改成 JSON,或在序列化文本中手工替换路径。这可能破坏字符串长度信息,导致插件无法读取配置。
备注:内容仅供参考。
