Pbcopie de tableau après utilisation de références -COMPLEXE

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Pbcopie de tableau après utilisation de références -COMPLEXE

par titerm » 30 juin 2007, 15:01

Han ! j'ai qd meme trouvé un bug alors :)

Dommage que debug_zval_dump n'indique pas l'état du flag is_ref (y a qd meme le & qd is_ref = 1 dans les tableau mais pas sur une variable)
..

Après quelque test, le bout de code avec SPL en fait- ne fonctionne pas.
$pointer ne se déplace pas vériablement dans l'arbre... Bref, cette méthode n'est pas bonne.
Peut etre avec des iterator mais je n'y crois pas trop.

Je ne vois pas trop d'autre solution que l'utilisation de référence a mon pb.

Transformer un chaine de type one/two/three/four en tableau récursif
$tab['one']['two']['three']['four'] ... Sachant que c'est un tableau que je met a jours en plusieurs fois, et que la fois d'après ca peut etre one/two/six/seven ...

Actuellement, je n'ai trouvé que 2 facons de faire. Soit par les pointers, ce qui me semblait le plus logique et le plus propre. Soit avec un eval(), mais je beurk les evals...

Suite a des tests avec la 5.2.3, c'est bien le bug #33282 qui posait problème.
Etant donné que j'effectue le parcours du tableau via des références dans une methode, il n'y a pas a faire de unset apres le while, c'est une variable local qui disparait automatiquement a la fin de la methode et la référence avec :) .
merci a tous.

par Hubert Roksor » 30 juin 2007, 14:53

Je pense que tu es tombé sur un bug qui a été corrigé dans la 5.2.1, parce que je n'ai pas le même résultat quand j'exécute ton script. Ici, les références ne s'assumulent pas dans l'arbre:
(PHP 5.2.4-dev (cli) (built: Jun 27 2007 20:04:30))

Code : Tout sélectionner

inwhile key = three & $tree=>array(1) { ["one"]=> array(1) { ["two"]=> &array(1) { ["three"]=> string(8) "__LEAF__" } } } before unset =>string(8) "__LEAF__" array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> &string(8) "__LEAF__" } } }

par titerm » 30 juin 2007, 14:32

Parfois, c'est pas si facile de déréférencer.

Regarde le code suivant :
<?php
// un tableau de démo
$tree['one']['two']['three'] = '__LEAF__';
// On affiche le contenu
echo "le tree de base =>",var_dump($tree);

// crée un pointer sur le tableau
$pointer = &$tree;

// parcour le tableau jusqu'a la premiere feuille 
// Ca n'alter rien ca parcours, a la fin du while, echo $pointer retournerai __LEAF__
while(is_array($pointer)) {
	$key = key($pointer);
	echo "inwhile key = $key & \$tree=>", var_dump($tree);
    $pointer = &$pointer[$key];
}
echo "before unset =>", var_dump($pointer,$tree);
unset($pointer);
echo "after unset =>", var_dump($pointer,$tree);
// copy le tableau
$copy = $tree;

// Modifie la feuille
$tree['one']['two']['three'] = 'ALTER';
// Affiche le tableau d'origine et la copy
echo "tree => ",var_dump($tree);
echo "copy => ",var_dump($copy); 
et le résultat

Code : Tout sélectionner

le tree de base =>array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } inwhile key = one & $tree=>array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } inwhile key = two & $tree=>array(1) { ["one"]=> &array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } inwhile key = three & $tree=>array(1) { ["one"]=> &array(1) { ["two"]=> &array(1) { ["three"]=> string(8) "__LEAF__" } } } before unset =>string(8) "__LEAF__" array(1) { ["one"]=> array(1) { ["two"]=> &array(1) { ["three"]=> &string(8) "__LEAF__" } } } after unset =>NULL array(1) { ["one"]=> array(1) { ["two"]=> &array(1) { ["three"]=> string(8) "__LEAF__" } } } tree => array(1) { ["one"]=> array(1) { ["two"]=> &array(1) { ["three"]=> string(5) "ALTER" } } } copy => array(1) { ["one"]=> array(1) { ["two"]=> &array(1) { ["three"]=> string(5) "ALTER" } } }
On constate a la sortie du while que $tree contient 2 références... Je cherche encore pourquoi...
et après le unset, il n'en contient plus qu'une, mais c'est encore une de trop. Du coup, on ne peut toujour pas dupliquer $tree a la sortie.

Pour contourner ca, j'ai fais le code suivant. Je suis obliger de passer par une variable intermédiaire pour pouvoir faire un unset de $pointer avant de le mettre a jour... Super pratique...



<?php
// un tableau de démo
$tree['one']['two']['three'] = '__LEAF__';
// On affiche le contenu
echo "le tree de base =>",var_dump($tree);

// crée un pointer sur le tableau
$pointer = &$tree;

// parcour le tableau jusqu'a la premiere feuille 
// Ca n'alter rien ca parcours, a la fin du while, echo $pointer retournerai __LEAF__
while(is_array($pointer)) {
	$key = key($pointer);
	echo "inwhile key = $key & \$tree=>", var_dump($tree);
	// Utilise une var tampon
    $pointer2 = &$pointer[$key];
    // on supprime la référence
    unset($pointer);
    // On réafect a pointer
    $pointer = $pointer2;
    // On supprime la var tampon puisque c'est aussi une référence
    unset($pointer2);
}
echo "before unset =>", var_dump($pointer,$tree);
unset($pointer);
echo "after unset =>", var_dump($pointer,$tree);
// copy le tableau
$copy = $tree;

// Modifie la feuille
$tree['one']['two']['three'] = 'ALTER';
// Affiche le tableau d'origine et la copy
echo "tree => ",var_dump($tree);
echo "copy => ",var_dump($copy); 

Et la voila le résultat.

Code : Tout sélectionner

X-Powered-By: PHP/5.2.0 Content-type: text/html; charset=iso-8859-15 le tree de base =>array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } inwhile key = one & $tree=>array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } inwhile key = two & $tree=>array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } inwhile key = three & $tree=>array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } before unset =>string(8) "__LEAF__" array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } after unset =>NULL array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } } tree => array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(5) "ALTER" } } } copy => array(1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(8) "__LEAF__" } } }
J'ai bien obtenu une copy de mon arbre...

Reste la question, pourquoi y avait il 2 références, d'autant que meme si l'arbre est plus profond, il n'y en a tjs que 2 qui glisse vers le fond...

Au final, j'ai une solution plus propre en utilisant la SPL, mais la conso ram n'est pas la meme et il faut que je vérifie si cela s'applique dans mon contexte (parcequeici, c'est une version simplifiée du problème :) )

Le code avec la SPL
<?php

$tree = new ArrayObject(array('one'=>array('two'=>array('three'=>'LEAF'))));
var_dump($tree);

// crée  pointer sur le tableau (c'est un objet donc passage par réf)
$pointer = $tree;

var_dump($tree,$pointer);
while(is_array(current($pointer))) {
	$key = key($pointer);
	$pointer = new ArrayObject($pointer->offsetGet($key));
	echo "inwhile key = $key & \$tree=>", var_dump($pointer,$tree);
}

echo "before unset =>", var_dump($pointer,$tree);
unset($pointer);
echo "after unset =>", var_dump($pointer,$tree);

// Pour la copie, Au choix 
// soit cette solution
$copy =  $tree->getArrayCopy();
// ou 
// $copy = clone $tree;


// Modifie la feuille
$tree['one']['two']['three'] = 'ALTER';
// Affiche le tableau d'origine et la copy
echo "tree => ",var_dump($tree);
echo "copy => ",var_dump($copy); 

et le résultat

Code : Tout sélectionner

object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } } object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } } object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } } inwhile key = one & $tree=>object(ArrayObject)#2 (1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } } inwhile key = two & $tree=>object(ArrayObject)#3 (1) { ["three"]=> string(4) "LEAF" } object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } } before unset =>object(ArrayObject)#3 (1) { ["three"]=> string(4) "LEAF" } object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } } after unset =>NULL object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } } tree => object(ArrayObject)#1 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(5) "ALTER" } } } copy => object(ArrayObject)#3 (1) { ["one"]=> array(1) { ["two"]=> array(1) { ["three"]=> string(4) "LEAF" } } }

par Hubert Roksor » 30 juin 2007, 01:53

Ah, j'ai compris pourquoi on voit le problème différemment. Pour reprendre le commentaire du premier message:
// parcour le tableau jusqu'a la premiere feuille  
// Ca n'alter rien ca parcours, a la fin du while, echo $pointer retournerai __LEAF__
En fait si, ça altère bien le tableau. Remplacez print_r() par var_dump() et vous verrez que la nature du tableau a changé. Et du coup:
Comment faire pour obtenir une copie
Là aussi, pour moi $copy est une copie de $tree, à condition d'utiliser la définition de PHP concernant les copies de tableau :)

Au passage, j'en ai profité pour chercher comment déréférencer l'élément pointé par $pointer... en fait il suffit de suivre la bonne pratique que j'évoquais plus haut. En supprimant $pointer à la sortie de la boucle, l'élément redevient une valeur et la copie fonctionne comme prévue.

par Hubert Roksor » 30 juin 2007, 01:39

On parle ici de référence sur le tableau et non pas à l'intérieur
Ben en fait le problème vient surtout du fait que le tableau est composé de références (enfin, son seul élément en tout cas). Ensuite il se conjugue avec le mécanisme de copie des tableaux, mais si on n'utilise pas de références dans le tableau tout fonctionne comme on peut s'y attendre.

Pour l'avertissement je doute que ça n'arrive jamais dans la mesure où il ne s'agit pas d'un bug per se et il y a certainement des utilisations légitimes. Quand je fais une copie d'un tableau contenant des références, je sais que les références sont préservées et je n'aimerais pas trop que PHP génère une erreur alors que mon code fonctionne comme je le souhaite. Pour pousser le raisonnement à l'absurde, c'est comme si PHP générait un avertissement lorsque tu assignes un objet "par valeur", pour te prévenir qu'il s'agit en fait du même objet.

Donc voilà, c'est juste un fonctionnement par forcément évident, il suffit juste de s'en rappeler.

par Calimero » 29 juin 2007, 23:44

Hubert, relis bien le code du début du sujet et tous les tests présentés. On parle ici de référence sur le tableau et non pas à l'intérieur (comme dans le bug que pointe Mandar et sur lequel Zeev a laissé un commentaire qui rejoignait ce que tu dis et qui laissait comprendre DIY - "Do It Yourself").

En revanche l'article de Derick Rethans que tu pointes comporte un paragraphe et un schéma qui reproduisent et expliquent très bien le problème sans toutefois souligner que c'en est un (Figure 2, page 3 dans le PDF, page 38 du magazine).

Il faut rentrer assez profondément dans les mécanismes de gestion de mémoire internes au Zend Engine, expliqués dans le PDF, pour comprendre pourquoi une même ligne de code de simple affectation peut soit créer une référence soit une vraie copie (cas illustré dans les tests que nous nous étions échangés plus haut), et il s'agit bien d'un bug dans le sens où le codeur php lambda peut être dérouté par ce code qui ne produit pas forcément le résultat qu'on serait en droit d'attendre, à savoir une copie.

Je pense que si un correctif n'est pas envisagé pour cela par les développeurs du langage, au moins l'ajout d'un message d'avertissement (et pourquoi pas en E_NOTICE...) serait une idée judicieuse pour au moins informer l'utilisateur que ce code est sujet à une interprétation pas toujours heureuse.

Par exemple quelquechose comme : Notice: Assigning by value an already referenced variable may lead to unexpected references [inside] of the target variable - in /home/calimero/php/php_array_cloning.php on line 50

par Hubert Roksor » 29 juin 2007, 21:49

Ok, donc j'ai exécuté le script du premier message, et en effet le résultat n'est pas évident à première vue. En réduisant le code à sa plus simple expression on peut plus précisément identifier d'où vient le problème:
// un tableau de démo 
$tree['one'] = '__LEAF__';

// on crée une référence vers lui-même
$tree['one'] =& $tree['one'];

// copy le tableau 
$copy = $tree; 

// Modifie la feuille 
$tree['one'] = 'ALTER'; 

// Affiche le tableau d'origine et la copy 
print_r($tree); 
print_r($copy);
Ce n'est pas forcément évident, mais c'est dû à la nature des références en PHP. J'ai mis quelques années avant de connaître leur vraie nature et aujourd'hui encore il me reste des lacunes sur leur fonctionnement intrinsèque donc ne vous affolez pas si vous n'étiez pas au courant :lol: Je ne sais pas si on peut le résumer facilement, mais si je le ferais comme ça :
  • toutes les données ("truc", 42, false, etc...) en PHP sont dans des "containers"
  • toute variable ($var) se voit assigner un container et possède un indicateur pour mémoriser s'il s'agit d'une référence ou d'une valeur
  • chaque container possède un compteur pour mémoriser le nombre de variables qui lui sont assignées et quand ce compteur tombe à zéro le container est détruit
Quand on fait
$tree['one'] =& $tree['one'];
...on dit à PHP "assigne par référence à la variable $tree['one'], le container #xyz de la variable $tree['one']". PHP s'exécute et le lien avec le container devient alors une référence. [note: ça ne fonctionne que pour les éléments d'une variable de type composé, donc les tableaux et les objets]

Là où ça se corse, c'est que quand on copie un tableau (et apparemment, quand on clone un objet), toutes les valeurs sont copiées en tant que valeurs, et toutes les références en tant que références. Donc quand on fait une copie du tableau $tree, son élément "one" est créé en tant que référence vers le container #xyz et leur destin est alors scellé.

Je ne sais pas si c'est plus clair, mais si vous avez des doutes et que vous lisez l'anglais je ne saurais trop vous conseiller de lire cet article de Derick Rethans sur les références en PHP, tiré du php|architect de juin 2005.

par Hubert Roksor » 29 juin 2007, 20:57

Je n'ai pas encore lu le topic en profondeur (j'essaie encore de comprendre le code du premier message) mais il y a deux points que j'aimerais qu'on éclaircisse pour éviter que quelqu'un tombe dessus au hasard d'une recherche et retienne une fausse information.
Ce qui n'est pas écrit, dans la version française, c'est que ça concerne aussi les tableaux.
Comme ça a été vérifié dans la suite du topic, sauf erreur les tableaux ne sont pas passés par références, mais lorsqu'on copie un tableau les références qu'il contient restent intactes.
le serialize ne gere pas de notion de référence
Je ne sais pas si on parle de la même chose, mais serialize() préserve les références au sein d'un objet ou d'un tableau. Par exemple
$a = array(5);
$a[1] =& $a[0];

$a = unserialize(serialize($a));
$a[1] = 3;

print_r($a);
La référence $a[1] <=> $a[0] est préservée et lorsqu'on en modifie un l'autre se retrouve également affecté.

Au passage, j'en profite pour partager une habitude indispensable concernant les références. N'oubliez pas de détruire les références inutilisées à la seconde où vous ne vous en servez plus. Dans l'exemple du premier message, il faudrait donc ajouter
unset($pointer);
...juste à la sortie du while(). Il m'est arrivé de perdre beaucoup de temps sur des bugs extrêment étranges (absurdes ou qui semblent se produire de manière aléatoire) parce que j'avais oublié qu'une variable était une référence et que j'avais par la suite réutilisé cette variable. (un nom commun comme $row ou $tmp par exemple)

par Sékiltoyai » 29 juin 2007, 20:24

En fait, c'est pas super compliqué, et très logique, quand on a une référence entre $machin1 et $machin2, si on fait :

Code : Tout sélectionner

$machin2 = 'truc';
On ne supprime pas la référence entre $machin1 et $machin2, mais on affecte à $machin 1 et $machin2 la même valeur, soit 'truc', hé bien quand on fait :

Code : Tout sélectionner

$machin2 = $machin1;
Il n'y a aucune raison que cela supprime la référence, car cela ne fait qu'affecter à $machin2 et $machin1 la valeur de $machin1. C'est logique, '=' ne supprime pas une référence lors d'une affectation banale, il n'y a aucune raison que cela la supprime pour un cas particulier comme celui là...

par titerm » 29 juin 2007, 16:36

Ah ok, la je pige. C'est donc un pb de conception au niveau php.
Tin, ca date de 2002, a mon avis, c'est pas près de changer.

thx.

par Mandar » 29 juin 2007, 16:14

Un problème semblable et l'explication : http://bugs.php.net/bug.php?id=15025

par titerm » 29 juin 2007, 15:58

Ca reproduit bien le pb, mais je suis dubitatif qd meme

Lorsqu'une une référence vers kk $tab[0] est créée, on devrait avoir un pointer sur la le contenu de $tab[0] et point barre. $tab n'est pas modifée par ca. En fait, tant qu'une référence existe, il n'est plus possible de dupliquer la variable, tout ce qu'on obtient c'est des référence. C'est space qd meme .

par Mandar » 29 juin 2007, 15:37

Je vois pas de bug, moi.
Le problème, c'est que lorsque tu modifies la feuille après la copie, $pointer est encore une référence du dernier champ du tableau, donc en modifiant $tree['one']['two']['three'], tu modifies aussi $pointer, donc la copie aussi (à cause de la référence toujours présente).

Un
unset($pointer);
juste avant ta copie et tu n'as plus de problème (ou alors j'ai rien compris :D).


Tu peux reproduire le comportement avec un script plus simple encore :
<?php
	header('Content-type: text/plain');
	
	$tab = array('truc');
	
	// On crée une référence vers la cellule du tableau
	$ref =& $tab[0];
	
	// On fait une copie du contenu, la référence est "dupliquée" aussi
	$copie = $tab;
	
	// On modifie la cellule
	$tab[0] = 'autre chose';
	
	// Les deux tableaux contiennent bien exactement la même chose
	echo "Tableau original :\n";
	print_r($tab);
	echo "\nCopie :\n";
	print_r($copie);
	
	/* Affiche :
Tableau original :
Array
(
    [0] => autre chose
)

Copie :
Array
(
    [0] => autre chose
)
	*/
?>

par titerm » 29 juin 2007, 15:35

Bah dans le code que je donne, si on ne fais pas le serialize/unserialize, le bug est la.

Pour ton cas, y a encore plus etrange...

// simple copie, on modifie l'original juste après : la copie ne change pas [OK]
$tree['one']['two']['three'] = 0;
$copy = $tree;
$tree['one']['two']['three'] = 1;

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br><hr>';

// référence puis modification de l'original : la référence change [OK]
$copy = &$tree;
$tree['one']['two']['three'] = 2;

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br><hr>';

// simple copie et modification : la copie change [??] mais pas copy2 !!!
$copy2 = $tree;
$copy = $tree;
$tree['one']['two']['three'] = 3;

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br>';
echo 'copy2 : '.print_r($copy2,true).'<br><hr>';

// copie par sérialisation : la copie change [??] mais pas copy3
$copy3 = unserialize(serialize($tree));
$copy = unserialize(serialize($tree));
$tree['one']['two']['three'] = 4;

echo 'copy : '.print_r($copy3,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br>';
echo 'copy3 : '.print_r($copy3,true).'<br><hr>';

Code : Tout sélectionner

copy : Array ( [one] => Array ( [two] => Array ( [three] => 0 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 1 ) ) ) copy : Array ( [one] => Array ( [two] => Array ( [three] => 2 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 2 ) ) ) copy : Array ( [one] => Array ( [two] => Array ( [three] => 3 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 3 ) ) ) copy2 : Array ( [one] => Array ( [two] => Array ( [three] => 2 ) ) ) copy : Array ( [one] => Array ( [two] => Array ( [three] => 3 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 4 ) ) ) copy3 : Array ( [one] => Array ( [two] => Array ( [three] => 3 ) ) )
On peut donc en déduire que lorsque une variable est utilisé en tant que référence, elle est transformée pointeur et se comporte après coup systématiquement en pointeur, meme si tu lui affecte autre chose qu'une référence.

Personne pour tester ce truc en 5.2.3 ?

par Calimero » 29 juin 2007, 14:50

J'utilise PHP 5.2.1 (cli) (built: May 22 2007 19:05:44) sous Linux.

Essaye ça (regarde bien le test du serialize en 4°) :
// simple copie, on modifie l'original juste après : la copie ne change pas [OK]
$tree['one']['two']['three'] = 0;
$copy = $tree;
$tree['one']['two']['three'] = 1; 

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br>';

// référence puis modification de l'original : la référence change [OK]
$copy = &$tree;
$tree['one']['two']['three'] = 2;

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br>';

// simple copie et modification : la copie change [??]
$copy = $tree;
$tree['one']['two']['three'] = 3;

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br>';

// copie par sérialisation : la copie change [??]
$copy = unserialize(serialize($tree));
$tree['one']['two']['three'] = 4;

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br>';

// Pour forcer la copie : revenir à 1 seule référence [OK]
unset($copy);
$copy = $tree;
$tree['one']['two']['three'] = 5;

echo 'copy : '.print_r($copy,true).'<br>';
echo 'tree : '.print_r($tree,true).'<br>';
Chez moi ça donne :

Code : Tout sélectionner

copy : Array ( [one] => Array ( [two] => Array ( [three] => 0 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 1 ) ) ) copy : Array ( [one] => Array ( [two] => Array ( [three] => 2 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 2 ) ) ) copy : Array ( [one] => Array ( [two] => Array ( [three] => 3 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 3 ) ) ) copy : Array ( [one] => Array ( [two] => Array ( [three] => 4 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 4 ) ) ) copy : Array ( [one] => Array ( [two] => Array ( [three] => 4 ) ) ) tree : Array ( [one] => Array ( [two] => Array ( [three] => 5 ) ) )
Le bug serait donc bien dû à une référence non-détruite, vu que même serialize/unserialize n'y change rien.

Dans le code que tu donnais juste au dessus, tu stockes la référence dans $pointer et ensuite la copies dans $copy, ce qui peut expliquer pourquoi ça marche.