php8需自定义递归函数实现深层嵌套数组转url查询字符串,支持点号或方括号风格、空数组、中文键名及数字/字符串键区分处理,http_build_query无法满足该需求。

PHP8 递归拼接多层数组为 URL 查询字符串,http_build_query 不够用
PHP8 的 http_build_query 默认只支持一维或简单二维数组,遇到深层嵌套(比如 ['user' => ['profile' => ['name' => 'a', 'tags' => [1,2]]]]),它会直接丢弃键名层级、转成扁平索引,或者报错(开启严格模式时)。你真正需要的是保留嵌套结构的「点号」或「方括号」风格拼接,例如 user[profile][name]=a&user[profile][tags][]=1&user[profile][tags][]=2。
手动递归函数必须处理三类键:字符串键、数字键、空数组值
PHP 数组键类型混杂是常见坑源。递归时若不区分,0 和 '0' 可能被当成同一键;数字索引会被自动转成 [],但嵌套中又需保留父级路径。关键逻辑是:每层递归传入当前路径前缀(如 'user[profile]'),再根据子键类型决定拼法。
实操建议:
- 用
is_array($value)判断是否继续递归,而非count($value) > 0(空数组也要输出key[]=) - 对数字键(
is_int($key)),统一追加[],不保留具体数字(URL 规范不依赖索引顺序) - 对字符串键,用
rawurlencode()编码键和值,避免&、=等破坏结构 - 递归调用时,新前缀按
$prefix . '[' . $encodedKey . ']'拼(非点号),因 PHP 原生解析器只认方括号
示例函数:支持深度嵌套 + 空数组 + 中文键名
function buildNestedQuery(array $data, string $prefix = ''): string {
$parts = [];
foreach ($data as $key => $value) {
if (is_array($value)) {
$encodedKey = is_int($key) ? '' : rawurlencode((string)$key);
$newPrefix = $prefix === ''
? ($encodedKey === '' ? '[]' : $encodedKey)
: ($encodedKey === '' ? $prefix . '[]' : $prefix . '[' . $encodedKey . ']');
$parts[] = buildNestedQuery($value, $newPrefix);
} else {
$encodedKey = is_int($key) ? '[]' : rawurlencode((string)$key);
$encodedVal = rawurlencode((string)$value);
$fullKey = $prefix === ''
? $encodedKey
: ($encodedKey === '[]' ? $prefix . '[]' : $prefix . '[' . $encodedKey . ']');
$parts[] = $fullKey . '=' . $encodedVal;
}
}
return implode('&', $parts);
}
调用 buildNestedQuery(['a' => ['b' => ['c' => '测试']]]) 输出 a[b][c]=%E6%B5%8B%E8%AF%95,中文安全,层级清晰。
注意 PHP8 的 array_is_list 不适用于此场景
有人想用 array_is_list 区分「纯数字索引数组」和「关联数组」来决定是否加 [],但实际无效:嵌套中一个 ['items' => [0 => 'x', 1 => 'y']] 是 list,而 ['items' => ['0' => 'x']] 就不是 —— 但两者在 URL 解析时行为一致,都应输出 items[]=x&items[]=y。强行依赖 array_is_list 会导致键名字符串化后误判。老老实实用 is_int($key) 更直接可靠。
最易忽略的是空数组:['filters' => []] 必须生成 filters[]=,否则后端接收不到该字段。递归函数里不能跳过 empty($value) 的数组分支。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











