Назначить по ссылке ошибка
Я столкнулся с этим, казалось бы, очень простым вопросом на днях Как изменить значение в $array2 без ссылки на $array1? Однако чем больше я смотрел на это, тем более странным казалось, что это действительно работает так, как задумано. После этого я начал изучать коды операций, которые генерируются на основе следующих результатов.
$array1 = array(2, 10);
$x = &$array1[1];
$array2 = $array1;
$array2[1] = 22;
echo $array1[1]; // Outputs 22
Это кажется мне безумным, поскольку array2 должна быть только копией array1, и все, что происходит с одним массивом, не должно влиять на содержимое другого. Конечно, если вы закомментируете вторую строку, последняя строка отобразит 10, как и ожидалось.
Заглядывая дальше, я мог бы найти классный сайт, который показывает мне коды операций, которые PHP создает с помощью Vulcan Logic Dumper. Вот коды операций, сгенерированные вышеуказанным кодом.
Finding entry points
Branch analysis from position: 0
Return found
filename: /in/qO86H
function name: (null)
number of ops: 11
compiled vars: !0 = $array1, !1 = $x, !2 = $array2
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > INIT_ARRAY ~0 2
1 ADD_ARRAY_ELEMENT ~0 10
2 ASSIGN !0, ~0
4 3 FETCH_DIM_W $2 !0, 1
4 ASSIGN_REF !1, $2
5 5 ASSIGN !2, !0
6 6 ASSIGN_DIM !2, 1
7 OP_DATA 22, $6
8 8 FETCH_DIM_R $7 !0, 1
9 ECHO $7
10 > RETURN 1
Эти коды операций не очень хорошо документированы здесь http://php.net/manual/en/internals2.opcodes.php но я считаю, что на английском языке коды операций делают следующее. По линии... может быть больше для меня, чем кто-либо еще.
- Строка 3: мы инициализируем массив его первым значением, а затем добавляем к нему 10, прежде чем присваивать его массиву $array1.
- Строка 4: Получить только для записи? значение из массива и назначить его по ссылке на $x.
- Строка 5: установите $ array1 в $array2.
- Строка 6: получить индекс массива 1. od_data Я предполагаю, что устанавливает его на 22, хотя $6 никогда не возвращается. OD_DATA не имеет абсолютно никакой документации и не указан в качестве кода операции, где бы я ни смотрел.
- Строка 8: извлечение значения только для чтения из индекса 1 массива $ array1 и его вывод.
Даже работая через коды операций, я не уверен, где это происходит не так. У меня есть ощущение отсутствия документации по кодам операций, и моя неопытность в работе с ними, вероятно, не дает мне понять, в чем дело.
РЕДАКТИРОВАТЬ 1:
Как указал Майк в первом комментарии, для массивов ссылочный статус сохраняется при их копировании. Здесь можно увидеть документацию, а также место в статье о массиве, на которое она ссылается по http://php.net/manual/en/language.types.array.php. Это достаточно забавно, не считается предупреждением. Что удивительно для меня, если это правда, ссылочный статус не сохраняется для этого кода, как вы ожидаете.
$array1 = array(2, 10);
$x = &$array1;
$array2 = $array1;
$array2[1] = 22;
echo $array1[1]; // Output is 10
Таким образом, кажется, что это происходит только тогда, когда вы пытаетесь назначить отдельные элементы по ссылке, что делает эту функцию еще более запутанной.
Почему php сохраняет статус индексов массивов только при индивидуальном назначении?
РЕДАКТИРОВАТЬ 2:
Сегодня я провел некоторое тестирование с использованием HHVM, и HHVM обрабатывает первый фрагмент кода так, как вы думаете. Я люблю PHP, но HHVM выглядит лучше и лучше, чем Zend Engine.
1 ответ
Это объясняется в руководстве по PHP (даже если вам нужно потратить больше времени, чем нужно, чтобы найти его), в частности, на http://php.net/manual/en/language.types.array.php
"Общие" данные остаются общими, а первоначальное назначение просто действует как псевдоним. Пока вы не начнете манипулировать массивами с помощью независимых операций, таких как ...[] = ...
что интерпретатор начинает обрабатывать их как расходящиеся списки, и даже в этом случае общие данные остаются общими, поэтому вы можете иметь два массива с общими первыми n элементами, но расходящимися последующими данными.
Для истинного "копирования по значению" для одного массива в другой вы в конечном итоге делаете что-то вроде
$arr2 = array();
foreach($arr1 as $val) {
$arr2[] = $val;
}
или же
$arr2 = array();
for($i=count($arr1)-1; $i>-1; $i--) {
$arr2[$i] = $arr[$i];
}
(использование обратной петли в основном потому, что недостаточно людей помнят, что вы можете это сделать, и более эффективно, чем прямая петля =)
Вы представляете, что там будет array_copy
функция или что-то, чтобы помочь справиться с причудой копирования массива, но ее просто не существует. Это странно, но одна из тех вещей, "состояние PHP". Выбор был сделан в прошлом, в результате PHP прожил этот выбор в течение нескольких лет, так что это просто "одна из тех вещей". К несчастью!