php - Asignar por error de referencia
opcodes (1)
Encontré esta pregunta aparentemente muy simple el otro día ¿Cómo cambiar el valor en $ array2 sin referirme a $ array1? Sin embargo, cuanto más lo miraba, más extraño me parecía que esto funcionara como estaba previsto. Después de esto comencé a buscar en los códigos de operación que se generan a partir de la salida de lo siguiente.
$array1 = array(2, 10);
$x = &$array1[1];
$array2 = $array1;
$array2[1] = 22;
echo $array1[1]; // Outputs 22
Esto me parece una locura, ya que array2 solo debería ser una copia de array1 y todo lo que le suceda a una matriz no debería afectar el contenido de la otra. Por supuesto, si comenta la segunda línea, la línea final se hará eco de 10 como se esperaba.
Mirando más lejos, podría encontrar un sitio genial que me muestre los códigos de operación que PHP produce utilizando el Dump Vulcan Logic. Aquí están los códigos de operación generados por el código anterior.
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
Estos opcodes no están bien documentados aquí http://php.net/manual/en/internals2.opcodes.php pero creo que en inglés los opcodes están haciendo lo siguiente. Por línea ... podría ser más para mí que cualquier otra persona.
- Línea 3: Inicializamos la matriz con su primer valor y luego le agregamos 10 antes de asignarla a $ array1.
- Línea 4: ¿Obtener solo escritura? valor de la matriz y asignarlo por referencia a $ x.
- Línea 5: Establecer $ array1 a $ array2.
- Línea 6: Obtener índice de matriz de 1. od_data Supongo que lo establece en 22, aunque $ 6 nunca se devuelven. OD_DATA no tiene absolutamente ninguna documentación y no aparece como un código de operación en ningún lugar que haya visto.
- Línea 8: Obtenga un valor de solo lectura del índice 1 de $ array1 y haga eco.
Incluso trabajando a través de los códigos de operación no estoy seguro de que esto vaya mal. Tengo la sensación de que la falta de documentación sobre los códigos de operación y mi inexperiencia al trabajar con ellos probablemente me impidan descubrir en qué me está yendo mal.
EDITAR 1:
Como señaló Mike en el primer comentario, el estado de referencia se conserva cuando se copian. Aquí se puede ver la documentación junto con un lugar en el artículo de matriz que vincula a http://php.net/manual/en/language.types.array.php#104064 . Esto bastante gracioso no se considera una advertencia. Lo que me sorprende si esto es cierto, el estado de referencia no se conserva para este código como cabría esperar.
$array1 = array(2, 10);
$x = &$array1;
$array2 = $array1;
$array2[1] = 22;
echo $array1[1]; // Output is 10
Así que parece que esto solo sucede cuando intenta asignar elementos individuales por referencia, lo que hace que esta funcionalidad sea aún más confusa.
¿Por qué php solo conserva el estado de los índices de las matrices cuando se asignan individualmente?
EDIT 2:
Hice algunas pruebas con HHVM hoy y HHVM maneja el primer snip-it del código de cómo cree que lo haría. Me encanta PHP pero HHVM se ve mejor y mejor que Zend Engine.
Esto se explica en el manual de PHP (incluso si tiene que pasar más tiempo del que debería para poder encontrarlo), específicamente en http://php.net/manual/en/language.types.array.php#104064
Los datos "compartidos" permanecen compartidos, y la asignación inicial solo actúa como un alias. No es hasta que empiezas a manipular los arreglos con operaciones independientes como ...[] = ...
que el intérprete comienza a tratarlos como listas divergentes, e incluso entonces los datos compartidos se comparten para que puedas tener dos arreglos con un primero compartido. Elementos n , pero datos posteriores divergentes.
Para una verdadera "copia por valor" para una matriz a otra, prácticamente terminas haciendo algo como
$arr2 = array();
foreach($arr1 as $val) {
$arr2[] = $val;
}
o
$arr2 = array();
for($i=count($arr1)-1; $i>-1; $i--) {
$arr2[$i] = $arr[$i];
}
(usar el bucle inverso principalmente porque no hay suficientes personas que recuerden que eso es algo que puede hacer, y es más eficiente que un bucle hacia adelante =)
Se podría imaginar que habría una función array_copy
o algo para ayudar a lidiar con la peculiaridad de la copia array, pero no parece haber una. Es extraño, pero una de esas cosas "el estado de PHP". En el pasado se hizo una elección, PHP vivió con esa elección durante varios años como resultado, por lo que es solo "una de esas cosas". ¡Desafortunadamente!