Как на самом деле работает PHP foreach?Php

Кемеровские программисты php общаются здесь
Ответить
Anonymous
 Как на самом деле работает PHP foreach?

Сообщение Anonymous »

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



Долгое время я предполагал, что foreach работает с самим массивом. Затем я нашел много упоминаний о том, что он работает с копией массива, и с тех пор предположил, что на этом история закончилась. Но недавно я вступил в дискуссию по этому поводу и после небольшого экспериментирования обнаружил, что на самом деле это не на 100% правда.

Позвольте мне показать, что я имею в виду. В следующих тестовых случаях мы будем работать со следующим массивом:

Код: Выделить всё

$array = array(1, 2, 3, 4, 5);
Тестовый пример 1:

Код: Выделить всё

foreach ($array as $item) {
echo "$item\n";
$array[] = $item;
}
print_r($array);

/* Output in loop:    1 2 3 4 5
$array after loop: 1 2 3 4 5 1 2 3 4 5 */
Это ясно показывает, что мы не работаем напрямую с исходным массивом — иначе цикл будет продолжаться вечно, поскольку во время цикла мы постоянно помещаем элементы в массив. Но просто чтобы убедиться, что это так:

Тестовый пример 2:

Код: Выделить всё

foreach ($array as $key => $item) {
$array[$key + 1] = $item + 2;
echo "$item\n";
}

print_r($array);

/* Output in loop:    1 2 3 4 5
$array after loop: 1 3 4 5 6 7 */
Это подтверждает наш первоначальный вывод: во время цикла мы работаем с копией исходного массива, иначе мы увидим измененные значения во время цикла. Но...

Если мы посмотрим в руководство, то обнаружим следующее утверждение:


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


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

Тестовый пример 3:

Код: Выделить всё

// Move the array pointer on one to make sure it doesn't affect the loop
var_dump(each($array));

foreach ($array as $item) {
echo "$item\n";
}

var_dump(each($array));

/* Output
array(4) {
[1]=>
int(1)
["value"]=>
int(1)
[0]=>
int(0)
["key"]=>
int(0)
}
1
2
3
4
5
bool(false)
*/
Итак, несмотря на то, что мы не работаем напрямую с исходным массивом, мы работаем напрямую с указателем исходного массива — об этом говорит тот факт, что указатель находится в конце массива в конце цикла. Но это не может быть правдой - если бы это было так, то тестовый пример 1 зацикливался бы навсегда.

В руководстве PHP также говорится:


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


Что ж, давайте выясним, что это за «неожиданное поведение» (технически любое поведение является неожиданным, поскольку я больше не знаю, чего ожидать).

Тестовый пример 4:

Код: Выделить всё

foreach ($array as $key => $item) {
echo "$item\n";
each($array);
}

/* Output: 1 2 3 4 5 */
Тестовый пример 5:

Код: Выделить всё

foreach ($array as $key => $item) {
echo "$item\n";
reset($array);
}

/* Output: 1 2 3 4 5 */
...ничего неожиданного здесь нет, на самом деле, похоже, это подтверждает теорию «копии источника».



Вопрос

Что здесь происходит? Мой C-fu недостаточно хорош, чтобы я мог сделать правильный вывод, просто взглянув на исходный код PHP. Я был бы признателен, если бы кто-нибудь мог перевести его на английский для меня.

Мне кажется, что foreach работает с копией массива, но после цикла устанавливает указатель исходного массива в конец массива.
  • Правильно ли это и вся история?
  • Если нет, то что он на самом деле делает?
  • Есть ли ситуация, когда используются функции, корректирующие указатель массива (, reset() и др.) во время foreach могут повлиять на результат цикла?


Подробнее здесь: https://stackoverflow.com/questions/100 ... ually-work
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Php»