【PHP - 雷】foreach 與 reference 的雷
26

前言

前陣子公司定期技術研討會時,有人提出了一個問題。

$arr = [1, 2, 3]; 

foreach ($arr as &$a) {}
foreach ($arr as $a) {}

var_dump($arr); 

考慮以上程式碼執行結果,試問陣列 $arr 在執行結束後的值會是如何?

註:執行環境 PHP 7.1 without swoole

結果:$arr 的值為 [1, 2, 2]

緣由

在 PHP 中,foreach 結束後,迴圈中的索引值(index)及內容(value)並不會被消滅。

$a = [1, 2, 3]; 
foreach ($a as $v) {}

var_dump($v); // int(3)

foreach ($a as $k => $value) {}

var_dump($k, $value); // int(2), int(3)

同理,foreach ($a as &$v) {} 時,在迴圈結束後 $v 值不會被消滅,其值仍是參考於(referenced by)陣列中的最後一個值,執行範例如下:

$a = [1, 2, 3]; 
foreach ($a as &$v) {}

var_dump($a); 
/*
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  &int(3)
}
*/

如果在迴圈結束後變更 $v 值,則陣列中的最後一個值也會一併被變更。

解決方法

在使用 foreach ($a as &$v) {} 這類寫法後,應手動 unset($v) 以避免潛在問題發生。

$a = [1, 2, 3];
foreach ($a as &$v) {
    // do something... 
}
unset($v);

參考資料

http://php.net/manual/en/control-structures.foreach.php

Warning 中有明確指出此問題。

本帖由 Summer 于 10个月前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 8
CraryPrimitiveMan

以前触发过这个雷,现在写的时候,都是先写上unset

10个月前

上星期去面试的时候遇到过一模一样的笔试题...

10个月前
ChiVincent

@Lichmaker 這個出成筆試題就有點不太厚道了。

我一直在強調,人腦不是、也不該是 compiler,不該用很刁鑽或炫技的考題來測試面試者。

10个月前

只需要在foreach中的value 都不相同就可以完美的避过去了吧

10个月前

@xhh110 +1 从来不会让 foreach 中的使用过的两个参数在其他地方使用

10个月前
ChiVincent

@xhh110 沒錯,只不過要注意在同個變數存在域下不可對同個變數進行賦值,與其檢查這個不如直接用完就隨手 unset

10个月前

@JokerLinly 我也是这样的循环用一样的值在我公司里我分分钟想打死他
@ChiVincent 不检查 强制要求循环不能用一个名称就好了。。。哈哈哈

10个月前

并不建议使用&,不懂它就最好不要乱用

10个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!