一次php foreach 變量作用域的踩坑記錄


記錄一次因為對PHP作用域理解不夠導致的小坑。

自測需求的時候發現有一塊地方數據很奇怪,要么該寫的沒有寫入、要么數據被寫入雙份。剝離業務后的代碼大概如下:

<?php

$arr = [
    ['is_checked'=>false,'k'=>1],
    ['is_checked'=>false,'k'=>2],
];

foreach ($arr as  &$item) {
    if ($item['k']==1) {
        $item['is_checked'] = true;
    }
}

echo '<pre>';
foreach ($arr as $item) {
    if ($item['is_checked']) {
        print_r($item);
    }
}

我預想中 上面的代碼應該是只打印arr里的第一條記錄,也就是['is_checked'=>true,'k'=>1],然而實際運行發現打印的是這樣的:

Array
(
    [is_checked] => 1
    [k] => 1
)
Array
(
    [is_checked] => 1
    [k] => 1
)

居然打印了兩條記錄,而且兩條的k都是1。

斷點調試的時候也發現,運行到第二個foreach里的時候 arr確實變成了這樣的數組:

[
    ['is_checked'=>true,'k'=>1],
    ['is_checked'=>true,'k'=>1],
]

仔細看代碼,前面foreach的時候,循環里的變量是用的item,而且是取引用,后面的foreach也是item。我之前是認為這倆item的作用域是不重合的,也就是認為第一個foreach的作用域只在foreach代碼塊里(這點可能是受了golang變量作用域的影響)

然而從結果來看,兩個item應該是一樣的,也就是第二個循環里的item還是前一個循環里的item,而前一個循環里的item是對數組里元素取的引用,也就是說,第一個循環結束后,item還是指向$arr的第二個元素。第二個foreach開始的時候,$arr的第一個元素的值被賦給item,這樣$arr的第二個元素就被第一個元素覆蓋了,所以產生了上面的結果。

來一段代碼驗證下:

<?php

$arr1 = [1,2,3,4];

foreach ($arr1 as  &$item) {
    //do nothing
}

$arr2 = ['a','b','c','d'];

echo '<pre>';
foreach ($arr2 as $item) {
    print_r($arr1);
}

輸出結果:

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => a
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => b
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => c
)
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => d
)

這里應該算是比較基礎的點吧。但是因為對作用域范圍不夠敏感,踩了個坑還排查了半天(實際業務代碼較多,開始沒想到是這里的問題)。

說下這里要注意的點吧

  1. foreach 時候的循環變量盡量不要用同一個變量,尤其是涉及到取引用的
  2. 循環變量取引用的,退出循環后,最好是unset掉,防止后面不小心改掉了該數據


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM