在Web开发中,我们经常需要把PHP里的多维数组、关联数组等复杂结构通过HTML表单发给浏览器,再随提交请求还原回PHP。直接使用多个同名input虽然能传递一维列表,但遇到嵌套数组或混合类型时就会力不从心,而且明文暴露数据结构还存在被篡改的风险。

为什么普通表单字段难以承载复杂数组
HTML表单提交的每一个字段本质上都是键值对,值只能是字符串。当PHP收到 $_POST 时,若字段名写成 data[] 形式,PHP会把它整理成索引数组;写成 data[key] 则会变成关联数组。但这种语法只支持有限的层级,且无法表达布尔、null或对象类型。一旦数组里嵌套了不规则结构,前端手写input就会非常繁琐且容易出错。
另外,如果把 print_r 或 var_export 的结果直接放进隐藏域,不仅体积大,还含有换行与特殊字符,在HTML里必须转义,否则会破坏页面结构。更关键的是,用户可以利用浏览器开发者工具随意修改隐藏域内容,服务端若不做校验就信任,会造成越权或逻辑绕过。
使用json与base64进行安全序列化
最实用的方案是把复杂数组在PHP端用 json_encode 转成JSON字符串,再用 base64_encode 编码为纯ASCII文本,这样就能安心放在隐藏域中。base64后的内容不含引号、尖括号等HTML敏感字符,配合 htmlspecialchars 输出即可避免XSS。示例如下:
<?php
$complex = [
'user' => ['id' => 12, 'roles' => ['admin', 'editor']],
'flag' => true,
'meta' => null
];
$encoded = base64_encode(json_encode($complex));
?>
<form method="post">
<input type="hidden" name="payload" value="<?php echo htmlspecialchars($encoded); ?>">
<button type="submit">提交</button>
</form>
提交后,PHP端先base64_decode再json_decode即可恢复数组。注意要检查JSON解码是否成功,避免传入非法数据导致null。
<?php
if (isset($_POST['payload'])) {
$json = base64_decode($_POST['payload'], true);
if ($json === false) {
die('非法数据');
}
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
die('解析失败');
}
// 此时 $data 与原数组结构一致
var_dump($data);
}
?>
防止用户篡改的签名校验
base64和json只是传输编码,并不提供防篡改能力。如果数组里包含权限信息,用户把隐藏域里的base64串解码、改掉roles再编码回去提交,服务端就会中招。为此可以引入签名:服务端用密钥对编码串做哈希,表单里多传一个sig字段。
<?php
$secret = 'my_secret_key';
$encoded = base64_encode(json_encode($complex));
$sig = hash_hmac('sha256', $encoded, $secret);
?>
<input type="hidden" name="payload" value="<?php echo htmlspecialchars($encoded); ?>">
<input type="hidden" name="sig" value="<?php echo htmlspecialchars($sig); ?>">
接收时重新计算签名并对比,不一致则拒绝。这样即便用户改了payload,没有密钥也无法生成正确sig。
<?php
$secret = 'my_secret_key';
$encoded = $_POST['payload'] ?? '';
$sig = $_POST['sig'] ?? '';
$expect = hash_hmac('sha256', $encoded, $secret);
if (!hash_equals($expect, $sig)) {
die('数据被篡改');
}
$data = json_decode(base64_decode($encoded), true);
?>
与前端原生表单数组的对比
有些场景可以用纯前端 name="a[b][c]" 写法让PHP自动重组数组,无需编码。但这种方式要求结构高度规则,且无法传递null、布尔等标量类型,也不利于签名。下表列出两者差异:
| 方案 | 适用结构 | 安全性 | 体积 |
|---|---|---|---|
| 原生表单数组 | 规则多维数组 | 低,易篡改 | 小 |
| json+base64 | 任意复杂结构 | 可签名加固 | 稍大 |
如果仅是传递少量筛选条件,原生写法更简单;若涉及会话态、权限配置等敏感复杂数据,json加签名是更稳妥的选择。
常见误区与注意事项
一个常见错误是忘记对隐藏域做 htmlspecialchars 转义,当base64串偶然出现加号或斜杠被浏览器误解析时,会引发数据损坏。另一误区是信任 json_decode 返回的null,未判断错误就直接使用,导致后续逻辑用空值运行。
此外,base64会使数据膨胀约三分之一,超大数组应考虑压缩或改为Ajax提交JSON。签名密钥不能写死在前端,必须留在服务端,否则就失去了防篡改意义。
PHPHTML_formarray_serialization修改时间:2026-08-05 02:06:25