这是一个好奇的问题,关于在PHP中实现foreach
的方式背后的原因。
考虑:
$arr = array(1,2,3);
foreach ($arr as $x) echo current($arr) . PHP_EOL;
将输出:
2
2
2
我理解foreach
将数组指针倒回到开头;但是,为什么它只增加一次呢?魔术盒里面发生了什么?这只是一个(丑陋的)人工制品吗?
感谢@NickC - 对于其他对zval
和refcount
感到好奇的人,您可以阅读基础知识here
答案 0 :(得分:15)
在第一次迭代之前,$array
被“软复制”,以便在foreach
中使用。这意味着没有完成实际的副本,但只有refcount
的zval的$array
增加到2
。
在第一次迭代中:
$x
。2
。current
来调用$array
。由于引用PHP无法再与循环共享zval
,因此需要将其分开(“硬复制”)。在接下来的迭代中,$array
zval因此不再与foreach
zval相关。因此,它的数组指针不再被修改,current
总是返回相同的元素。
顺便说一下,我在foreach copying behavior上写了一个小摘要。它可能对上下文感兴趣,但它与问题没有直接关系,因为它主要涉及硬拷贝。
答案 1 :(得分:3)
如果我们稍微改变一下代码,请看看有多有趣:
$arr = array(1,2,3);
foreach ($arr as &$x) echo current($arr) . PHP_EOL;
我们得到了这个输出:
2
3
一些有趣的参考文献:
http://nikic.github.com/2011/11/11/PHP-Internals-When-does-foreach-copy.html
http://blog.golemon.com/2007/01/youre-being-lied-to.html
现在,试试这个:
$arr = array(1,2,3);
foreach ($arr as $x) { $arr2 = $arr; echo current($arr2) . PHP_EOL; }
输出:
2
3
1
这确实非常好奇。
那是怎么回事:
$arr = array(1,2,3);
foreach ($arr as $x) { $arr2 = $arr; echo current($arr) . ' / ' . current($arr2) . PHP_EOL; }
echo PHP_EOL;
foreach ($arr as $x) { $arr2 = $arr; echo current($arr2) . ' / ' . current($arr2) . PHP_EOL; }
输出:
2 / 2
2 / 2
2 / 2
2 / 2
3 / 3
1 / 1
看起来发生的事情就像在NickC回答中所写, plus 将数组作为参数传递给current
函数的事实,因为它通过引用传递 ,里面的东西会修改作为参数传递给它的数组......
答案 2 :(得分:1)
这是使用php 5.3进行代码操作码分析的结果。
请参阅此示例:http://php.net/manual/en/internals2.opcodes.fe-reset.php
操作次数:15 已编译的变量:!0 = $ arr,!1 = $ x
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > INIT_ARRAY ~0 1
1 ADD_ARRAY_ELEMENT ~0 2
2 ADD_ARRAY_ELEMENT ~0 3
3 ASSIGN !0, ~0
3 4 > FE_RESET $2 !0, ->13
5 > > FE_FETCH $3 $2, ->13
6 > ZEND_OP_DATA
7 ASSIGN !1, $3
8 SEND_REF !0
9 DO_FCALL 1 'current'
10 CONCAT ~6 $5, '%0A'
11 ECHO ~6
12 > JMP ->5
13 > SWITCH_FREE $2
14 > RETURN 1
有关详细信息,请参阅NikiC的答案,但是在第8行看到!0在循环中永远不会改变。(5-12)