Cet article décrit le comportement de copie de foreach en PHP 5. Nécessite une certaine connaissance du fonctionnement interne de PHP, à savoir le comportement de zvals, de refcount et de copie sur écriture.
Le foreach de PHP est une construction de langage très soignée et précise. Pourtant, certaines personnes n’aiment pas l’utiliser parce qu’elles pensent que c’est lent. L'une des raisons de cette dénomination courante est que foreach copie le tableau qu'il itère.
Par conséquent, certaines personnes suggèrent d'écrire :
$keys = array_keys($array); $size = count($array); for ($i = 0; $i < $size; $i++) { $key = $keys[$i]; $value = $array[$key]; // ... }
au lieu d'être plus intuitif et direct :
foreach ($array as $key => $value) { // ... }
Il y a deux problèmes ici :
La microoptimisation est mauvaise. Souvent, cela vous fait perdre du temps et n’entraîne aucune amélioration mesurable des performances.
Le comportement de copie de foreach est un peu plus compliqué que la plupart des gens ne le pensent. Généralement, la version « optimisée » sera plus lente que la version originale.
Quand foreach copie-t-il ?
Le fait que foreach copie le tableau et le montant copié dépend de trois choses : si
fait référence au tableau itéré, qui Quelle est la valeur du refcount et si l'itération est effectuée par référence.
n'a pas de référence et refcount == 1
Dans le code ci-dessous, $array n'a pas de référence et refcount est 1. Dans ce cas, foreach ne copie pas le tableau (preuve) - contrairement à la croyance populaire selon laquelle foreach copie toujours le tableau itéré sans référence.
test(); function test() { $array = range(0, 100000); foreach ($array as $key => $value) { // ... } }
La raison est simple : pourquoi faites-vous cela ? Le seul endroit où foreach modifie $array est qu'il s'agit d'un pointeur de tableau interne. Il s’agit d’un comportement attendu, aucune prévention n’est donc nécessaire.
Non référencé, refcount > 1
Le code ci-dessous ressemble beaucoup au code précédent. La seule différence est que les tableaux sont désormais passés en paramètres. Cela peut sembler une différence insignifiante, mais cela change le comportement de foreach :
Il copiera désormais la structure du tableau, pas la valeur (preuve ; si vous vous demandez qu'il ne s'agit que de la structure copiée, comparez tel et tel script. Le premier copie uniquement la structure, le second copie les deux).
$array = range(0, 100000); test($array); function test($array) { foreach ($array as $key => $value) { // ... } }
Cela peut paraître un peu étrange à première vue :
Pourquoi lorsqu'un tableau est passé en paramètre, il copie, mais s'il est défini dans une fonction, il ne le fait pas La raison en est que le tableau zval est désormais partagé entre plusieurs variables : la variable $array en dehors de la fonction et la variable $array à l'intérieur de la fonction. Si foreach parcourt le tableau sans copier la structure du tableau, cela modifiera non seulement le pointeur de tableau de la variable $array dans la fonction, mais également le pointeur de la variable $array en dehors de la fonction. Foreach doit donc copier la structure du tableau (c'est-à-dire la table de hachage). D'un autre côté, les valeurs peuvent toujours partager des zvals, donc aucune copie n'est requise.
Citation
La situation suivante est très similaire à la précédente. La seule différence est que les tableaux sont passés par référence. Dans ce cas le tableau ne sera pas copié (preuve).
$array = range(0, 100000); test($array); function test(&$array) { foreach ($array as $key => $value) { // ... } }
Dans ce cas, le même raisonnement s'applique au cas précédent : le $array externe et le $array interne partagent des zvals. La différence est qu'ils sont désormais des références (isref == 1), donc dans ce cas, toute modification apportée au tableau interne sera apportée au tableau externe. Ainsi, si le pointeur du tableau interne change, le pointeur du tableau externe devrait également changer. C'est pourquoi foreach ne nécessite pas de copie.
Itérer par référence
Les exemples ci-dessus itèrent tous par valeur. Pour l'itération de référence, les mêmes règles s'appliquent, mais l'ajout d'une référence de valeur modifie le comportement de copie des valeurs du tableau (le comportement par rapport à la copie de structure reste le même).
La casse "non référencé, refcount == 1" n'a pas changé. L'itération de référence signifie que s'il y a un changement dans la valeur $, nous voulons muter le tableau d'origine afin que le tableau ne soit pas copié (preuve).
Le cas "référencé" reste également le même, auquel cas les modifications apportées à $value devraient modifier toutes les variables faisant référence au tableau itéré (preuve).
Seul le cas "non référencé, refcount > 1" a changé, car la structure du tableau et ses valeurs doivent désormais être copiées. structure du tableau, car sinon le pointeur de tableau de la variable $array en dehors de la fonction changerait, et les modifications apportées à $value modifieraient également la valeur externe de $array (preuve).
Résumé
Si et seulement si le tableau itéré n'est pas référencé et a refcount > 1, foreach copiera la structure du tableau
foreach le fera également Copie une valeur de tableau si et seulement si le point précédent s'applique et que l'itération est effectuée par référence
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!