理解 foreach() 处理中的差异

在某些相对不明显的情况下,foreach() 循环内的代码行为在 PHP 5 和 PHP 7 之间会有所不同。首先,内部有了很大的改进,这意味着在速度上,foreach() 循环内的处理在 PHP 7 下运行会比 PHP 5 快得多。 在 PHP 5 中注意到的问题包括在 foreach() 循环内对数组使用 current()unset() 。其他问题还包括在操作数组本身时通过引用传递值。

如何做…

1.考虑以下代码块:

  1. $a = [1, 2, 3];
  2. foreach ($a as $v) {
  3. printf("%2d\n", $v);
  4. unset($a[1]);
  5. }

2.在 PHP 5和7中,输出如下:

  1. 1
  2. 2
  3. 3

3.然而,如果你在循环之前添加一个赋值,行为就会改变:

  1. $a = [1, 2, 3];
  2. $b = &$a;
  3. foreach ($a as $v) {
  4. printf("%2d\n", $v);
  5. unset($a[1]);
  6. }

4.比较 PHP 5和7的输出:

PHP 5 PHP 7

1

3

<b></b>

1

2

3

5.在PHP 5中,使用引用内部数组指针的函数也会引起不一致的行为。 以下面的代码为例:

  1. $a = [1,2,3];
  2. foreach($a as &$v) {
  3. printf("%2d - %2d\n", $v, current($a));
  4. }

小贴士

每个数组都有一个内部指针,从1开始指向它的当前元素 current() 返回数组中的当前元素。

6.请注意,在 PHP 7 中运行的输出是规范化和一致的:

PHP 5 PHP 7

1 - 2

2 - 3

3 - 0

1 - 1

2 - 1

3 - 1

7.一旦数组引用迭代完成,在 foreach() 循环中添加新元素在PHP 5中也是有问题的。 这一行为在PHP 7中已经变得一致。下面的代码示例演示了这一点:

  1. $a = [1];
  2. foreach($a as &$v) {
  3. printf("%2d -\n", $v);
  4. $a[1]=2;
  5. }

8.我们将观察到以下输出:

PHP 5 PHP 7

1 -

<b></b>

1 -

2 -

9.另一个在PHP 7中解决的PHP 5不良行为的例子是,在通过引用进行数组迭代的过程中,使用了修改数组的函数,如 array_push()array_pop()array_shift()array_unshift()

我们看以下这个例子:

  1. $a=[1,2,3,4];
  2. foreach($a as &$v) {
  3. echo "$v\n";
  4. array_pop($a);
  5. }

10.我们将观察到以下输出:

PHP 5 PHP 7

1

2

1

1

1

2

<b></b>

<b></b>

11.最后,我们还有一种情况,就是通过引用迭代一个数组,并使用嵌套的 foreach() 循环,而这个循环本身也是通过引用迭代同一个数组。在 PHP 5 中,这个结构根本无法工作。在 PHP 7 中,这个问题得到了修正。下面的代码块演示了这种行为:

  1. $a = [0, 1, 2, 3];
  2. foreach ($a as &$x) {
  3. foreach ($a as &$y) {
  4. echo "$x - $y\n";
  5. if ($x == 0 && $y == 1) {
  6. unset($a[1]);
  7. unset($a[2]);
  8. }
  9. }
  10. }

12.下面是输出结果:

PHP 5 PHP 7

0 - 0

0 - 1

0 - 3

<b></b>

<b></b>

0 - 0

0 - 1

0 - 3

3 - 0

3 - 3

如何运行…

将这些代码示例添加到一个PHP文件中,chap_02_foreach.php 。在PHP 5下通过命令行运行该脚本。预期的输出如下:

理解 foreach() 处理中的差异 - 图1

在 PHP 7下运行相同的脚本,注意不同之处:

理解 foreach() 处理中的差异 - 图2

参考

如需了解更多信息,请查阅解决这一问题的RFC,该RFC已被接受。关于该RFC的文章可在以下网址找到:https://wiki.php.net/rfc/php7_foreach