Acabo de tener un comportamiento muy extraño con un simple script php que estaba escribiendo. Lo reduje al mínimo necesario para recrear el error:
<?php
$arr = array("foo",
"bar",
"baz");
foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);
foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?
?>
Esto produce:
Array
(
[0] => foo
[1] => bar
[2] => baz
)
Array
(
[0] => foo
[1] => bar
[2] => bar
)
¿Es esto un error o un comportamiento realmente extraño que se supone que debe suceder?
foreach($x AS &$y){ ... unset($y); }
en realidad está en php.net (no sé dónde) porque es un error muy cometido.Respuestas:
Después del primer bucle foreach,
$item
sigue siendo una referencia a algún valor que también está siendo utilizado por$arr[2]
. Por lo tanto, cada llamada foreach en el segundo bucle, que no llama por referencia, reemplaza ese valor y$arr[2]
, por lo tanto , con el nuevo valor.Entonces, el bucle 1, el valor y el
$arr[2]
devenir$arr[0]
, que es 'foo'.Loop 2, el valor y
$arr[2]
llegar a ser$arr[1]
, que es 'bar'.Bucle 3, el valor y
$arr[2]
llegar a ser$arr[2]
, que es 'bar' (debido al bucle 2).El valor 'baz' en realidad se pierde en la primera llamada del segundo bucle foreach.
Depuración de la salida
Para cada iteración del bucle, haremos eco del valor
$item
e imprimiremos recursivamente la matriz$arr
.Cuando se ejecuta el primer ciclo, vemos esta salida:
Al final del bucle,
$item
sigue apuntando al mismo lugar que$arr[2]
.Cuando se ejecuta el segundo ciclo, vemos esta salida:
Notarás cómo cada matriz de tiempo pone un nuevo valor
$item
, también se actualiza$arr[3]
con ese mismo valor, ya que ambos todavía apuntan a la misma ubicación. Cuando el bucle llega al tercer valor de la matriz, contendrá el valorbar
porque solo fue establecido por la iteración anterior de ese bucle.¿Es un error?
No. Este es el comportamiento de un elemento referenciado, y no un error. Sería similar a ejecutar algo como:
Un bucle foreach no es de naturaleza especial en el que puede ignorar los elementos referenciados. Simplemente establece esa variable en el nuevo valor cada vez como lo haría fuera de un ciclo.
fuente
$item
no es una referencia a$arr[2]
, el valor contenido por$arr[2]
es una referencia al valor al que hace referencia$item
. Para ilustrar la diferencia, también podría desarmar$arr[2]
, y no$item
se vería afectado, y escribir para$item
no afectarlo.$item
no sale del alcance cuando se sale del bucle foreach? Esto parece un problema de cierre?$item
es una referencia$arr[2]
y está siendo sobrescrito por el segundo bucle foreach como lo señaló animuson.fuente
Si bien esto puede no ser oficialmente un error, en mi opinión lo es. Creo que el problema aquí es que tenemos la expectativa de
$item
salir del alcance cuando se cierra el ciclo como lo haría en muchos otros lenguajes de programación. Sin embargo, ese no parece ser el caso ...Este código ...
Da la salida ...
Como ya dijeron otras personas, está sobrescribiendo la variable referenciada
$arr[2]
con su segundo bucle, pero solo sucede porque$item
nunca salió del alcance. ¿Qué piensan ustedes ... error?fuente
{}
bloques en general. Así es como funciona el lenguajeUna explicación más fácil, parece de Rasmus Lerdorf, creador original de PHP: https://bugs.php.net/bug.php?id=71454
fuente
El comportamiento correcto de PHP podría ser un error de AVISO en mi opinión. Si una variable referenciada creada en un bucle foreach se usa fuera del bucle, debería generar un aviso. Muy fácil caer en este comportamiento, muy difícil detectarlo cuando sucedió. Y ningún desarrollador va a leer la página de documentación foreach, no es una ayuda.
Debe hacer
unset()
referencia después de su ciclo para evitar este tipo de problema. unset () en una referencia solo eliminará la referencia sin dañar los datos originales.fuente
eso es porque lo usas por la directiva ref (&). el último valor será reemplazado por el segundo bucle y corromperá su matriz. La solución más simple es usar un nombre diferente para el segundo bucle:
fuente