PHP指针的行为出现意外情况

3

在检查Laravel模型之间是否相关的项目中,我注意到PHP出现了一些奇怪的指针行为。以下是一个最小化的示例,可以重现我发现的情况。

<?php

$arr = ['a', 'b', ['c']];


foreach($arr as &$letter) {
    if (!is_array($letter)) {
        $letter = [$letter];    
    }
}

dump($arr);

foreach($arr as $letter) {
    dump($arr);
}

function dump(...$dump) {
    echo '<pre>';
    var_dump($dump);
    echo '</pre>';  
}

起初我预期这个响应中的所有转储都将返回相同的数据:
[ ['a'], ['b'], ['c'] ]

但事实并非如此,我收到了以下回复:
[ ['a'], ['b'], ['c'] ]
[ ['a'], ['b'], ['a'] ]
[ ['a'], ['b'], ['b'] ]
[ ['a'], ['b'], ['b'] ]

一个可运行的示例可以在这里找到。

为什么指针会这样工作?如何在第一个循环中更新$letter而不必执行$arr[$key] = $letter


编辑:由于人们似乎误解了为什么有第二个foreach循环,这是为了表明数组在不重新分配的情况下发生了变化。


@B001ᛦ,这只是一个最简单的例子。我的实际工作不仅仅有一个元素。 - Dexter
在 foreach 循环中输出数组,你期望得到什么结果? - AymDev
正如问题中所述,我不会期望第二个foreach循环显示不同的输出。 - Dexter
2个回答

2
根据PHP文档
在foreach循环之后,$value的引用和最后一个数组元素仍然存在。建议使用unset()销毁它。
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)

// Without an `unset($value)`, `$value` is still a reference to the last item: `$arr[3]`

foreach ($arr as $key => $value) {
    // $arr[3] will be updated with each value from $arr...
    echo "{$key} => {$value} ";
    print_r($arr);
}
// ...until ultimately the second-to-last value is copied onto the last value

/* output:
   0 => 2 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 2 )
   1 => 4 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 4 )
   2 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 )
   3 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 ) */

有趣!所以这与指针无关,而是value在循环完成后不会被销毁。 - Dexter
这与变量作用域有关。foreach没有自己的作用域。简单来说,$value在这种情况下具有脚本/文件作用域,并且在循环后可用于使用。(更多关于作用域的内容请参考)http://php.net/manual/en/language.variables.scope.php - Dmytro

1
首先:PHP没有指针,它有引用。有关更多信息,请参见什么是引用什么不是引用
发生这种情况的原因是foreach循环后,$letter仍然持有对数组的最后一个元素(即[c])的引用。因此,在第二个循环中,您不仅在循环时覆盖了$letter,还覆盖了它所指向的引用。
要解决问题,您需要在第一个循环后使用unset($letter)
$arr = ['a', 'b', ['c']];

foreach($arr as &$letter) {
    if (!is_array($letter)) {
        $letter = [$letter];    
    }
}
unset($letter);   // this is important

dump($arr);

foreach($arr as $letter) {
    dump($arr);
}

function dump(...$dump) {
    echo '<pre>';
    var_dump($dump);
    echo '</pre>';  
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接