PAYATHON 2026

如何在 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,或在序列化文本中手工替换路径。这可能破坏字符串长度信息,导致插件无法读取配置。

备注:内容仅供参考。