foreach循环中current()的意外行为

Bab*_*aba 39 php arrays foreach loops

这是一个简单的循环

$list = array("A", "B", "C","D");
foreach ($list as $var) {
    print(current($list));
}
Run Code Online (Sandbox Code Playgroud)

输出(演示)

 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
Run Code Online (Sandbox Code Playgroud)

题 :

  • 有人可以解释一下发生什么事吗?
  • 为什么我没有得到ABCD
  • 即使数组的副本是由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);
}
Run Code Online (Sandbox Code Playgroud)

输出(演示)

 BCDA   // What the hell 
Run Code Online (Sandbox Code Playgroud)

题 :

  • 为什么不获得"BBBB"?
  • 如何在函数中包装电流会影响foreach输出?
  • 额外的"A"来自哪里?

更新2

$list = array("A","B","C","D");
item2($list);
function item2($list) {
    foreach ( $list as $var ) {
        print(current($list));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出(见演示)

AAAA // No longer BBBB when using a function
Run Code Online (Sandbox Code Playgroud)

题 :

  • 在函数中运行循环并在函数外部运行它有什么不同,因为你在大多数PHP版本中都有AAAA外部和BBBB函数

Ja͢*_*͢ck 18

为什么从B开始?

因为5.2 foreach(可靠地)在循环体开始之前使数组指针前进.另请参阅FE_RESET操作码.

$list = array("A", "B", "C","D");
foreach ($list as $var) {
    break;
}
var_dump(current($list));
Run Code Online (Sandbox Code Playgroud)

输出:

B
Run Code Online (Sandbox Code Playgroud)

这可能与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";
Run Code Online (Sandbox Code Playgroud)

输出:

ABCD
Array
(
    [0] => A
)
Run Code Online (Sandbox Code Playgroud)

将循环变量传递给函数

这是另一个有趣的场景:

$list = array('A', 'B', 'C', 'D');
function itm($arr) 
{
    return current($arr);
}

foreach ($list as $item) {
    print itm($list);
}
var_dump(current($list));
Run Code Online (Sandbox Code Playgroud)

输出:

BCDA
bool(false)
Run Code Online (Sandbox Code Playgroud)

这次,数组按值传递,因此其数组结构被复制(而不是元素)到函数的$arr参数中.与前面的示例不同,循环的内部引用和$list符号之间没有解除关联,因为复制发生在函数作用域中.

最后一个"A"怎么样?

这是目前最神秘的行为,foreach只能在这种情况下见证.在最后一个循环迭代中,数组指针似乎重绕到第一个项目; 看起来因为在循环结束时它显然超出了元素的末尾(正如你可以从输出的最后一行看到的).

这可能与在SWITCH_FREEa结束时执行的操作码有关foreach.

那么为什么放置foreach一个函数会让它与众不同呢?

请注意以下代码:

function item2($arr) 
{
    foreach ($arr as $var) {
        print(current($arr));
    }
    var_dump(current($arr));
}
$list = array("A","B","C","D");
item2($list);
Run Code Online (Sandbox Code Playgroud)

输出:

AAAA
string(1) "A"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,foreach使用数组的副本初始化该内部引用(因为它具有refcount> 1),因此会立即与$arr符号解除关联.

会变得更糟吗?

当然!当您开始使用引用或foreach在同一变量上嵌套多个循环时,您甚至可以获得更糟糕的结果.

那么我怎样才能获得一致的结果呢?

Iteratorsforeach操作期间使用或不依赖于从引用数组变量获取一致值.