Méthodes magiques : __clone
PHP depuis sa version 5 implémente des méthodes magiques, que vous pouvez implémenter dans vos classes, et qui seront automatiquement appelées par votre script. La méthode magique __clone() est l'une de ces méthodes. Le tutoriel qui suit introduit le fonctionnement de la méthode magique __clone() en se basant sur des exemples simples et concrets.
Rappels sur la programmation orientée objet
Pour bien comprendre le principe de cette méthode et son champ d’application, voici comment marche (dans les grandes lignes), la programmation orientée objet.
Prenons un objet « Point » qui dispose des propriétés protégées « _x » et « _y », qui ici correspondront aux coordonnées du point. On y ajoute une méthode setCoords() qui permet de spécifier ces valeurs :
<?phpclass Point {/*** Abscisse du point
Lorsque l'on souhaite utiliser cet objet, on appelle son constructeur (ici, il est déclaré implicitement) et nous le stockons dans une variable le résultat de cet appel. C'est ce que l'on appelle la phase d'instanciation de la classe. La variable est une instance de notre objet.
<?php// instanciation de l'objet Point// et stockage de l'instance dans la variable "oDot"$oDot = new Point();?>
PHP 5 et le passage des objets par référence
Voilà pour les grandes lignes. Maintenant, parlons (désolé) PHP 4. Avec la version 4 de PHP, les objets étaient passés par « valeur » (ou bien par « copie »), ce qui signifie que lorsqu'une variable « $a » contenant l’instance d'un objet Point était copiée dans une variable $b, alors les deux variables $a et $b contenaient chacune une instance (objet) unique. Depuis PHP 5, les objets sont passés et copiés par référence. Cela implique donc qu'en cas de copie d'une variable $a vers $b, cette dernière contiendra la référence référence vers l'objet de la variable $a. Prenons un exemple simple pour illustrer concrètement la théorie.
<?php$oDot = new Point;$oDot->setCoords(10,10);$oNewDot = $oDot;$oNewDot->setCoords(20,20);?>
Ici, nous instancions notre objet Point et nous stockons l’instance dans la variable $oDot. Puis nous copions le contenu de la variable $oDot dans la variable $oNewDot. Nous modifions les propriétés de l’instance de l’objet Point stocké dans $oNewDot et enfin nous affichons les informations de l’instance de notre objet.
Le premier var_dump() affiche alors :
object(Point)#1 (2) { ["_x:protected"]=> int(10) ["_y:protected"]=> int(10) }
Le second retourne :
object(Point)#1 (2) { ["_x:protected"]=> int(20) ["_y:protected"]=> int(20) }
Nous constatons ici que nous avons affaire à une seule et unique instance bien que l'on ait essayé de copier la variable.
Seulement voilà, dans certains cas il peut être utile de dupliquer une instance de classe, ce qui est impossible en passant par la méthode « PHP 4 », comme nous venons de le voir. La solution est donc d'avoir recours au mot clé « clone ».
Le mot-clé clone
L’utilisation est simple, au lieu de faire :
<?php$oNewDot = $oDot;?>
Nous allons faire :
<?php$oNewDot = clone $oDot;?>
En modifiant et en exécutant le code précédent :
<?php$oDot = new Point;$oDot->setCoords(10,10);$oNewDot = clone $oDot;$oNewDot->setCoords(20,20);?>
Voici l’affichage du premier var_dump() :
object(Point)#1 (2) { ["_x:protected"]=> int(10) ["_y:protected"]=> int(10) }
Et voici l’affichage du deuxième, nous pouvons constater que les propriétés _x et _y sont bien modifiées :
object(Point)#1 (2) { ["_x:protected"]=> int(20) ["_y:protected"]=> int(20) }
Voici l’affichage du troisième et dernier var_dump :
object(Point)#1 (2) { ["_x:protected"]=> int(10) ["_y:protected"]=> int(10) }
Vous le constatez vous-même, nous avons bien deux instances distinctes de la classe Point.
Implémentation de la méthode magique __clone
Maintenant que nous avons bien saisi le concept de clonage d’objets en PHP, nous allons enfin pouvoir parler de la méthode magique « __clone() ». Le principe de cette méthode est simple, elle sera automatiquement appelée lorsque l’on utilisera le mot clé clone sur un objet. Prenons un exemple.
Voici une classe Sheep (mouton, pour les anglophobes) :
<?phpclass Sheep {/*** Nom du mouton
Instancions notre classe, puis dupliquons notre objet :
<?php$oSheep = new Sheep('Dolly');$oNewSheep = clone $oSheep;?>
Voici ce qu’affiche le premier var_dump() :
object(Sheep)#3 (1) { ["_name:protected"]=> string(5) “Dolly” }
Et voici ce qu’affiche le deuxième var_dump() :
object(Sheep)#4 (1) { ["_name:protected"]=> string(14) “Copie de Dolly” }
En dupliquant l’instance « $oSheep , ma méthode __clone a été automatiquement appelée et a modifié le nom en ajoutant « Copie de » en préfix du nom du mouton. Vous remarquerez que nous avons bien utilisé la référence sur l'objet lui même $this pour modifier les informations de la nouvelle instance.
Implémentation dans le cas d’un Singleton
Le Singleton est un design pattern permettant de s’assurer de n’avoir qu’une seule instance d’un objet dans un script. Pour plus d’informations, rendez-vous sur ce billet.
Seulement, même si votre constructeur est déclaré en accès privé ou protégé, il sera toujours possible de cloner votre objet. Vous perdrez alors le principe de singleton. Pour pallier à ce problème, il suffit de lever une exception lorsque le développeur utilisant le Singleton décide de le cloner :
<?phpclass Singleton {/*** Instance de la classe Singleton
Et voici ce qui s’affichera à l’exécution du code :
Oops, exception : Are you Trying to clone me ? I’m a Singleton dude !
L'exception a bien été levée et a donc empêché la tentative de clônage de l'objet. Le singleton conservera ainsi son unicité dans l'application développée.
Conclusion
Ce tutoriel vous a présenté le fonctionnement global de la méthode magique __clone() et du clônage d'objet en PHP 5. Vous êtes à présent à même d'utiliser cette méthode magique dans vos applications pour faciliter les clônages de vos objets ou bien pour en modifier les comportements d'origine.
Les commentaires
Soyez le premier à réagir !