Méthodes magiques : __clone

Rechercher

Méthodes magiques : __clone

  • Par Palleas
  • 0 commentaire
  • 13556 lectures
  • RSS -  Atom

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 :

<?php
class Point {
/**
* Abscisse du point
  • *
    * @var integer
    */
    protected $_x = 0;
    /**
    * Ordonnée du point
  • *
    * @var integer
    */
    protected $_y = 0;
    /**
    * Fixe les coordonnées du point
  • *
    * @param integer $x Abscisse du point
    * @param integer $y Ordonnée du point
    * @return void
    */
    public function setCoords($x,$y) {
    $this->_x = (int)$x;
    $this->_y = (int)$y;
    }
    }
  • 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);
    var_dump($oDot);
    $oNewDot = $oDot;
    $oNewDot->setCoords(20,20);
    var_dump($oDot);
    ?>

    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);
    var_dump($oDot);
    $oNewDot = clone $oDot;
    $oNewDot->setCoords(20,20);
    var_dump($oNewDot);
    var_dump($oDot);
    ?>

    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) :

    <?php
    class Sheep {
    /**
    * Nom du mouton
  • *
    * @var String
    */
    protected $_name;
    /**
    * Contructeur de la classe Sheep
  • *
    * @param String $name nom du mouton
    */
    public function __construct($name) {
    $this->_name = (string)$name;
    }
    /**
    * Methode magique clone
  • *
    * @return void
    */
    public function __clone() {
    $this->_name = 'Copie de '.$this->_name;
    }
    }
  • Instancions notre classe, puis dupliquons notre objet :

    <?php
    $oSheep = new Sheep('Dolly');
    $oNewSheep = clone $oSheep;
    var_dump($oSheep);
    var_dump($oNewSheep);
    ?>

    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 :

    <?php
    class Singleton {
    /**
    * Instance de la classe Singleton
  • *
    * @var Singleton
    */
    protected static $_instance = null;
    /**
    * Constructeur de la classe
  • *
    * @access protected
    */
    protected function __construct() {}
    /**
    * getInstance() : recuperation de l'instance de la classe
  • *
    * @return Singleton
    */
    public static function getInstance() {
    if(null === self::$_instance) self::$_instance = new Singleton();
    return self::$_instance;
    }
    /**
    * Methode magique clone
  • *
    * @return void
    */
    public function __clone() {
    throw new Exception('Are you Trying to clone me ? I\'m a Singleton dude !');
    }
    }
    try {
    $oSingleton = Singleton::getInstance();
    clone $oSingleton;
    }catch(Exception $e) {
    echo 'Oops, exception : ', $e->getMessage();
    }
    ?>
  • 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 !