// 形式 1:僅獲取值
foreach ($array as $value) { ... }
// 形式 2:同時獲取鍵名與值
foreach ($array as $key => $value) { ... }
foreach 操作的是數組的**副本**(PHP 7+),不會移動原數組的內部指針。但若在循環中修改數組(增刪元素),行為取決於 PHP 版本:
&$value —— 必須手動銷毀$arr = [1, 2, 3];
foreach ($arr as &$v) { $v *= 2; }
unset($v); // 關鍵!否則 $v 仍引用最後一個元素
// 後續代碼中 $v 會意外修改 $arr[2]
最佳實踐:用完引用循環立即 unset($value),或改用 array_map/array_walk 避免引用殘留。
function walk_array($array, $depth = 0) {
foreach ($array as $key => $value) {
echo str_repeat(' ', $depth) . "$key => ";
if (is_array($value)) {
echo "\n";
walk_array($value, $depth + 1);
} else {
echo "$value\n";
}
}
}
function flatten_generator($array) {
foreach ($array as $key => $value) {
if (is_array($value)) {
yield from flatten_generator($value);
} else {
yield $key => $value;
}
}
}
// 記憶體友好,逐個產出
foreach (flatten_generator($hugeNestedArray) as $k => $v) { ... }
| 陷阱 | 現象 | 解決方案 |
|---|---|---|
| 引用殘留修改最後元素 | 循環後 $value 仍引用 $arr[last] | 循環結束後 unset($value) |
| 遍歷時修改數組導致跳過/重複 | 刪除當前元素導致指針偏移 | 先收集待刪除鍵,循環後批量 unset;或反向遍歷 |
| 字符串鍵名隱式轉換 | $arr['01'] vs $arr[1] | 鍵名保持字符串,避免數值索引自動轉換 |
| 對象屬性遍歷順序不確定 | foreach($obj as $k=>$v) | 實現 Iterator 或 ArrayAccess 接口控制順序 |
| 非數組/不可遍歷對象報錯 | Invalid argument supplied for foreach() | 先 is_array() 或 is_iterable() 判斷 |
for ($i=0, $len=count($arr); $i<$len; $i++) 或直接 foreach。& 避免複製,但注意寫時複製機制。$result = []; $result[] = ... 比 array_push 更快。array_map、array_filter、array_reduce 由 C 實現,通常比手寫循環快。// 箭頭函數 + array_map
$doubled = array_map(fn($n) => $n * 2, $numbers);
// array_filter + 箭頭函數
$evens = array_filter($numbers, fn($n) => $n % 2 === 0);
// array_reduce 累加
$sum = array_reduce($numbers, fn($carry, $n) => $carry + $n, 0);
// 解構賦值(PHP 7.1+)
foreach ($users as ['id' => $id, 'name' => $name]) { ... }
foreach 是 PHP 最常用的遍歷構造,理解其副本機制、引用陷阱、修改數組邊界條件,配合現代語法糖(箭頭函數、解構賦值、生成器),能寫出高效、安全、可維護的循環代碼。記住鐵律:**引用循環必須 unset**。