PHP 7 меняется на foreach: могу ли я по-прежнему удалять элементы в массиве, в котором я итерирую?

В Документе об изменениях, несовместимых с предыдущими версиями PHP 7, говорится следующее о foreach:

При использовании в режиме по значению по умолчанию foreach теперь будет работать с копией повторяемого массива, а не с самим массивом. Это означает, что изменения в массиве, сделанные во время итерации, не повлияют на итерируемые значения.

Я пытаюсь понять, что это значит, и мой главный вопрос: будет ли этот код работать в PHP 7 так же, как и в PHP 5.6?

foreach($array as $elementKey => $element) {
    if ($element == 'x') {
        unset($array[$elementKey]);
    }
}

Мои два вопроса:

  1. Будет ли этот код работать?

  2. Если да, можете ли вы объяснить (может быть, на примере), что означает это новое изменение в PHP 7?

Изменить

Я перечитал заявление документа. Я думаю, что это означает, что если вы измените значения элементов ниже в массиве, этих изменений не будет, когда вы доберетесь до этих элементов в итерации. Пример:

$array = ['x', 'y', 'z'];

$new = [];

foreach($array as $element) {
    if ($element == 'x') {
        $array[2] = 'a';
    }

    $new[] = $element;
}

print_r($new);

Однако, когда я запускаю этот пример, кажется, нет никакой разницы в версиях PHP (хотя я никогда раньше не пользовался этим инструментом, поэтому не знаю, как он работает).

Я понимаю, что если я сделаю это по ссылке, я получу a в новом. Иначе не буду. Но это похоже на обе версии.

Что мне действительно нужно знать, так это что такое несовместимость (на примере)?

Изменить 2

ссылка на ответ, предложенная @NikiC, содержит остальную часть истории, которую я искал:

В большинстве случаев это изменение прозрачно и не имеет никакого другого эффекта, кроме повышения производительности. Однако есть один случай, когда это приводит к другому поведению, а именно к случаю, когда массив был заранее ссылкой:

$array = [1, 2, 3, 4, 5];
$ref = &$array;
foreach ($array as $val) {
    var_dump($val);
    $array[2] = 0;
}
/* Old output: 1, 2, 0, 4, 5 */
/* New output: 1, 2, 3, 4, 5 */

Раньше итерация массивов ссылок по значению была частным случаем. В этом случае дублирования не произошло, поэтому все изменения массива во время итерации будут отражены в цикле. В PHP 7 этот особый случай исчез: итерация массива по значению всегда будет продолжать работать с исходными элементами, не обращая внимания на любые модификации во время цикла.

Этот ответ объясняет редкий «особый случай», когда в разных версиях все работает по-разному в отношении foreach работы с копией массива.


person userlite    schedule 03.01.2016    source источник


Ответы (1)


Все это означает, что теперь вам нужно явно указать, что вы хотите сослаться на массив, который вы итерируете сейчас.

Однако в вашем примере кода вы все равно ссылаетесь на корневой массив, поэтому он будет работать независимо от того, передаете ли вы его по ссылке.

<?php
$array = ['x', 'y', 'z'];

foreach($array as $elementKey => $element) {
    if ($element=='x') {
        unset($array[$elementKey]);
    }
}

var_dump($array); // lists 'y' and 'z'

Лучший пример. В этом случае мы меняем значение внутри foreach без ссылки. Таким образом, локальные изменения будут потеряны:

<?php
$array = ['x', 'y', 'z'];

foreach($array as $element) {
    if ($element=='x') {
        $element = 'a';
    }
}

var_dump($array); // 'x', 'y', 'z'

Vs по ссылке, где мы объявляем $element ссылкой на элемент массива:

<?php
$array = ['x', 'y', 'z'];

foreach($array as &$element) {
    if ($element=='x') {
        $element = 'a';
    }
}

var_dump($array); // 'a', 'y', 'z'

Вот демонстрация этого кода, работающего в различных версиях PHP.

person Machavity♦    schedule 03.01.2016
comment
Спасибо, Мачавити. Ваши примеры полезны и понятны для меня. Мне все еще интересно, в чем разница между 5.6 и 7. Мне кажется, что все три из этих примеров делают то, что я ожидал в PHP 5.6. - person userlite; 03.01.2016
comment
Действительно, все три примера здесь действуют одинаково в 5 и 7. - person rjdown; 03.01.2016
comment
Я думаю, что основная проблема, из-за которой я пришел сюда, была решена, а именно: я хотел быть уверенным, что концепция удаления элементов в массиве, который я итерирую, не будет отличаться в PHP 7. Моя второстепенная проблема просто понимание того, что на самом деле отличается от других. У большинства других несовместимостей есть примеры в документе, но у этого нет. - person userlite; 03.01.2016
comment
@userlite См. stackoverflow.com/questions/ 10057671/, раздел PHP 7 › Дублирование массива › Параграф 2 и следующий пример. - person NikiC; 03.01.2016
comment
@Machavity: ваш пример кода (когда-то исправленный, чтобы не использовать литерал массива, поэтому он работает в более старых версиях PHP) работает точно так же для всех протестированных версий, когда я его запускаю; и если вы тестируете с эталонным синтаксисом или без него, тест будет одинаковым для всех версий. Какую разницу вы видите для одного и того же кода во всех этих версиях PHP? - person Adam Cameron; 03.01.2016
comment
@AdamCameron См. Комментарий NikiC по ссылке (он понимает PHP намного лучше, чем я). Оказывается, есть крайний случай, когда массив является ссылкой на что-то еще и ведет себя не так, как вы ожидаете. - person Machavity♦; 04.01.2016
comment
Конечно, я понимаю проблему. Моя точка зрения заключалась в том, что я просто не думаю, что ваш ответ - несмотря на все его одобрение и принятие - на самом деле является ответом на заданный вопрос. Возможно, это ответ на другой вопрос ;-) - person Adam Cameron; 04.01.2016