从 PHP 7.2 开始,each() 函数就被标记为废弃,PHP 8.0 正式将它从语言中移除。如果你的项目升级到 PHP 8.1 后突然报出 Call to undefined function each() 的致命错误,说明代码里还残留着这种老式写法。本文将带你彻底理解 each() 的原始行为,并给出多种符合现代 PHP 风格的替代方案。

先弄清楚 each() 原来到底做了什么
在旧版本 PHP 中,each() 接收一个数组,返回当前内部指针指向的键值对,然后把指针向前移动一位。返回值是一个包含四个元素的数组:键名出现在下标 0 和 key 两个位置,值出现在下标 1 和 value 两个位置。当指针越过数组末尾时,each() 返回 false。
正因为这种“取一次走一步”的特性,老代码里常见两种用法:一种是与 list() 配合在 while 循环里逐行解析数据;另一种是配合 reset() 手动控制遍历进度。看一段典型的旧代码:
<?php
$user = ['name' => 'Tom', 'age' => 28, 'city' => 'Beijing'];
// 旧式写法:PHP 8.1 中会直接报错
reset($user);
while (list($key, $value) = each($user)) {
echo $key . ' => ' . $value . PHP_EOL;
}
?>这段代码的问题不仅在于函数被移除,更在于它依赖数组的内部指针,而 foreach 从 PHP 7 开始已经不再操作内部指针(它会先保存再还原指针状态),所以直接替换时要注意语义差异。
首选方案:用 foreach 完整替代
绝大多数场景下,foreach 都是最佳替代品。它语法简洁、不依赖内部指针、性能也更好。上面的旧代码可以直接改写成:
<?php
$user = ['name' => 'Tom', 'age' => 28, 'city' => 'Beijing'];
foreach ($user as $key => $value) {
echo $key . ' => ' . $value . PHP_EOL;
}
?>foreach 有几个额外优势值得注意。第一,它不会修改数组的内部指针,遍历结束后原数组的指针状态保持不变,这在函数间传递数组时更安全。第二,PHP 7 之后 foreach 对数组的遍历做了引擎级优化,基于内部哈希表直接迭代,比 while 配合函数调用的方式快得多。第三,如果遍历的是大数组且不需要修改原数据,可以用引用遍历避免数组拷贝:
<?php
// 引用遍历,避免大数组复制开销
foreach ($user as &$value) {
$value = strtoupper($value);
}
unset($value); // 遍历后务必释放引用,避免后续误修改
// 遍历完成后重置指针(如果其他代码依赖指针位置)
reset($user);
?>需要特别提醒的是引用遍历后一定要 unset() 引用变量,否则后续同名变量赋值会意外修改数组最后一个元素,这是实际项目中最常见的坑之一。
必须保留指针语义时怎么办
有些遗留代码确实依赖 each() 的“取一个、停一步”特性,比如在多次调用之间分批消费数组。这种情况下,可以用 key()、current() 和 next() 三个函数组合模拟:
<?php
// 自定义函数模拟 each() 的行为
function myEach(array &$arr): ?array
{
$key = key($arr);
if ($key === null) {
return null; // 指针已越过末尾
}
$result = [1 => current($arr), 'value' => current($arr),
0 => $key, 'key' => $key];
next($arr);
return $result;
}
// 使用示例
reset($user);
while (($item = myEach($user)) !== null) {
echo $item['key'] . ' => ' . $item['value'] . PHP_EOL;
}
?>返回 null 而不是 false 是有意为之,因为 PHP 8.1 中数组元素本身不可能为 null 之外的歧义值,这样判断更严格。如果你的旧代码中数组值可能是 false,用 null 作为结束标记还能避免旧 each() 返回 false 时产生的逻辑混淆。
另一种更现代的思路是用生成器改造。把遍历逻辑封装成 Generator 函数,代码既保留惰性求值的特性,又完全不依赖指针:
<?php
function lazyEach(array $arr): Generator
{
foreach ($arr as $key => $value) {
yield [$key, $value];
}
}
foreach (lazyEach($user) as [$key, $value]) {
echo $key . ' => ' . $value . PHP_EOL;
}
?>其他替代方案与选型建议
除了 foreach,PHP 还提供了几个针对性工具。array_walk() 适合对每个元素执行回调处理,不需要返回新数组时比较方便:array_walk($user, function ($value, $key) { echo "$key => $value\n"; });。array_map() 和 array_filter() 则适合转换和过滤,它们都是声明式写法,比命令式循环更直观。
选型时可以参考这样的原则:只是简单遍历就用 foreach;需要变换数据用 array_map;需要边遍历边根据条件筛选用 array_filter;需要逐条处理且有副作用时用 array_walk;跨函数分批消费数据时用生成器。至于模拟 each() 的自定义函数,只建议作为迁移过渡方案,长期保留会让代码显得过时。
最后建议在升级 PHP 8.1 之前,先用静态分析工具扫描整个项目中对 each()、create_function()、get_magic_quotes_gpc() 等已移除函数的调用,提前列出改造清单。改造 each() 时顺手把 while 加 list 的写法统一替换为 foreach,代码可读性和维护性都会明显提升,这也符合现代 PHP 的编码规范趋势。
PHP each函数foreach遍历数组遍历修改时间:2026-09-06 22:10:39