PHP 7.0+ 将 foreach(null) 从 Warning 升级为 Fatal error,因类型校验变严;常见 null 来源包括未提交表单字段、json_decode 失败、未预加载关联模型等;安全做法是用 is_array($data) && !empty($data) 或显式判空,而非依赖 (array) 强转兜底。

foreach(null) 直接报 Fatal error 是 PHP 版本升级的硬性限制
PHP 7.0+ 把 foreach() 的参数类型校验从 Notice 级别警告 升级为 Fatal error。也就是说,传入 null 不再只是“提醒你错了”,而是直接中断脚本执行:Fatal error: Uncaught TypeError: foreach(): Argument #1 ($array) must be of type array|object, null given。这不是 bug,是语言层面对类型安全的强制收紧。
PHP 5.x 时代它只抛 Warning: Invalid argument supplied for foreach(),脚本还能往下跑;但 7.0 起,只要参数不是数组或对象,就立刻崩溃。所以老代码迁移到新环境时,这个错误会突然爆发——根源不在逻辑,而在类型契约变严了。
常见 null 来源:不是你写了 null,而是你“没拿到东西”
绝大多数 foreach(null) 错误,源头根本不是手动赋值 $data = null,而是以下几种“看似有、实则空”的场景:
-
$_POST["checkbox_group"]在表单首次加载时未提交,直接读取返回null(不是空数组) -
json_decode($json)解析失败(比如格式错误、UTF-8 BOM 干扰),返回null,而非[] - Laravel 中未预加载关联模型,
$user->posts是null而非空集合 - 数据库查询无结果,
$stmt->fetchAll()返回false或null(取决于驱动和错误模式),被误当数组遍历 - 配置文件读取失败(如
parse_ini_file()路径错/权限不足),返回false,强转成数组后仍是无效结构
为什么 isset() 不够,is_array() 也不保险?
单独用 isset($data) 拦不住:如果 $data 是已声明但值为 null 的变量,isset() 返回 false,看似能跳过,但若你写成 if (isset($data)) { foreach($data as ...) },那没问题;可一旦漏掉判断、或在 else 分支里又用了 $data,照样崩。
立即学习“PHP免费学习笔记(深入)”;
而 is_array($data) 对 null 返回 false,看似安全,但它对 stdClass、SimpleXMLElement 或某些扩展返回的“伪数组对象”也返回 false——而这些值其实支持 foreach。反过来,如果误判它们“不可遍历”而跳过,又可能丢数据。
真正稳的组合是:is_array($data) && !empty($data)(确保非空数组),或更通用的:is_array($data) || is_object($data) + 额外校验(比如对象是否实现了 Traversable)。
最简兼容写法:(array) 强制转换不是银弹,但能兜底
写成 foreach ((array) $data as $k => $v) 是很多老项目沿用的“保命写法”。它把 null、false、0、"" 全转成空数组,循环体一次都不进,不会报错。
但它有代价:
- 掩盖问题:你本该知道
$data为什么是null,但现在它静默变成[],调试时更难定位源头 - 语义丢失:
(array) null得到[],但(array) "hello"得到[0 => "hello"],字符串被当成单元素数组——这通常不是你想要的行为 - 对象不转化:
(array) new StdClass得到空数组,但如果是自定义对象,可能丢失属性或触发意外逻辑
所以它适合快速修复线上报错,不适合长期依赖。生产环境应优先明确数据契约,而不是靠类型擦除糊弄过去。
真正容易被忽略的是:错误往往不出现在 foreach 那一行,而出现在它上游三四个函数调用之外——比如一个 API 客户端方法没校验响应体,就把 null 塞给了业务层的遍历逻辑。查错时得顺着调用链往回翻,不能只盯着报错行。



















