Это перевод статьи которую в написал Johannes Schlüter в своем блоге. Оригинал можно найти перейдя по ссылке References and foreach.
Я использовал изображения из его поста, добавив подписью, перевод надписей. На самом деле это вообще мой первый перевод, который я публикую, так что надеюсь что получилось не слишком плохо.
Ссылки в PHP ужасны, как я уже говорил и вы безусловны должны избегать их использование.
Так вот, есть один пример, который приводит на первый взгляд к неожиданному поведению, которое я еще не видел,
до того как я наткнулся на него в первый раз, но затем было несколько бег-репортов об этом инцеденте, а недавно знакомый спросил меня о нем, итак вот он:
Что выведет этот код?
$a = array('a', 'b', 'c', 'd');
foreach ($a as &$v) { }
foreach ($a as $v) { }
print_r($a);
Мы делаем 2 раза итерацию по массиву, ничего не делая с ним. Так что результат не должен измениться. Правильно? - Не Правильно! На самом деле результат будет таким:
Array
(
[0] => a
[1] => b
[2] => c
[3] => c
)
Для того чтобы понять, почему так произошло, сделаем шаг назад и посмотрим как реализованы переменные и ссылки в PHP:
Переменные в PHP в основном состоят из двух вещей. Метка (“label”) и контейнер (“container”). Метка это запись в хэш таблице (есть несколько оптимизацонных моментов в движке, поэтому не всегда в хэш-таблице, ну да ладно) которые могут представлять собой таблицу символов функции, массива или таблицу свойств объекта. Итак, у нас есть имя и указатель на контейнер. Внутреннее название контейнера “zval”, он хранит значения и некую мета информацию, этот контейнер также может быть новой хэш-таблицей с набором меток, указывающих на другие контейнеры, если мы сейчас сделаем ссылку на него, то это приведет ко второй метке указывающей на этот контейнер с другой меткой. Обе метки с этих пор имеют те же полномочия что и контейнер.
Теперь давайте посмотри на ситуацию более детально.
Графически это будет выглядеть так:
Графически это будет выглядеть так:
| Глобальная таблица символов (ГТС) |
Итак у нас есть 6 контейнеров (глобальная таблица символов вверху, контейнер содержащий массив называющийся $a слева и по одному контейнеру на каждый элемент - справа) Теперь мы начнем первую итерацию. ГТС получает новую запись $v и это делает ссылку на контейнер первого элемента массива.
| Новая глобальная перменная v содержащая контейнер с ‘a’ |
$a[0] и $v находятся в одном контейнере, следовательно при их изменении одной переменной, на другой будет тот же эффект. Когда итерация продолжится, ссылка оказывается сломанной и $v делает ссылку на другой элемент. Так после окончания итераций $v ссылается на последний элемент.
| После итерации $v указывает на ‘d’ |
Запомните: $v ссылка, а значит любые изменения $v, оказывают тот же эффект и на переменные на которые она ссылается, в нашем случае это я$a[3]. До сих пор не произошло ничего особенного , но сейчас рассмотрим второю итерацию. Она присваивает значение текущего элемента переменной $v для каждого шага. Сейчас $v ссылается на элемент $a[3], так что после присваивания значения к $v, $a[3] тоже изменится.
| Для первого шага второй итерации значение $a[0] присваивается переменной $v |
Это продолжается и для следующих шагов:
И теперь мы легко можем догадаться что будет на последнем шаге: $v присваивается значение последнего элемента $a[3] и так как $a[3] является ссылкой на $v, то следовательно ничего не происходит.
И это результат который мы видели выше.
Таким образом чтобы сделать короткий вывод из этой истории с картинками: Будьте осторожны с ссылками! Они могут иметь очень странные эффекты.
0 коммент.:
Отправить комментарий