164 votes

PHP Foreach Pass par référence: duplication du dernier élément? (Punaise?)

J'ai juste eu un comportement très étrange avec un simple script PHP que j'écrivais. Je l'ai réduit au minimum nécessaire pour recréer le bug:

 <?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?

?>
 

Cela produit:

 Array
(
    [0] => foo
    [1] => bar
    [2] => baz
)
Array
(
    [0] => foo
    [1] => bar
    [2] => bar
)
 

Est-ce un bug ou un comportement vraiment étrange qui est censé se produire?

174voto

animuson Points 23184

Après la première boucle foreach, $item est encore une référence à une certaine valeur qui est également utilisée par $arr[2]. De sorte que chaque foreach appel dans la deuxième boucle, qui n'a pas d'appel par référence, remplace cette valeur, et donc de $arr[2], avec la nouvelle valeur.

Donc la boucle 1, la valeur et l' $arr[2] deviennent $arr[0], ce qui est "foo".
La boucle 2, la valeur et l' $arr[2] deviennent $arr[1], ce qui est "bar".
La boucle 3, la valeur et l' $arr[2] deviennent $arr[2], ce qui est " bar " (à cause de la boucle 2).

La valeur 'baz' est, de fait, perdu à la suite du premier appel, la deuxième boucle foreach.

La Sortie de débogage

Pour chaque itération de la boucle, nous nous ferons l'écho de la valeur de $item ainsi que de façon récursive imprimer le tableau d' $arr.

Lors de la première boucle est exécutée, nous voyons cette sortie:

foo
Array ( [0] => foo [1] => bar [2] => baz )

bar
Array ( [0] => foo [1] => bar [2] => baz )

baz
Array ( [0] => foo [1] => bar [2] => baz )

À la fin de la boucle, $item pointe toujours à la même place qu' $arr[2].

Lors de la deuxième boucle est exécutée, nous voyons cette sortie:

foo
Array ( [0] => foo [1] => bar [2] => foo )

bar
Array ( [0] => foo [1] => bar [2] => bar )

bar
Array ( [0] => foo [1] => bar [2] => bar )

Vous verrez comment chaque moment de la matrice de mettre une nouvelle valeur en $item, elle a également mis à jour $arr[3] avec la même valeur, car ils sont les deux qui pointe toujours vers le même endroit. Lorsque la boucle est à la troisième valeur de la pile, il contiendra la valeur bar parce que c'était juste fixé par la précédente itération de la boucle.

Est-ce un bug?

Pas de. C'est le comportement d'un élément référencé, et non pas d'un bug. Il serait similaire à l'exécution de quelque chose comme:

for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }

Une boucle foreach n'est pas spécial à la nature dans laquelle il peut ignorer les éléments référencés. C'est tout simplement le réglage de cette variable à la nouvelle valeur à chaque fois, comme vous le feriez à l'extérieur d'une boucle.

32voto

Michael Leaney Points 436

$item est une référence à $arr[2] et est écrasé par la seconde boucle foreach, comme l'a souligné animuson.

 foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

unset($item); // This will fix the issue.

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?
 

3voto

jocull Points 3718

Même si cela peut ne pas être officiellement un bug, c'est à mon avis. Je pense que le problème ici est que nous avons l'attente, $item aller hors de portée lorsque la boucle est sorti comme il le ferait dans beaucoup d'autres langages de programmation. Cependant, cela ne semble pas être le cas...

Ce code...

$arr = array('one', 'two', 'three');
foreach($arr as $item){
    echo "$item\n";
}    
echo $item;

Donne à la sortie...

one
two
three
three

Comme les autres ont déjà dit, vous êtes l'écrasement de la variable en question en $arr[2] avec votre deuxième boucle, mais c'est seulement parce qu' $item ne sont jamais allés hors de portée. Ce gars, vous ne pensez... bug?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X