【问题标题】:Weird PHP behaviour - What's going on? [duplicate]奇怪的 PHP 行为 - 发生了什么? [复制]
【发布时间】:2012-04-29 09:48:55
【问题描述】:

可能重复:
Strange behavior Of foreach

最近在一个 PHP 应用程序中遇到了这个错误。不知道发生了什么。

基本上,当使用两个foreach(一个带&,一个不带)的组合时,它就会出现。

这是重现问题的测试代码:

$items = array(

    array('id'=>1, 'name'=>'foo', 'value'=>150),

    array('id'=>2, 'name'=>'bar', 'value'=>190)
);


foreach($items as &$item)
{

    $item['percentage'] = $item['value'] * 0.75;

}

var_dump($items);   // All Good

foreach($items as $item)
{

    var_dump($item);    // Shows 1st item twice
}

第二个foreach 循环按预期运行该块两次,但$item 仍然停留在第一个项目上。

我知道这可能是由在第一个循环中使用引用 & 引起的,但我不明白为什么它应该表现得像这样..

有什么想法吗?这是一个错误吗?

在 5.3.8、5.3.10 和 5.4 上获得相同的结果

【问题讨论】:

  • 这是重用引用时众所周知的副作用。我敢肯定到处都有重复的问题。 unset($item) 在第一个循环之后。
  • 我很高兴人们宁愿称其为众所周知的副作用而不是错误:)
  • @FelixKling 感谢您的参考。投票结束我自己的问题(不要以为我以前做过)

标签: php reference foreach


【解决方案1】:

首先,这不是 Rasmus 所说的错误。见https://bugs.php.net/bug.php?id=29992

在此,使用 & 修改数组的循环变量的正确实现。

<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // break the reference with the last element

var_dump($arr);   // All Good

foreach($arr as $value) {
   var_dump($value);    // All good
}

?>

【讨论】:

    【解决方案2】:

    这是一种奇怪的 PHP 行为,几乎一直存在,当您混合使用变量作为引用而不是像以前那样引用时,就会发生这种情况。

    我使用如下命名约定来处理它:当我将foreach&amp;$item 一起使用时,我将其命名为&amp;$refItem。这使我无法混合类型。

    【讨论】:

      【解决方案3】:

      在使用 foreach 和引用数组后,您需要取消设置指针。

      http://php.net/unset

      【讨论】:

      • 没错。 PHP manual's foreach page says "$value 的引用和最后一个数组元素即使在 foreach 循环之后仍然存在。建议通过 unset() 销毁它。"。
      • 我以为你写的是reset,而不是unset。赞成,现在我要睡觉了..
      • 你原来的答案没有这么说,即使是现在的也是模棱两可的。 数组指针引用变量是两个不同的东西。
      • @TimCooper 你确实看到了reset,我也看到了。答案已更新
      • 是的,我确实将答案从 reset 更新为 unset,但我只在更新后看到了 cmets,因为我在发布后的第二次更新了它。甚至没有更新日志!
      【解决方案4】:

      这可能是看起来比较理解foreach的问题

      $last_value_of_first_foreach = 1;
      $item = 2;
      $c = 3;
      $item = &$last_value_of_first_foreach ; // Think that this statement is first foreach loop
      // Here $item is pointer to $last_value_of_first_foreach 
      // To Better understanding, let change the name ($reference_to_last_value = $item;)
      

      现在,新循环是

      $item = $c;
      // Here, what it do is update value where the $item pointer refer to 
      // (mean $last_value_of_first_foreach )
      // so, at here $last_value_of_first_foreach has value of $c
      

      现在,回到你的例子,从第一个 foreach 开始,$item 引用到数组的最后一个元素。现在,当您在第二个 foreach 中为 $item 分配一些东西时,它所做的就是在其中放入一些东西。

      在第一个循环结束时 $item 是指向 $items[1] 的指针 第二个循环的第一个 它会将第一个元素推送到 $item 指向的位置(这意味着 $items[1],所以为什么 $items[1] 会被 $items[0] 替换。

      如果您想阻止这个,只需在下次使用前取消设置 $item 变量。

      【讨论】:

        【解决方案5】:

        这是正常的,不是奇怪的行为。只需阅读参考here

        当您在变量前面添加 & 时,您将存储对变量的引用。所以,当你重复使用它时,它也会改变被引用变量的内容。

        foreach($items as &$item) // You have $item here, prefixed with &.
        {
            $item['percentage'] = $item['value'] * 0.75;
        }
        
        var_dump($items);
        
        foreach($items as $item) // And your re-use it here.
        {
            var_dump($item);
        }
        

        要解决这个问题,请在第一个循环中添加 unset($item):

        foreach($items as &$item)
        {
            $item['percentage'] = $item['value'] * 0.75;
            unset($item); // add this, because you use &$item previously.
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-11
          • 1970-01-01
          相关资源
          最近更新 更多