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:
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:
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 ];
}
};
require_once("Objet.class.php");
$obj = new Objet;
$obj->priv = 1;
echo $obj->priv;
Cet exemple retournera:
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.
class Objet
{
public function __call($func, $args)
{
echo "Calling $func\n";
}
public static function __callStatic($func, $args)
{
echo "Calling static $func\n";
}
};
class Objet {};
$obj = new Objet;
$obj->exemple = 1; echo $obj->exemple . "\n";
Et à l’exécution:
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:
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:
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:
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).