Clonage vs. Copiage.

ViPHP
ViPHP | 4674 Messages

19 juil. 2007, 13:52

Bonjour,

j'aborde une notion qui est nouvelle pour moi : le clonage, avec le mot clé clone de PHP (5).
Pour avoir une copie (clone ou copie) d'un objet, soit on utilise clone, ou alors la sérialization.

J'aurais aimé avoir votre avis là-dessus. Quelles sont les différences, les avantages, et les inconvénients ? :)

Merci.
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 928 Messages

19 juil. 2007, 13:58

Salut,
un code valant mieux qu'un long discours, avec ça tu devrais comprendre directement la différence entre clonage et pas clonage :
class Test
{
	public $toto;
}

// On instancie une première fois Test et on set la propriété toto sur bidule
$test1 = new Test;
$test1->toto = 'bidule';

// En PHP5, le code ci dessous fait une copie par référence
$test2 = $test1;
// Donc quand je modifie $test2->toto je modifie en fait $test1->toto aussi
$test2->toto = 'nouveau machin';

// La preuve :
echo 'test1->toto = ' . $test1->toto . '<br />';
echo 'test2->toto = ' . $test2->toto . '<br />';
echo '----------<br />';

// Là maintenant je fais un objet $test3 en clonant $test2
$test3 = clone $test2;

// Si je modifie $test3->toto, ben ni $test1 ni $test2 ne seront modifiés
$test3->toto = 'le truc ultime';

// La preuve :
echo 'test1->toto = ' . $test1->toto . '<br />';
echo 'test2->toto = ' . $test2->toto . '<br />';
echo 'test3->toto = ' . $test3->toto . '<br />';[/code]

ce qui affichera à l'écran
[quote]test1->toto = nouveau machin
test2->toto = nouveau machin
----------
test1->toto = nouveau machin
test2->toto = nouveau machin
test3->toto = le truc ultime

Donc en clair si tu veux créer un nouveau objet à partir du premier, mais que tu veux qu'il soit indépendant du premier, il faut le cloner.

Important : en PHP4, lorsque tu fais $test1 = $test2 l'objet est automatiquement cloné. c'est une des différences entre PHP4 et PHP5, et sur un projet totalement orienté objet ça peut jouer sur la portabilité.

Astuce : voici un moyen de rendre le clone portable en PHP4 / PHP5 :
if (version_compare(phpversion(), '5.0') < 0)
{
    eval('function clone($object){return($object);}');
}
Modifié en dernier par Genova le 19 juil. 2007, 14:14, modifié 1 fois.

ViPHP
ViPHP | 5924 Messages

19 juil. 2007, 14:07

Le problème, c'est que, à l'instar de son homologue en java, clone fait une copie superficielle, c'est à dire si certaines attributs de l'objet à cloner sont des tableaux ou des objets, la copie se fera par référence. Pour faire un clonage récursif, il faut le faire soi même (Le plus simple, c'est en implémentant la méthode magique __clone(), le plus infaillible, en parcourant toutes les classes ou les tableaux dans sa fonction de clonage faite maison)...

ViPHP
ViPHP | 4674 Messages

19 juil. 2007, 14:07

LoL. J'ai compris le clonage. T'as pas compris ma question, mais c'est sûrement de ma faute.

On a deux façons d'obtenir un clone apparement :
$a = new A;
$b = unserialize(serialize($a));
ou
$a = new A;
$b = clone $a;
Ou est la différence ?

Et j'aimerais savoir si instancier un objet est plus ou moins lourd que de le cloner. Qu'est-ce qui est le plus coûteux en ressources ?
$a = new A;
$b = new A;
et
$a = new A;
$b = clone $a;
En considérant qu'avec la méthode __clone() on réinitialise l'objet.

Merci.
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 928 Messages

19 juil. 2007, 14:14

Voilà la principale différence entre les deux :
class A
{
	public $toto;
}
$var = 'bonjour';

$a = new A;
$a->toto = &$var;

$b = clone $a;
$b->toto = 'salut';

echo $var . '<br />';

$c = unserialize(serialize($a));

$c->toto = 'lol';

echo $var;
Avec un clonage basique, l'objet sera cloné, mais si les propriétés ont une référence sur quelque chose, cette référence sera gardée.
Avec le serialize() / unserialize(), les références ne sont pas conservées.

Ensuite en terme de ressource, à mon avis ça doit dépendre des cas, essaie de faire des benchmark en temps et en mémoire pour tester ;)

ViPHP
ViPHP | 4674 Messages

19 juil. 2007, 14:18

D'accord, mais théoriquement ? Si quelqu'un aurait une idée, ce serait bien :)
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 5924 Messages

19 juil. 2007, 14:20

Avec le serialize() / unserialize(), les références ne sont pas conservées.
T'en es sur ? J'ai lu ici que seriralize() gérait les références...

ViPHP
ViPHP | 4674 Messages

19 juil. 2007, 14:26

Faites le test, vous serez fixé :)
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 928 Messages

19 juil. 2007, 15:31

Si tu regardes mon code Sekil tu verras que justement il illustre le fait que serialize() ne les garde pas. Ce qui est logique au fond puisque serialize() transforme quelque chose d'abstrait en une chaîne de caractère, il serait par conséquent illogique de garder les références sur des données volatiles comme les variables qui disparaissent en fin de script.

Alors la différence entre new et clone en terme de performance, j'ai fait un benchmark :

(100000 itérations)

Code : Tout sélectionner

class A { public $titi = array('toto', 'tutu', 'tyty'); private $test = 'salut'; } $a = new A; // 0.18666601181 $b = clone $a; // 0.0804181098938

Code : Tout sélectionner

class A { public $titi = array('toto', 'tutu', 'tyty'); private $test = 'salut'; public function __clone() { } } $a = new A; // 0.182492017746 $b = clone $a; // 0.171550989151

Code : Tout sélectionner

class A { public $titi = array('toto', 'tutu', 'tyty'); private $test = 'salut'; public function __construct() { } public function __destruct() { } } $a = new A; // 0.308668851852 $b = clone $a; // 0.144464969635

Code : Tout sélectionner

class A { public $titi = array('toto', 'tutu', 'tyty'); private $test = 'salut'; public function __construct() { } public function __destruct() { } public function __clone() { } } $a = new A; // 0.314460992813 $b = clone $a; // 0.230401039124

J'ai aussi essayé en mettant un tableau de 100000 éléments dans la propriété $titi, ça ne change rien.

En conclusion cloner semble plus rapide qu'instancier, mais à mon avis c'est une mauvaise idée car :
- l'optimisation est très faible (insignifiante, a moins de manipuler une collection de 100000 objets dans ta page ...)
- ce ne serait pas spécialement du point de vu du fonctionnement du langage et du lecteur du code

ViPHP
ViPHP | 4674 Messages

19 juil. 2007, 16:12

- ce ne serait pas spécialement du point de vu du fonctionnement du langage et du lecteur du code
Pas compris ça ...

Je travaille sur un outil où j'ai pas mal d'objets, avec une conception assez lourde. Et le moindre gain de ressource n'est pas à négliger. Merci pour les tests en tout cas :)
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 928 Messages

19 juil. 2007, 16:18

J'ai oublié des mots :mrgreen:

En gros je voulais dire que ce serait pas très logique d'un point de vu conceptuel d'utiliser un clone pour créer des objets. Mais si ton projets doit manipuler des tas d'objets issus de la même classe, apparemment clone semble plus rapide.

ViPHP
ViPHP | 4674 Messages

19 juil. 2007, 16:39

En fait, je dois boucler une collection d'objets. Mais c'est bon, j'ai trouvé une meilleure façon de procéder. J'instancie mes objets en dehors de ma boucle, et je me fais un tas de setters pour tout remettre. Y a moyen sans que ce soit trop lourd.

Comme ça, pas de clonage, pas de copie, et 1 seule instance. Mais je vais tout de même utiliser le clonage sur 1 objet en particulier.

Merci à vous deux : pour les tests, et pour les informations :) Sujet clos (peut être l'épingler ? ça pourrait servir pour d'autres, je sais pas).
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

ViPHP
ViPHP | 5924 Messages

22 juil. 2007, 02:18

Si tu regardes mon code Sekil tu verras que justement il illustre le fait que serialize() ne les garde pas. Ce qui est logique au fond puisque serialize() transforme quelque chose d'abstrait en une chaîne de caractère, il serait par conséquent illogique de garder les références sur des données volatiles comme les variables qui disparaissent en fin de script.
Je viens de vérifier, en effet, serialize se comporte tout à fait normalement, c'est bizarre que j'ai cru entendre le contraire... Enfin bon, c'est vrai que le comportement contraire aurait été plus que louche, c'est mieux comme cela :).