PHP 5.5.12. Учти это:
<?php $a = [ 'a', 'b', 'c' ]; foreach($a as &$x) { $x .= 'q'; } print_r($a);
Это, как и ожидалось, выводит:
Array ( [0] => aq [1] => bq [2] => cq )
Теперь рассмотрим:
<?php $a = [ 'a', 'b', 'c' ]; foreach(z($a) as &$x) { $x .= 'q'; } print_r($a); function z($a) { return $a; }
Эти результаты:
Array ( [0] => aq [1] => bq [2] => cq )
(!) Но подождите минуту. $ a не передается по ссылке. Это означает, что я должен получить копию обратно из z (), которая будет изменена, а $ a должна быть оставлена одна.
Но что происходит, когда мы заставляем PHP делать свою магию копирования на запись:
$a = [ 'a', 'b', 'c' ]; foreach(z($a) as &$x) { $x .= 'q'; } print_r($a); function z($a) { $a[0] .= 'x'; return $a; }
Для этого мы получаем то, что ожидаю:
Array ( [0] => a [1] => b [2] => c )
EDIT: Еще один пример …
$a = [ 'a', 'b', 'c' ]; $b = z($a); foreach($b as &$x) { $x .= 'q'; } print_r($a); function z($a) { return $a; }
Это работает так, как ожидалось:
Array ( [0] => a [1] => b [2] => c )
Есть ли рациональное объяснение этому?
Для решения этой проблемы был открыт ошибка 67633 . Это поведение было изменено этим фиксатором, чтобы устранить ссылочные ограничения из foreach.
Из этого вывода 3v4l вы можете ясно видеть, что такое поведение со временем изменилось:
Исправлено с этим фиксацией ; это станет доступно в 5.5.18 и 5.6.2.
До PHP 5.5 ваш код действительно вызвал бы фатальную ошибку:
Fatal error: Cannot create references to elements of a temporary array expression
Эти версии не выполняют copy-on-write, когда результат функции используется непосредственно внутри блока foreach
. Таким образом, исходный массив теперь используется, и изменения в элементах являются постоянными.
Я лично считаю, что это ошибка ; должна быть сделана копия-на-запись.
В ветке phpng , которая, вероятно, станет основой следующей крупной версии, постоянные массивы становятся неизменными, поэтому копирование на запись выполняется правильно только в этом случае. Объявление массива, как показано ниже, будет иметь такую же проблему с phpng:
$foo = 'b'; $a = ['a', $foo, 'b'];
доказательство
Только Hack правильно обрабатывает ситуацию, как она сейчас стоит.
Документированный способ использования результата функции по ссылке заключается в следующем:
$a = [ 'a', 'b', 'c' ]; foreach(z($a) as &$x) { $x .= 'q'; } print_r($a); // indicate that this function returns by reference // and its argument must be a reference too function &z(&$a) { return $a; }
демонстрация
Чтобы избежать изменения исходного массива, на данный момент у вас есть следующие параметры:
foreach
; В этом примере функция z ничего не делает. Он не копирует и не клонирует что-либо, поэтому ответ от z () будет таким же, как и не называть вообще. Вы просто возвращаете переданный объект и, следовательно, ответ такой, как ожидалось.
<?php $a = [ 'a', 'b', 'c' ]; foreach(z($a) as &$x) { $x .= 'q'; } print_r($a); function z($a) { return $a; }
Thiis легче продемонстрировать с помощью объектов, поскольку им присваивается идентификатор системы:
<?php $obj = new stdClass(); $obj->name = 'foo'; function z($a) { $a->name = 'bar'; return $a; } var_dump($obj); var_dump(z($obj));
Выход для этого:
object(stdClass)#1 (1) { ["name"]=> string(3) "foo" } object(stdClass)#1 (1) { ["name"]=> string(3) "bar" }
Оба объекта имеют идентификатор «1», который показывает, что они не являются копиями или клонами.
В вашем коде
<?php $a = [ 'a', 'b', 'c' ]; foreach(z($a) as &$x) { $x .= 'q'; } print_r($a); function z($a) { return $a; }
Поистине странно мне сослаться на временное выражение. это &$x
to z($a)
потому что мой PHP-сервер этого не допускает.
Но если ваш PHP-сервер действительно может это сделать, мое предложение, пожалуйста, избегайте использования имени параметра, такого же, как и любая глобальная переменная. В вашем случае $a
является глобальной переменной области <?php
script. Мое объяснение состоит в том, что речь идет о смешанном глобальном имени переменной с именем параметра. Поэтому, когда вы вызываете return $a;
в вашей функции он даст глобальную переменную вместо значения параметра, которое вы ввели. Это значение все еще дает прямую ссылку переменной $a
в функции foreach()
.
Обычно у меня есть имя префикса для любого имени параметров для моих функций, чтобы избежать смешивания с глобальными или локальными переменными. Обычно я использую $p_
в качестве префикса, поэтому попробуйте использовать function z($p_a)
вместо function z($a)
и увидеть результат.