У меня было очень странное поведение с простым PHP-скриптом, который я писал. Я уменьшил его до минимума, необходимого для воссоздания ошибки:
<?php
$arr = array("foo",
"bar",
"baz");
foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);
foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?
?>
Это выводит:
Array
(
[0] => foo
[1] => bar
[2] => baz
)
Array
(
[0] => foo
[1] => bar
[2] => bar
)
Это ошибка или какое-то странное поведение, которое должно произойти?
foreach($x AS &$y){ ... unset($y); }
- это на самом деле на php.net (не знаю где), потому что это большая ошибка.Ответы:
После первого цикла foreach,
$item
все еще остается ссылка на некоторое значение, которое также используется$arr[2]
. Таким образом, каждый вызов foreach во втором цикле, который не вызывается по ссылке, заменяет это значение и, таким образом$arr[2]
, новым значением.Таким образом, цикл 1, значение и
$arr[2]
стать$arr[0]
, что является 'Foo'.Цикл 2, значение и
$arr[2]
стать$arr[1]
, который является «бар».Цикл 3, значение и
$arr[2]
стать$arr[2]
, который является «бар» (из-за цикла 2).Значение baz фактически теряется при первом вызове второго цикла foreach.
Отладка вывода
Для каждой итерации цикла мы будем
$item
выводить значение, а также рекурсивно выводить массив$arr
.Когда первый цикл проходит, мы видим этот вывод:
В конце цикла
$item
все еще указывает на то же место, что и$arr[2]
.Когда второй цикл проходит, мы видим этот вывод:
Вы заметите, как каждый раз, когда массив помещает новое значение
$item
, он также обновляется$arr[3]
тем же значением, так как они все еще указывают на одно и то же местоположение. Когда цикл достигает третьего значения массива, он будет содержать значение,bar
потому что оно было просто установлено предыдущей итерацией этого цикла.Это ошибка?
Нет. Это поведение ссылочного элемента, а не ошибка. Это было бы похоже на запуск чего-то вроде:
Цикл foreach не является особенным по своей природе, в котором он может игнорировать ссылочные элементы. Это просто установка этой переменной на новое значение каждый раз, как если бы вы были вне цикла.
источник
$item
не является ссылкой на$arr[2]
, значение, содержащееся в,$arr[2]
является ссылкой на значение, на которое ссылается$item
. Чтобы проиллюстрировать разницу, вы также можете отключить ее$arr[2]
, и это не повлияет$item
, и запись в$item
нее не повлияет.$item
выходит за рамки при выходе из цикла foreach? Это похоже на проблему закрытия?$item
является ссылкой на$arr[2]
и перезаписывается вторым циклом foreach, как указал animuson.источник
Хотя это официально не может быть ошибкой, на мой взгляд, это так. Я думаю, что проблема здесь в том, что мы ожидаем
$item
выхода из области видимости при выходе из цикла, как это было бы во многих других языках программирования. Однако это не так ...Этот код ...
Дает вывод ...
Как уже говорили другие люди, вы перезаписываете указанную переменную
$arr[2]
вторым циклом, но это происходит только потому, что$item
никогда не выходило за рамки. Что вы, ребята, думаете ... ошибка?источник
{}
блоков в целом. Вот как работает языкБолее простое объяснение, кажется от Расмуса Лердорфа, оригинального создателя PHP: https://bugs.php.net/bug.php?id=71454
источник
Правильное поведение PHP может быть ошибкой УВЕДОМЛЕНИЯ по моему мнению. Если ссылочная переменная, созданная в цикле foreach, используется вне цикла, это должно вызвать уведомление. Очень легко поддаться такому поведению, очень трудно определить, когда это произошло. И ни один разработчик не собирается читать страницу документации по foreach, это не помощь.
Вы должны
unset()
использовать ссылку после цикла, чтобы избежать такого рода проблем. unset () для ссылки просто удалит ссылку без ущерба для исходных данных.источник
это потому, что вы используете директиву ref (&). Последнее значение будет заменено вторым циклом, и это повредит ваш массив. самое простое решение - использовать другое имя для второго цикла:
источник