Странное поведение Foreach - PullRequest
40 голосов
/ 11 февраля 2011
<?php
  $a = array('a', 'b', 'c', 'd');

  foreach ($a as &$v) { }
  foreach ($a as $v) { }

  print_r($a);
?>

Я думаю, что это нормальная программа, но я получаю вывод:

Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => c
)

Может кто-нибудь объяснить мне это?

Ответы [ 2 ]

88 голосов
/ 11 февраля 2011

Это хорошо документированное поведение PHP. См. Предупреждение на странице foreach php.net

Предупреждение

Ссылка $ value и последний элемент массива остается даже после цикла foreach .Уничтожить его рекомендуется с помощью unset ().

$a = array('a', 'b', 'c', 'd');

foreach ($a as &$v) { }
unset($v);
foreach ($a as $v) { }

print_r($a);

РЕДАКТИРОВАТЬ

Попытка пошагового руководства к тому, что на самом деле происходитздесь

$a = array('a', 'b', 'c', 'd');
foreach ($a as &$v) { }   // 1st iteration $v is a reference to $a[0] ('a')
foreach ($a as &$v) { }   // 2nd iteration $v is a reference to $a[1] ('b')
foreach ($a as &$v) { }   // 3rd iteration $v is a reference to $a[2] ('c')
foreach ($a as &$v) { }   // 4th iteration $v is a reference to $a[3] ('d')

                          // At the end of the foreach loop,
                          //    $v is still a reference to $a[3] ('d')

foreach ($a as $v) { }    // 1st iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[0] ('a').
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'a'.
foreach ($a as $v) { }    // 2nd iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[1] ('b').
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'b'.
foreach ($a as $v) { }    // 3rd iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[2] ('c').
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'c'.
foreach ($a as $v) { }    // 4th iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[3] ('c' since 
                          //       the last iteration).
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'c'.
3 голосов
/ 03 июля 2015

Первый цикл foreach не вносит никаких изменений в массив, как мы и ожидали.Однако это заставляет $v назначать ссылку на каждый из элементов $a, так что к моменту окончания первого цикла $v фактически является ссылкой на $a[2].

Как только начинается второй цикл, $v назначается значение для каждого элемента.Однако $v уже является ссылкой на $a[2];, поэтому любое присвоенное ему значение будет автоматически скопировано в последний элемент массива!

Таким образом, во время первой итерации $a[2] станетноль, затем один, а затем еще один, будучи эффективно скопирован на себя.Чтобы решить эту проблему, вы всегда должны сбрасывать значения переменных, которые вы используете в циклах foreach по ссылкам, или, что еще лучше, вообще не использовать первые.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...