Вот простой цикл
$list = array("A", "B", "C","D"); foreach ($list as $var) { print(current($list)); }
Выход ( демо )
BBBB // Output for 5.2.4 - 5.5.0alpha4 BCD // Output for 4.4.1 AAAA // Output for 4.3.0 - 4.4.0, 4.4.2 - 5.2.3
Вопрос:
foreach
я должен получать AAAA
но не получать это в текущей стабильной версии PHP
Примечание * Я знаю, что могу просто использовать print $var
но из PHP DOC
current – Возвращает текущий элемент в массиве Функция current () просто возвращает значение элемента массива, на который в данный момент указывается внутренний указатель. Он не перемещает указатель. Если внутренний указатель указывает за пределы списка элементов или массив пуст, current () возвращает FALSE.
Обновление 1 – Новое наблюдение
Спасибо Даниэлю Фигероа : просто обернув current
в функции, вы получите другой результат
foreach ( $list as $var ) { print(item($list)); } function item($list) { return current($list); }
Выход ( демо )
BCDA // What the hell
Вопрос:
foreach
? Обновление 2
$list = array("A","B","C","D"); item2($list); function item2($list) { foreach ( $list as $var ) { print(current($list)); } }
Выход ( см. Демонстрацию )
AAAA // No longer BBBB when using a function
Вопрос:
AAAA
снаружи и BBBB
в функции в большинстве версий PHP Почему это начинается с B?
Поскольку 5.2 foreach
(надежно) продвигает указатель массива до начала цикла. См. Также код операции FE_RESET
.
$list = array("A", "B", "C","D"); foreach ($list as $var) { break; } var_dump(current($list));
Вывод:
B
У этого может быть что-то с тем, как ZEND_OP_DATA
псевдо-код ZEND_OP_DATA
(который не документирован).
Почему current()
продолжает давать одинаковое значение?
Перед запуском цикла foreach
создает внутреннюю ссылку на массив, который вы зацикливаете. Как только внутри цикла, всякий раз, когда переменная массива модифицируется или передается по ссылке, внутренняя ссылка диссоциируется из переменной путем создания копии структуры массива (но не элементов). Это скопированное значение сохраняет указатель массива (который ранее был изменен инициализацией цикла).
Это поведение также проявляется при более разрушительной операции unset()
:
$list = array('A', 'B', 'C', 'D'); foreach ($list as $key => $val) { echo $val; unset($list[1], $list[2], $list[3]); } echo "\n", print_r($list, true), "\n";
Вывод:
ABCD Array ( [0] => A )
Передача переменной цикла в функцию
Это еще один интересный сценарий:
$list = array('A', 'B', 'C', 'D'); function itm($arr) { return current($arr); } foreach ($list as $item) { print itm($list); } var_dump(current($list));
Вывод:
BCDA bool(false)
На этот раз массив передается по значению, и поэтому его структура массива копируется (а не элементы) в параметр $arr
. В отличие от предыдущего примера, нет никакой диссоциации между внутренней ссылкой цикла и символом $list
поскольку копия имеет место в области функций.
Как насчет последнего "A"
?
Это, безусловно, самое мистифицирующее поведение foreach
и может быть засвидетельствовано только в этих обстоятельствах. В последней итерации цикла указатель массива, по- видимому, перематывается на первый элемент; по-видимому, потому что в конце цикла он явно указывает за пределы элементов (как вы можете видеть из последней строки вывода).
Это может иметь какое-то отношение к SWITCH_FREE
который выполняется в конце foreach
.
Итак, почему размещение foreach
в функции делает ее другой?
Соблюдайте следующий код:
function item2($arr) { foreach ($arr as $var) { print(current($arr)); } var_dump(current($arr)); } $list = array("A","B","C","D"); item2($list);
Вывод:
AAAA string(1) "A"
В этом случае внутренняя ссылка foreach
инициализируется копией массива (потому что он имеет номер пересчета> 1) и, таким образом, создает немедленную диссоциацию из символа $arr
.
Может ли это ухудшиться?
Конечно! Вы можете получить даже более удачные результаты, когда начнете использовать ссылки или вставьте несколько циклов foreach
в одну и ту же переменную.
Итак, как я могу получить согласованные результаты?
Используйте Iterators
или не полагайтесь на получение согласованного значения от ссылки на переменную массива во время операции foreach
.
ОТ PHP.net
Функция current () просто возвращает значение элемента массива, на который в данный момент указывается внутренний указатель. Он не перемещает указатель каким-либо образом
затем: используйте следующий ()
$list = array("A", "B", "C","D"); foreach ($list as $var) { print(current($list)); next($list); }
ПРИМЕЧАНИЕ: первый элемент не будет напечатан, потому что foreach переместил указатель на второй элемент массива 🙂
В этом примере объясняется полное поведение:
$list = array("A", "B", "C","D"); foreach ($list as $var) { if(!isset($a)) reset($list); $a = 'isset'; print(current($list)); next($list); }
это ABCD
Также обратите внимание, что:
As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior.
для каждого
EDIT: Я хочу поделиться и моим новым запутанным находкой !!!
Example1:
$list = array("A", "B", "C","D"); $list_copy = $list; foreach ($list as $key => $val) { current($list_copy); echo current($list); //next($list); }
ВЫХОД: AAAA
Example2:
$list = array("A", "B", "C","D"); $list_copy = $list; foreach ($list as $key => $val) { current($list_copy); echo current($list); next($list); }
ВЫХОД: ABCD
При вызове функции current () внутри foreach даже для другого массива это повлияет на поведение foreach …
Example3:
$list = array("A", "B", "C","D"); $refcopy = &$list; foreach ($list as $key => $val) { if(!isset($a)) { $a = 'isset'; reset($list); } echo current($list); next($list); }
OUTPUT: ACD (WOW! B отсутствует)
Пример: 4
$list = array("A", "B", "C","D"); $refcopy = &$list; foreach ($list as $key => $val) { echo current($list); next($list); }
ВЫХОД: BCD
Невозможно точно решить, что произойдет внутри цикла foreach !!!
Ну, я не знаю, почему это так, но я подозреваю, что это может иметь какое-то отношение к тому, как оценивается / обрабатывается присвоение. Для удовольствия я попробовал это, и это привело к еще одному incorrect
поведению:
$arr = array('A', 'B', 'C', 'D'); function itm($val) { return current($val); } foreach ($arr as $item) { print itm($arr); }
Результат: BCDA
Поэтому я предполагаю, что здесь происходит то, что вызов функции заставляет вычислять токи в правильном порядке. Также причиной для меня получения BCDA вместо ABCD является, вероятно, потому, что внутренний указатель сначала увеличивается (указывая на B), а затем в en возвращается к пункту A.
Возможно, стоит отметить эту строку в документе PHP :
Обратите внимание, что присваивание копирует исходную переменную в новую (присваивание по значению), поэтому изменения в один не влияют на другую. Это может также иметь значение, если вам нужно скопировать что-то вроде большого массива внутри жесткого цикла.
Наверное, это не значит, что это ответ, но мне понравился ваш вопрос и я хотел внести небольшой вклад.
Даже если копия массива была сделана с помощью foreach, я должен получать AAAA, но не получать это в текущей стабильной версии PHP
Поскольку я не нашел ответа на этот вопрос здесь, я попробую объяснить.
Перед первой итерацией $list
foreach
$list
фактически не копируется. Только подсчет ссылок в $list
будет увеличен до 2. Итак, на первой итерации: первое значение $list
будет скопировано в $var
, указатель переместится ко второму элементу $list
и будет произведена фактическая копия $list
. Поэтому, когда вы вызываете current
указательные точки ко второму элементу, но на второй и дальнейших итерациях он никогда не изменяется, потому что фактическая копия $list
существует, так что current
всегда будет выводить второй элемент.
Редактировать:
Я играл с debug_zval_dump
чтобы понять это очень неожиданное поведение:
<pre> <?php $list = array("A", "B", "C","D"); echo '<h1>Ref count before entering foreach:</h1><br>'; debug_zval_dump($list); echo '<br><br>'; $i = 0; echo '<h1>Ref count in foreach:</h1><br>'; foreach ($list as $var) { $i++; echo '<b>Iteration #'.$i.':</b> '; debug_zval_dump($list); echo '<br>'; } $list = array("A", "B", "C","D"); //re-assign array to avoid confusion echo '<h1>Ref count before entering foreach that calls method "item" and passes array by value:</h1><br>'; debug_zval_dump($list); $i = 0; echo '<h1>Ref count in foreach that calls method "item" and passes array by value:</h1><br>'; foreach ( $list as $var ) { $i++; item($list, $i); } function item($list, $i) { echo '<b>Iteration #'.$i.':</b> '; debug_zval_dump($list); } $list = array("A", "B", "C","D"); //re-assign array to avoid confusion echo '<h1>Ref count before entering foreach that calls method "item" and passes array by reference:</h1><br>'; debug_zval_dump($list); $i = 0; echo '<h1>Ref count in foreach that calls method "item" and passes array by reference:</h1><br>'; foreach ( $list as $var ) { $i++; itemWithRef($list, $i); } function itemWithRef(&$list, $i) { echo '<b>Iteration #'.$i.':</b> '; debug_zval_dump($list); }
И получил следующий результат:
Счет перед вводом foreach:
array (4) refcount (2) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (1) }
Количество ссылок в foreach:
Итерация # 1: массив (4) refcount (3) { [0] => string (1) «A» refcount (2) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (1) }
Итерация # 2: массив (4) refcount (3) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (2) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (1) }
Итерация № 3: массив (4) refcount (3) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (2) [3] => строка (1) «D» refcount (1) }
Итерация # 4: массив (4) refcount (3) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (2) }
Ref count перед вводом foreach, который вызывает метод «item» и передает массив по значению:
array (4) refcount (2) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (1) } Счетчик ссылок в foreach, который вызывает метод «item» и передает массив по значению:
Итерация № 1: массив (4) refcount (5) { [0] => string (1) «A» refcount (2) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (1) } Итерация # 2: массив (4) refcount (5) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (2) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (1) } Итерация № 3: массив (4) refcount (5) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (2) [3] => строка (1) «D» refcount (1) } Итерация # 4: массив (4) refcount (5) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (2) } Счетчик ссылок перед вводом foreach, который вызывает метод «item» и передает массив по ссылке:
array (4) refcount (2) { [0] => string (1) «A» refcount (1) [1] => строка (1) «B» refcount (1) [2] => строка (1) «C» refcount (1) [3] => строка (1) «D» refcount (1) } Счетчик ссылок в foreach, который вызывает метод «item» и передает массив по ссылке:
Итерация # 1: массив (4) refcount (1) { [0] => string (1) «A» refcount (4) [1] => строка (1) «B» refcount (3) [2] => строка (1) «C» refcount (3) [3] => строка (1) «D» refcount (3) } Итерация # 2: массив (4) refcount (1) { [0] => string (1) «A» refcount (3) [1] => строка (1) «B» refcount (4) [2] => строка (1) «C» refcount (3) [3] => строка (1) «D» refcount (3) } Итерация № 3: массив (4) refcount (1) { [0] => string (1) «A» refcount (3) [1] => строка (1) «B» refcount (3) [2] => строка (1) «C» refcount (4) [3] => строка (1) «D» refcount (3) } Итерация № 4: массив (4) refcount (1) { [0] => string (1) «A» refcount (3) [1] => строка (1) «B» refcount (3) [2] => строка (1) «C» refcount (3) [3] => строка (1) «D» refcount (4) }
Результат немного запутан.
В первом примере foreach
создал внутреннюю копию $list
поэтому счетчик ссылок был 2 (4 в результате, потому что debug_zval_dump
добавляет один refCount
). Во втором примере (pass by value) refCount
увеличилось до 3, поскольку $list
был скопирован для функции. В третьем примере count хранится в 1, потому что $list
был передан по значению. Мне нужно время, чтобы понять, почему. Если вы получите точку из этой итоговой доли.
Все, что я могу сказать, это то, что когда мы передавали массив по значению foreach
проходил массив, который выполнял итерацию, но когда он передавался по ссылке, он принимал исходный $list
. Вопрос в том, почему foreach
передавал этот массив?
Код, который вы используете, если ложь. Даже в буквальном смысле это может выглядеть как один и тот же код, однако переменные не являются ( http://3v4l.org/jainJ ).
Чтобы ответить на ваш реальный вопрос, для согласованных результатов используйте правильные инструменты.
Если вам нужна переменная со значением массива, назначьте ее:
$list = array(....);
Если вам нужно получить текущее значение этого массива, используйте его перед foreach
:
$current = current($list);
Поскольку внутри foreach
это может быть одно и то же имя переменной, но значение будет другим (представьте, вы повторяете!).
Если вам нужно текущее значение на каждую итерацию, используйте его:
foreach ($list as $current) { ... }
См. $current
?
О, черт возьми, да, это было так просто. Подождите, у меня уже есть последовательные результаты. О, и было так легко не обмануть себя. Ура! 😉
Для журнала: Передача переменной в качестве параметра функции делает ее новой переменной. Даже когда ссылка (что объясняется).
Если вы сомневаетесь, не используйте ссылки PHP. Или даже переменные: http://3v4l.org/6p5nZ
Замечательно. Но, похоже, проблема с памятью связана с другой версией php. Также ток дает только текущую позицию, которую вы не увеличиваете (перемещаетесь) в любом месте, поэтому не получаете надлежащего вывода. Поскольку другая версия php интерпретирует следующую и начальную точку массива по-разному, решение этого может быть сбросом внутри цикла с некоторым условием. (кстати, цикл, а затем с использованием текущего, следующий prev – не лучший способ, поскольку уже есть объект в var :), что бы вы ни выбрали) Это один из способов заставить его работать:
<?php $list = array("A", "B", "C","D"); $flag =0; foreach ($list as $var) { if($flag==0) { reset($list); $flag=1; } print(current($list)); next($list); }
Выход – ABCD. См. http://3v4l.org/5Hm5Y
$list = array("A", "B", "C","D"); foreach ($list as $var) { echo $var; }
Должен это делать.
Использование Это вы уже знаете, что происходит!
$list = array('A', 'B', 'C','D'); foreach ($list as $var) { var_dump(current($list)); }
может быть, это поможет вам!