遇到资源类型或循环引用时直接跳过:先用is_resource()跳过资源,json_encode()预检失败则跳过,用is_countable()替代count(),黑名单过滤敏感键,迭代栈替代递归并预筛类型。

遇到资源类型(resource)或循环引用时直接跳过
PHP递归遍历数组时,如果碰到 fopen() 返回的 resource、mysqli 实例、或者存在循环引用的数组(如 $a['parent'] = &$a),会触发致命错误或无限递归。这类值无法序列化,也不能被 json_encode() 处理,更不能用 empty() 或 is_array() 安全判断。
- 先用
is_resource($v)检查是否为资源,是则跳过(continue),不进入递归分支 - 对疑似循环引用的结构,可在递归函数开头加
if (is_object($v) && method_exists($v, '__toString')) { continue; }防止 toString() 触发不可控行为 - 更稳妥的做法:在递归前用
json_encode($v, JSON_THROW_ON_ERROR)尝试预检——失败就跳过,避免后续崩溃 - 别依赖
gettype($v) === 'object'判断,有些对象(如Closure)会直接 fatal error
过滤掉不可比较/不可遍历的特殊值
像 NaN、INF、未初始化的 stdClass 属性、或某些扩展返回的伪类型(如 ffi_cdata),在 foreach 中可能报 Warning 或静默跳过,但递归里若没拦截,会导致逻辑断裂或键丢失。
- 用
!is_scalar($v) && !is_array($v) && !is_object($v)作为兜底条件,匹配到就跳过 - 对浮点数额外加
is_finite($v)判断,排除INF和NaN - 遇到
__isset()不可用的对象属性,不要强行访问,改用property_exists($v, $key)或isset($v->$key)前置校验 - 别在递归体里对
$v直接调用count()——它对非数组/Countable 类型会 warning,应先is_countable($v)
跳过特定键名或路径下的敏感字段
表单或配置数据中常含需跳过的字段,比如 _token、password、file_upload_resource,它们不该参与过滤、验证或日志记录。硬编码跳过比事后清理更安全。
- 递归函数接收一个黑名单数组,如
$skipKeys = ['_token', 'password', 'tmp_file_handle'] - 每次进入新层级时,检查当前键是否在黑名单中:
if (in_array($key, $skipKeys, true)) { continue; } - 路径拼接时(如
$path . '.' . $key),若路径已含敏感段(如'user.credit_card'),可提前return跳出整层 - 注意大小写:用
strtolower($key)统一后再比对,避免'Password'漏判
用迭代栈替代递归前先做类型预筛
当明确知道输入可能含大量特殊类型时,与其在递归里层层判断跳过,不如在构建初始栈时就过滤掉危险项。这能减少无效压栈,也避免栈深度失控。
- 初始化栈前,先对根数组做一轮轻量扫描:
array_filter($input, function($v) { return is_array($v) || is_object($v); }, ARRAY_FILTER_USE_BOTH); - 压入栈的每个任务单元必须带类型标记,例如
['type' => 'array', 'data' => $arr],处理时只对type === 'array'的才展开 - 对
is_object($v)且无Traversable接口的,直接标记为type => 'skip',进栈后立即continue - 别在
while (!empty($stack))循环内反复调用gettype()——它开销不小,提前分类好再进栈
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











