这是一个简单的循环
$list = array("A", "B", "C","D");
foreach ($list as $var) {
print(current($list));
}
输出(demo)
BBBB // Output for 5.2.4 - 5.5.0alpha4
BCD // Output for 4.4.1
AAAA // Output for 4.3.0 - 4.4.0, 4.4.2 - 5.2.3
问题:
foreach
制作的,我也应该AAAA
而不是在当前的PHP
稳定版本中获取注意*我知道我可以简单地使用print $var
但是来自PHP DOC
current - 返回数组中的当前元素 current()函数只返回内部指针当前指向的数组元素的值。它不会以任何方式移动指针。如果内部指针指向超出元素列表末尾或数组为空,则current()返回FALSE。
更新1 - 新观察
感谢Daniel Figueroa:只需将current
包裹在一个函数中,就会得到不同的结果
foreach ( $list as $var ) {
print(item($list));
}
function item($list) {
return current($list);
}
输出(Demo)
BCDA // What the hell
问题:
foreach
输出? 更新2
$list = array("A","B","C","D");
item2($list);
function item2($list) {
foreach ( $list as $var ) {
print(current($list));
}
}
输出(See Demo)
AAAA // No longer BBBB when using a function
问题:
AAAA
外部和BBBB
答案 0 :(得分:18)
为什么以B开头?
由于5.2 foreach
(可靠地)在循环体启动之前使数组指针前进。另请参阅FE_RESET
操作码。
$list = array("A", "B", "C","D");
foreach ($list as $var) {
break;
}
var_dump(current($list));
输出:
B
这可能与ZEND_OP_DATA
伪操作码的工作原理有关(实际上没有记录)。
为什么current()
会继续给出相同的值?
在循环开始之前,foreach
会创建对您循环的数组的内部引用。一旦进入循环,每当修改或通过引用传递数组变量时,通过复制数组结构(但不是元素)将内部引用与变量解除关联。此复制的值保留数组指针(之前已通过循环初始化修改)。
此行为也表现为更具破坏性的unset()
操作:
$list = array('A', 'B', 'C', 'D');
foreach ($list as $key => $val) {
echo $val;
unset($list[1], $list[2], $list[3]);
}
echo "\n", print_r($list, true), "\n";
输出:
ABCD
Array
(
[0] => A
)
将循环变量传递给函数
这是另一个有趣的场景:
$list = array('A', 'B', 'C', 'D');
function itm($arr)
{
return current($arr);
}
foreach ($list as $item) {
print itm($list);
}
var_dump(current($list));
输出:
BCDA
bool(false)
这次,数组按值传递,因此其数组结构(而不是元素)被复制到函数的$arr
参数中。与前面的示例不同,循环的内部引用和$list
符号之间没有解除关联,因为复制发生在函数范围内。
最后"A"
这是foreach
迄今为止最神秘的行为,只能在这些情况下见证。在最后一个循环迭代中,数组指针似乎重绕到第一个项目;看起来因为在循环结束时它显然超出了元素的末尾(正如你可以从输出的最后一行看到的那样)。
这可能与在foreach
结束时执行的SWITCH_FREE
操作码有关。
那么为什么在函数中放置foreach
会使它与众不同?
请注意以下代码:
function item2($arr)
{
foreach ($arr as $var) {
print(current($arr));
}
var_dump(current($arr));
}
$list = array("A","B","C","D");
item2($list);
输出:
AAAA
string(1) "A"
在这种情况下,foreach
的内部引用初始化为数组的副本(因为它具有引用> 1),因此立即与$arr
符号解除关联。
会变得更糟吗?
当然!当您开始使用引用或在同一变量上嵌套多个foreach
循环时,您甚至可以获得更糟糕的结果。
那么如何才能获得一致的结果?
使用Iterators
或不依赖于在foreach
操作期间从引用数组变量获取一致值。
答案 1 :(得分:3)
FROM PHP.net
current()函数只返回数组元素的值 目前正由内部指针指向。它不是 以任何方式移动指针
然后:使用next()
$list = array("A", "B", "C","D");
foreach ($list as $var) {
print(current($list));
next($list);
}
注意:第一个元素不会被打印,因为foreach将指针移动到数组的第二个元素:)
此示例将解释完整行为:
$list = array("A", "B", "C","D");
foreach ($list as $var) {
if(!isset($a)) reset($list); $a = 'isset';
print(current($list));
next($list);
}
out put是ABCD
请注意:
As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior.
编辑:我想分享我新的令人困惑的发现!!!
<强>例1:强>
$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
current($list_copy);
echo current($list);
//next($list);
}
输出:AAAA
示例2:
$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
current($list_copy);
echo current($list);
next($list);
}
输出:ABCD
在foreach中调用current()函数,即使对于另一个数组,也会影响foreach行为......
<强>示例3:强>
$list = array("A", "B", "C","D");
$refcopy = &$list;
foreach ($list as $key => $val) {
if(!isset($a)) { $a = 'isset'; reset($list); }
echo current($list);
next($list);
}
输出:ACD(WOW! B 缺失)
示例:4
$list = array("A", "B", "C","D");
$refcopy = &$list;
foreach ($list as $key => $val) {
echo current($list);
next($list);
}
输出:BCD
无法准确确定foreach循环内部会发生什么!!!
答案 2 :(得分:2)
嗯,我没有真正的线索,为什么会这样,但我怀疑它可能与如何评估/处理任务有关。为了好玩,我尝试了这个并导致了另一个incorrect
行为:
$arr = array('A', 'B', 'C', 'D');
function itm($val) {
return current($val);
}
foreach ($arr as $item) {
print itm($arr);
}
结果:BCDA
所以我的猜测是,这里发生的是函数调用强制评估电流以正确的方式发生。我获得BCDA而不是ABCD的原因可能是因为首先内部指针递增(指向B)然后在en中它重置回A点。
中可能值得注意这一行请注意,赋值会将原始变量复制到新变量(按值分配),因此更改为1将不会影响另一个变量。如果您需要在紧密循环内复制类似大型数组的内容,这也可能具有相关性。
我想这并不算作答案,但我喜欢你的问题,想要贡献一点。
答案 3 :(得分:1)
即使数组的副本是由foreach制作的,我也应该获得AAAA,但是在当前的PHP稳定版本中没有得到它
由于我在这里找不到这个问题的答案,我会(尝试)解释。
在foreach
$list
的第一次迭代未实际复制之前。只有$list
的{{1}}才会增加到2.所以在第一次迭代时:$list
的第一个值将被复制到$var
中,指针将移动到第二个元素将$list
和$list
的实际副本。因此,当您将current
指针指向第二个元素但在第二个和更远的迭代时,它永远不会被修改,因为$list
的实际副本存在,因此current
始终会输出第二个元素。
修改强>
我和debug_zval_dump
一起玩,以了解这个非常意想不到的行为:
<pre>
<?php
$list = array("A", "B", "C","D");
echo '<h1>Ref count before entering foreach:</h1><br>';
debug_zval_dump($list); echo '<br><br>';
$i = 0;
echo '<h1>Ref count in foreach:</h1><br>';
foreach ($list as $var) {
$i++;
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
echo '<br>';
}
$list = array("A", "B", "C","D"); //re-assign array to avoid confusion
echo '<h1>Ref count before entering foreach that calls method "item" and passes array by value:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by value:</h1><br>';
foreach ( $list as $var ) {
$i++;
item($list, $i);
}
function item($list, $i) {
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
}
$list = array("A", "B", "C","D"); //re-assign array to avoid confusion
echo '<h1>Ref count before entering foreach that calls method "item" and passes array by reference:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by reference:</h1><br>';
foreach ( $list as $var ) {
$i++;
itemWithRef($list, $i);
}
function itemWithRef(&$list, $i) {
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
}
得到以下输出:
Ref count before entering foreach:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Ref count in foreach:
Iteration #1: array(4) refcount(3){ [0]=> string(1) "A" refcount(2) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Iteration #2: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(2) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Iteration #3: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(2) [3]=> string(1) "D" refcount(1) }
Iteration #4: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(2) }
Ref count before entering foreach that calls method "item" and passes array by value:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Ref count in foreach that calls method "item" and passes array by value:
Iteration #1: array(4) refcount(5){ [0]=> string(1) "A" refcount(2) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Iteration #2: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(2) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Iteration #3: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(2) [3]=> string(1) "D" refcount(1) } Iteration #4: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(2) } Ref count before entering foreach that calls method "item" and passes array by reference:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Ref count in foreach that calls method "item" and passes array by reference:
Iteration #1: array(4) refcount(1){ [0]=> string(1) "A" refcount(4) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(3) } Iteration #2: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(4) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(3) } Iteration #3: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(4) [3]=> string(1) "D" refcount(3) } Iteration #4: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(4) }
输出有点令人困惑。
在第一个示例中,foreach
创建了$list
的内部副本,因此引用计数为2(结果为4,因为debug_zval_dump
添加了一个refCount
)。在第二个示例中(按值传递)refCount
增加到3,因为$list
被复制用于函数。在第三个示例中,计数保持为1,因为$list
是按值传递的。我需要一些时间来实现原因。如果你从这个结果份额中得到了重点。
我只能说,当我们通过值foreach
传递数组时传递的是迭代的数组,但是当通过引用传递时,它采用了原始 $list
。问题是:为什么foreach
传递了那个数组?
答案 4 :(得分:1)
谎言时使用的代码。即使字面上它看起来可能是相同的代码,但变量不是(http://3v4l.org/jainJ)。
要回答您的实际问题,要获得一致的结果,请使用正确的工具。
如果需要带有数组值的变量,请指定它:
$list = array(....);
如果您需要获取 数组的当前值,请在 foreach
之前使用:
$current = current($list);
因为在foreach
内部,这可能是相同的变量名,但值会有所不同(想象一下,你正在迭代!)。
如果每次迭代需要当前值,请使用它:
foreach ($list as $current) {
...
}
请参阅$current
?
对于日志:将变量作为函数参数传递使其成为新变量。即使是参考(也就是解释)。
如有疑问,请勿使用PHP引用。或者甚至不是变量:http://3v4l.org/6p5nZ
答案 5 :(得分:0)
很好的指出。但似乎内存指向不同版本的PHP的问题。 此外,当前仅提供当前位置,您没有在任何地方增加(导航),因此无法获得正确的输出。随着不同版本的PHP解释下一个和阵列的起点以不同的方式解决这个问题,可能是在一个条件下循环内的重置。 (通过循环然后使用当前的方式,下一个prev不是一个好方法,因为已经有var中的对象:)你选择的是什么? 这是让它运作的一种方式:
<?php
$list = array("A", "B", "C","D");
$flag =0;
foreach ($list as $var) {
if($flag==0)
{
reset($list);
$flag=1;
}
print(current($list));
next($list);
}
输出为ABCD。 见http://3v4l.org/5Hm5Y
答案 6 :(得分:-1)
$list = array("A", "B", "C","D");
foreach ($list as $var) {
echo $var;
}
应该这样做。
答案 7 :(得分:-1)
使用这你已经知道发生了什么!
$list = array('A', 'B', 'C','D');
foreach ($list as $var) {
var_dump(current($list));
}
可能会帮助你!