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 bogue:
<?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?
foreach($x AS &$y){ ... unset($y); }
- il est en fait sur php.net (je ne sais pas où) parce que c'est une erreur bien commise.Réponses:
Après la première boucle foreach, il
$item
y a toujours une référence à une valeur qui est également utilisée par$arr[2]
. Ainsi, chaque appel foreach dans la deuxième boucle, qui n'appelle pas par référence, remplace cette valeur, et donc$arr[2]
, par la nouvelle valeur.Donc boucle 1, la valeur et
$arr[2]
devenir$arr[0]
, qui est «toto».Boucle 2, la valeur et
$arr[2]
devenir$arr[1]
, qui est «bar».Boucle 3, la valeur et
$arr[2]
devenir$arr[2]
, qui est «bar» (à cause de la boucle 2).La valeur «baz» est en fait perdue au premier appel de la deuxième boucle foreach.
Débogage de la sortie
Pour chaque itération de la boucle, nous allons faire écho à la valeur de
$item
et imprimer récursivement le tableau$arr
.Lorsque la première boucle est exécutée, nous voyons cette sortie:
À la fin de la boucle,
$item
pointe toujours vers le même endroit que$arr[2]
.Lorsque la deuxième boucle est exécutée, nous voyons cette sortie:
Vous remarquerez que chaque fois que le tableau insère une nouvelle valeur
$item
, il est également mis$arr[3]
à jour avec la même valeur, car ils pointent toujours vers le même emplacement. Lorsque la boucle atteint la troisième valeur du tableau, elle contiendra la valeurbar
car elle vient d'être définie par l'itération précédente de cette boucle.Est-ce un bug?
Non. Il s'agit du comportement d'un élément référencé et non d'un bogue. Ce serait similaire à exécuter quelque chose comme:
Une boucle foreach n'est pas de nature spéciale dans laquelle elle peut ignorer les éléments référencés. Il s'agit simplement de définir cette variable sur la nouvelle valeur à chaque fois, comme vous le feriez en dehors d'une boucle.
la source
$item
n'est pas une référence à$arr[2]
, la valeur contenue par$arr[2]
est une référence à la valeur référencée par$item
. Pour illustrer la différence, vous pourriez également ne pas être défini$arr[2]
, et$item
ne serait pas affecté, et écrire à$item
ne l'affecterait pas.$item
sort-il pas de la portée lorsque la boucle foreach est sortie? Cela semble être un problème de fermeture?$item
est une référence à$arr[2]
et est écrasée par la deuxième boucle foreach comme l'a souligné animuson.la source
Bien que ce ne soit pas officiellement un bogue, à mon avis, c'est le cas. Je pense que le problème ici est que nous nous attendons à ce que nous devenions
$item
hors de portée lorsque la boucle est sortie, comme dans de nombreux autres langages de programmation. Cependant, cela ne semble pas être le cas ...Ce code ...
Donne la sortie ...
Comme d'autres l'ont déjà dit, vous écrasez la variable référencée
$arr[2]
avec votre deuxième boucle, mais cela ne se produit que parce qu'il$item
n'est jamais sorti hors de portée. Que pensez-vous les gars ... bug?la source
{}
blocs en général. Voici comment fonctionne la langueUne explication plus simple, semble de Rasmus Lerdorf, créateur original de PHP: https://bugs.php.net/bug.php?id=71454
la source
Le comportement correct de PHP pourrait être une erreur de NOTICE à mon avis. Si une variable référencée créée dans une boucle foreach est utilisée en dehors de la boucle, cela devrait provoquer une notification. Très facile de tomber dans ce comportement, très difficile de le repérer quand il s'est produit. Et aucun développeur ne lira la page de documentation foreach, ce n'est pas une aide.
Vous devriez
unset()
la référence après votre boucle pour éviter ce genre de problème. unset () sur une référence supprimera simplement la référence sans nuire aux données d'origine.la source
c'est parce que vous utilisez par la directive ref (&). la dernière valeur sera remplacée par la deuxième boucle et cela corrompra votre tableau. la solution la plus simple consiste à utiliser un nom différent pour la deuxième boucle:
la source