这是不是PHP的bug?

习家家天下 发布于 2014/07/25 08:19
阅读 719
收藏 2
PHP
 $array = [ 1, 2, 3 ];
 echo implode(',', $array), "\n";

 foreach ($array as &$value) { }
 echo implode(',', $array), "\n";

 foreach ($array as $value) { }
 echo implode(',', $array), "\n";


1,2,3
1,2,3
1,2,2

加载中
1
酒逍遥
酒逍遥

不是  bug 第一个 foreach 用的是引用

也就是 在第一个foreach 执行 之后, $value 这个变量就是$array 这个数组最后一个元素的引用

当你执行第二个foreach的时候,数组的元素赋值给value的时候上就是在改变 $array这个数组的最后一个元素的值

所以实际上foreach执行时 数组的值是这么变化的

[1,2,1]    [1,2,2] 注意当变成[1,2,2] 的时候,由于数组最后一个元素的值已经改变,所以赋值给$value 的时候, 依然为 2

所以第二个foreach执行完之后 数组实际上就已经变成了 [1,2,2] 了

0
HandMU
HandMU
没有BUG,你的foreach循环体根本没有语句执行。
0
南湖船老大
南湖船老大

    可以说是bug,也可以说不是。不过PHP开发组表示这不是一个bug,也不会修改这个语言特性。但是这确实是一个bug,很多人都认为就是bug。

    这个特性确实很容易让人困惑,这种很诡异的不符合直觉的代码可以归为bug。虽然可以用opcode来解释,不过这个属于从结果来找原因了。

参考:http://flykobe.com/index.php/2012/02/13/php-foreach-bug/

评论: https://bugs.php.net/bug.php?id=29992

 

南湖船老大
南湖船老大
回复 @夏涌升 : 我看到的评论,主要是说PHP缺少块级作用域,会受到function-level的副作用影响。我觉得还是有道理的
夏涌升
夏涌升
其实是符合代码逻辑的。也说不上是bug。 最后一个&$value在循环之后还存在。 下面的$value就是这个对数组最后一个元素的引用。所以这样。 引用循环后,需要unset掉引用变量。至少每次循环都用不同的变量吧。 我敢打赌,c或c++的应该不会犯这个错误。因为他们使用变量都是很小心的。纯php像我这样的,就比较容易掉进去。
0
fxhover
fxhover

你的数组定义写的不对,下面是php手册的片段:

The weird behaviour of references in foreach remains as long as in PHP 5.2.08  (Linux) and PHP 5.2.9 (Windows XP).   The simplest example would be:

<?php
    $a
= array('a', 'b','c');
    foreach(
$a as &$row){
       
//you don't have to do anything here
      
}
   
print_r($a);
    foreach(
$a as $row){
       echo
"<br />".$row;
    }
?>

the result of print_r will be correct - array of ('a','b','c') values. The second foreach, however, would
produce a,b,b. For all arrays, regardless of size, it would be the n-1 element
 (the element right before the last one). Eg. for 'a','b','c','d','e','f', the script would produce a,b,c,d,e,e.
There are few solutions to this:

1. safest - avoid using references in foreach;  so instead of
<?php
foreach($a as &$row){
   
// do something, eg.
   
$row = trim($row);
}
?>

you would use

<?php
foreach($a as $i=>$row){
   
// do something on row, eg.
   
$row = trim($row);
   
// replace the row in the table
   
$a[$i]=$row;
}
?>
decrease of performance is the cost, however

2. equally safe and more usable - unset the element reference right after the foreach loop with references, eg.:
<?php
    $a
= array('a', 'b','c');
    foreach(
$a as &$row){
       
//you don't have to do anything here
      
}
       unset (
$row)); // it is safe now to use $row again
   
print_r($a);
    foreach(
$a as $row){
       echo
"<br />".$row;
    }
?>

3. use references in both case; it seems that iterations work correct if you use &$row in both loops or don't use it in any:

<?php
    $a
= array('a', 'b','c');
    foreach(
$a as &$row){
       
//you don't have to do anything here
      
}
      
print_r($a);
      
// works OK now even without unset($row)
   
foreach($a as &$row){
       echo
"<br />".$row;
    }
?>

4. use references in foreach only inside functions or methods; outside the function scope it should be safe

<?php
function cleanTable($a){
    foreach(
$a as &$row){
       
$row = trim($row);
    }
}

$a = array('a','b','c');
cleanTable($a);
foreach(
$a as $row){
echo
"<br />".$row;
}
?>

drawbacks: NONE! I Quite contrary, the code looks more tidy.

5. avoid using the same variable names for referenced and non-referenced values; for example, in the first case use &$rRow, and in the second one - $row. It's neither elegant, nor efficient, though, as each new variable lowers the application performance a bit.
<?php
    $a
= array('a', 'b','c');
    foreach(
$a as &$rRow){ // $rRow for 'referenced row'
        //you don't have to do anything here
      
}
   
print_r($a);

    foreach(
$a as $row){
       echo
"<br />".$row;
    }
?>

fxhover
fxhover
回复 @mr-zhuo : 你们都好时髦,我还在5.3。。。
jacky-zhuo
jacky-zhuo
$arrr = []; //这个是php5.4开始添加进来的,数组的精简语法
jingdor
jingdor
这个是 5.4版本以上的 array 所以是[]
0
Hobo
Hobo

这不算bug吧

第一个foreach以后  $value 一直指向 数组最后一个位置 

第二个foreach里 实际上是没循环一次 都把当前值赋给最后一个位置

第二个forecah里打印数组的变化就可以比较清楚了

$array = [ 1, 2, 3 ]; 

 foreach ($array as &$value) { } 
 echo implode(',', $array), "\n"; 

 echo '---------------foreach------------------'.PHP_EOL;
 foreach ($array as $value) {
      echo implode(',', $array), "\n"; 
 } 
 echo '---------------endforeach---------------'.PHP_EOL;
echo implode(',', $array), "\n";




0
吾爱
吾爱

第一个foreach结束时$value变量并未销毁,并且$value此时是$array[2]的一个引用。

当第二个foreach开始时

$array[2] = $value = $array[0] = 1

此时array=[1,2,1]

接着
$array[2] = $value = $array[1] = 2

此时 array = [1,2,2]

接着

$array[2] = $value = $array[2] = 2

结果就成这样了

0
konakona
konakona

首先楼主要明白PHP一个很重要的事:全局变量和局部变量。

所谓全局变量,则一般为$_GET $_POST $_REQUEST 这些PHP自身提供的全局变量,方便给各位开发时在任何地方都可以轻松调用。(试想,在a.php中检查$_GET中的某个值是否合适,然后请求b.php里的方法,在该方法里再次检测$_GET这个值是否合适——这个过程需要传递参数吗?不用)

也可以使用global关键字声明全局变量。例如任何地方 global $global_val = "我是全局变量,你在任何地方都可以echo到我!我的存在是为了方便你在这次生命周期中拥有我";

局部变量是指在函数块内部的变量,例如function me(){$var1 = "我是局部变量,在这个函数之外你echo不出我";}


0
宋辉6
宋辉6
foreach里多了一个&
0
anziguoer
anziguoer
这些东西还是你自己理解了原理在说这些话!
返回顶部
顶部