Это вопрос любопытства в отношении причин, по которым foreach
реализуется в PHP.
Рассматривать:
$arr = array(1,2,3); foreach ($arr as $x) echo current($arr) . PHP_EOL;
который будет выводить:
2 2 2
Я понимаю, что foreach
перематывает указатели массива на начало; однако почему он увеличивает его только один раз? Что происходит внутри волшебной коробки? Это просто (уродливый) артефакт?
Спасибо @NickC – для всех, кто интересуется zval
и refcount
, вы можете прочитать об refcount
здесь
Прямо перед первой итерацией $array
«мягко скопирован» для использования в foreach
. Это означает, что фактическая копия не выполняется, но только refcount
zval $array
увеличивается до 2
.
На первой итерации:
$x
. 2
. current
вызывается с $array
переданным по ссылке. Из-за ссылки PHP больше не может делиться zval
с циклом, и его нужно разделить («жестко скопировано»). На следующих итерациях $array
zval больше не связан с foreach
zval. Таким образом, его указатель массива больше не изменяется, а current
всегда возвращает один и тот же элемент.
Кстати, я написал небольшую сводку о том, что вы делаете для копирования . Это может быть интересно в контексте, но оно напрямую не связано с проблемой, поскольку в основном речь идет о жестком копировании.
Посмотрите, насколько интересно, если мы немного изменим код:
$arr = array(1,2,3); foreach ($arr as &$x) echo current($arr) . PHP_EOL;
Мы получили этот результат:
2 3
Некоторые интересные ссылки:
http://nikic.github.com/2011/11/11/PHP-Internals-When-does-foreach-copy.html
http://blog.golemon.com/2007/01/youre-being-lied-to.html
Теперь попробуйте следующее:
$arr = array(1,2,3); foreach ($arr as $x) { $arr2 = $arr; echo current($arr2) . PHP_EOL; }
Вывод:
2 3 1
Это очень любопытно.
И как насчет этого:
$arr = array(1,2,3); foreach ($arr as $x) { $arr2 = $arr; echo current($arr) . ' / ' . current($arr2) . PHP_EOL; } echo PHP_EOL; foreach ($arr as $x) { $arr2 = $arr; echo current($arr2) . ' / ' . current($arr2) . PHP_EOL; }
Вывод:
2 / 2 2 / 2 2 / 2 2 / 2 3 / 3 1 / 1
Кажется, что происходит так же, как написано в ответе NickC, плюс тот факт, что при передаче массива в качестве аргумента current
функции, поскольку она передается по ссылке , что-то внутри там модифицирует массив, переданный в качестве аргумента для него …
Это результаты анализа кода кода кода с помощью php 5.3.
См. Этот пример: http://php.net/manual/en/internals2.opcodes.fe-reset.php
количество операций: 15 скомпилированных vars:! 0 = $ arr,! 1 = $ x
line # * op fetch ext return operands --------------------------------------------------------------------------------- 2 0 > INIT_ARRAY ~0 1 1 ADD_ARRAY_ELEMENT ~0 2 2 ADD_ARRAY_ELEMENT ~0 3 3 ASSIGN !0, ~0 3 4 > FE_RESET $2 !0, ->13 5 > > FE_FETCH $3 $2, ->13 6 > ZEND_OP_DATA 7 ASSIGN !1, $3 8 SEND_REF !0 9 DO_FCALL 1 'current' 10 CONCAT ~6 $5, '%0A' 11 ECHO ~6 12 > JMP ->5 13 > SWITCH_FREE $2 14 > RETURN 1
См. Ответ NikiC для получения дополнительной информации, но вы видите на строке # 8, что! 0 никогда не меняются в цикле. (5-12)