PHP array_filter 后返回 JSON 对象,前端收不到数组
array_filter 保留原键,索引不连续时 json_encode 会输出对象。在列表字段上使用 array_values,并检查业务键、空值过滤与 JSON 编码选项。
接口没有筛选条件时返回 [] 形式的列表,加上筛选条件后却变成带数字键的 {},前端原来的数组处理随之失效。对于普通 PHP 数组,先看过滤后保留下来的键:
<?php
$items = ['alpha', 'beta', 'gamma'];
$filtered = array_filter($items, function ($value) {
return $value !== 'beta';
});
echo json_encode($filtered);
// 预期 {"0":"alpha","2":"gamma"}
array_filter 保留原键,过滤掉中间元素后出现空档;json_encode 因而把它编码为对象。本文针对 PHP 7/8 的普通数组和默认 JSON 编码,不把所有框架响应类型变化都归为这一原因。
列表的键必须从 0 开始连续排列
PHP 数组同时能表达列表和键值映射。JSON 数组要求按列表方式编码;当 PHP 数组的键不是从 0 开始的连续数字序列时,编码结果会显式带上键,形成 JSON 对象。
这解释了为什么同一个接口有时正常:过滤没有移除元素,或者只移除了尾部元素时,键可能仍连续。移除首项或中间项则更容易改变结果形状。应检查过滤后实际保留的键,数组长度不足以判断编码形状。
排查时在受控样例中查看 array_keys($filtered),再对比编码前后的结构。不要先在前端把所有对象都转成数组,否则可能掩盖后端接口不稳定的问题,也可能误处理本来就应该是映射的数据。
在列表字段的输出边界重新编号
如果该字段的契约就是列表,在过滤完成后对这一层调用 array_values:
<?php
$items = ['alpha', 'beta', 'gamma'];
$filtered = array_filter($items, function ($value) {
return $value !== 'beta';
});
$payload = ['items' => array_values($filtered)];
$json = json_encode($payload);
if ($json === false) {
throw new RuntimeException(json_last_error_msg());
}
echo $json;
// 预期 {"items":["alpha","gamma"]}
array_values 返回重新使用数字索引的值列表。它只处理当前这一层,不会自动修正所有嵌套数组,也不会恢复已经被过滤掉的元素。编码失败仍须单独处理,不能用重新编号代替 JSON 错误检查。
应用到框架响应时,保留原来的响应构造方法,只调整应为列表的字段值。例如将传入响应的 products 改为 array_values($filteredProducts);这个变量应是过滤后的普通数组。不要把整个响应对象连同状态字段一起重新编号。
不要丢掉有业务含义的键
如果原键是产品 ID、配置名称或其他业务标识,重新编号会丢失键与数据的对应关系。此时应保留映射契约,或先把标识移入每条记录的显式字段,再输出列表;不能只为满足前端调用数组方法就直接删掉键。
另外,不带回调的 array_filter 会移除 PHP 认定为空的值,包括零、字符串 "0"、false 和空字符串。若只想排除 null,应写明确的判断:
$filtered = array_filter($items, function ($value) {
return $value !== null;
});
是否重新编号与“哪些值应被移除”是两项独立决定。重新编号不能挽回错误过滤的值,修复时要同时检查筛选条件。
还要排除 JSON_FORCE_OBJECT 等强制对象编码选项。默认编码下,重新编号后的空列表应得到 [];显式要求对象编码时,结果形状会不同。
验证应覆盖保留全部、移除首项、移除中间项、仅剩一项和全部移除,并在前端检查字段确实为数组、元素顺序与标识正确。保留调整前的响应样例用于回退;如果客户端已经依赖数字对象键,需要协调契约变更,不能只看 JSON 是否能解析。


