經典面試題:兩個連續的 foreach,第一個使用引用 &$value,第二個不使用,輸出卻出現重複值。本文從 Zend 引擎層面剖析變量綁定機制,徹底講透這一「诡異」行為。
<?php
$arr = ['one', 'two', 'three'];
foreach ($arr as &$value) { echo "Value: $value<br>"; }
foreach ($arr as $value) { echo "Value: $value<br>"; }
// 輸出:
// Value: one
// Value: two
// Value: three
// Value: one
// Value: two
// Value: two <-- 這裡變成了 two!
&$value 使 $value 成為數組最後一個元素($arr[2])的引用別名。循環結束後,$value 仍指向 $arr[2] 的內存地址。as $value,但 PHP 復用 了同名變量 $value 的符號表條目。每次賦值 $value = $arr[$i] 實際上是 修改了 $arr[2] 的內容。$arr[2] = 'one' → 數組變為 ['one','two','one']$arr[2] = 'two' → 數組變為 ['one','two','two']$arr[2] = 'two'(自身賦值) → 保持 ['one','two','two']<?php
$arr = ['one', 'two', 'three'];
foreach ($arr as &$v) {}
var_dump($arr); // 三、最後一個元素被 &$v 引用
unset($v); // 關鍵:斷開引用綁定
foreach ($arr as $v) { echo "$v\n"; } // 輸出正常:one two three
unset($value) —— 斷開符號表綁定,防止污染後續邏輯。$val、$item 等不同名稱。&;PHP 7+ 寫時複製(COW)機制下值傳遞性能損耗可忽略。PHP 7 引入 zend_array 與 ZVAL 新結構,foreach 實現改為外部迭代器,但引用殘留問題依然存在,因為是語言語義層面的設計決策,而非實現細節。PHP 8.1+ 甚至在編譯期發出 Deprecated: foreach by reference 警告,推薦改用 array_walk 或 索引循環修改數組。
理解引用與符號表的綁定關係,是寫出健壯 PHP 代碼的關鍵。下次遇到「莫名其妙」的數組值變化,第一時間檢查上文是否有未清理的 &$var。