PHP: Autres fonctions méconnues de la programmation objet

Ceci est la seconde partie de PHP: Quelques fonctions méconnues de la programmation objet.

__get, __set, __isset, __unset

PHP permet d’affecter à une instance d’une classe n’importe quelle valeur (presque comme à un tableau de hashage) pour autant que ça ne soit pas une variable privée:

<?php

class Objet {};
$obj = new Objet;
$obj->exemple = 1;
echo $obj->exemple . "\n"; // affiche '1'

Lors de l’affectation d’une variable de telle façon, PHP appelle en fait les fonctions __set et __get pour l’affectation et la lecture de la valeur. Il est évidement bien entendu possible de les réécrire afin de définir un nouveau comportement:

<?php // Objet.class.php

class Objet
{
    private $data;
    public function __construct()
    {
        $data = array();
    }

    public function __set($name, $value)
    {
        echo "Setting $name to $value\n";
        $this->data[ $name ] = $value;
    }

    public function __get($name)
    {
        echo "returns $name\n";
        return $this->data[ $name ];
    }
};
<?php // fichier test.php

require_once("Objet.class.php");
$obj = new Objet;
$obj->priv = 1;
echo $obj->priv;

Cet exemple retournera:

$ php test.php
Setting priv to 1
return priv
1

Cela permet de dévier les affectations des variables dans les instances de classe, mais aussi de « protéger » le contenu des classes. Il faut également noter que si l’appel s’effectue pour une variable déclarée privée dans la classe, on sera également redirigé par __get et __set et l’on n’aura plus l’erreur comme quoi on fait appel à une variable privée.

De plus, de la même façon que pour __get et __set, on aura également __isset et __unset qui serviront à redéfinir le comportement lors d’un appel à isset ou à unset.

__call et __callStatic

Comme pour les variables, il est possible d’attraper les appels des fonctions non définies et privées dans les classes, statiques ou non en utilisant __call et __callStatic.

<?php // Objet.class.php
class Objet
{
    public function __call($func, $args)
    {
        echo "Calling $func\n";
    }

    public static function __callStatic($func, $args)
    {
        echo "Calling static $func\n";
    }
};
<?php // test.php

class Objet {};
$obj = new Objet;
$obj->exemple = 1; echo $obj->exemple . "\n";

Et à l’exécution:

$ php test.php
Calling hello
Calling static hello

Ces deux fonctions, ajoutées aux setters/getters du dessus, permettent de « wrapper » entièrement des instances de classes permettant des utilisations bien plus intéressantes qu’un simple héritage.

__clone

__clone est appelé lorsque l’on crée une copie d’une instance d’objet en appelant clone. Cela résulte à deux instances de la classe totalement indépendante avec le même contenu. Bien que je ne sois pas sûr que clone soit utilisé tous les jours, __clone peut éventuellement être utilisé afin de modifier l’un des deux objets avant ou après le clonage, ou encore de ne pas effectuer ce clonnage afin d’assurer d’avoir un singleton.

__sleep, __wakeup

Finalement, __sleep et __wakeup sont utilisées lors de la sérialisation/désérialisation d’un objet. Elles seront appelées respectivement à l’utilisation de serialize et unserialize. A noter que __sleep doit retourner un array avec la liste des champs à sérialiser.

Comme les courts exemples sont mieux que les longs discours, voici une classe ouvrant un fichier à sa création, le refermant à sa sérialisation, pour le réouvrir à la désérialisation:

<?php // Objet.class.php

class Objet
{
    private $file_name = NULL;
    private $file_hd = NULL;

    public function __construct($file_name)
    {
        $this->file_name = $file_name;
        $this->file_open();
    }

    public function __destruct()
    {
        if($this->file_hd !== NULL)
            $this->file_close();
    }

    private function file_open()
    {
        echo "Opening file\n";
        $this->file_hd = fopen($this->file_name, "a+");
    }

    private function file_close()
    {
        if(NULL === $this->file_hd)
            return false;

        echo "Closing file\n";
        fclose($this->file_hd);
        $this->file_hd = NULL;
    }

    public function __sleep()
    {
        echo "* Serialization.\n";
        $this->file_close();

        return(array('file_name'));
    }

    public function __wakeup()
    {
        echo "* Unserialization.\n";
        $this->file_open();
    }
};

Et testons tout ça:

<?php // fichier test.php

require_once("Objet.class.php");

$obj = new Objet('helloworld.txt');

$serializedObj = serialize($obj);
unset($obj);
var_dump($serializedObj);

$newObj = unserialize($serializedObj);

echo "Fin du script\n";

On peut vérifier le comportement:

$ php test.php
Opening file
* Serialization.
Closing file
string(62) "O:5:"Objet":1:{s:16:"Objetfile_name";s:14:"helloworld.txt";}"
* Unserialization.
Opening file
Fin du script
Closing file

J’espère que quelqu’un aura découvert quelque chose ! :)

Pour ces deux articles, je me suis un peu inspiré d’un article analogue m’ayant poussé à lire un peu plus la doc de PHP: 9 magic methods for php (thinkvitamin.com).

Laisser un commentaire


NOTE - Vous pouvez utiliser les éléments et attributs HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>