Méthodes magiques : __sleep() et __wakeup()
Nous avons étudié dans les précédents articles les méthodes magiques__clone(), __set(), __get() et __call(). PHP ajoute à ces dernières deux méthodes magiques supplémentaires __sleep() et __wakeup() qui permettent de surcharger le processus natif de sérialisation et de désérialisation des données de PHP. C'est ce que nous allons expliquer au cours de tutoriel avec quelques exemples concrets et faciles à comprendre.
Qu’est ce que la sérialisation de données ?
Pour résumer très simplement, l’action de sérialiser une variable consiste à convertir celle-ci en une chaine de caractère pour, par exemple, la stocker dans un fichier texte. A contrario, l’action de désérialiser une chaine de caractères consiste à appliquer le procédé inverse afin d'en récupérer la variable d’origine. Ce procédé ne se limite pas à PHP, vous le retrouverez dans beaucoup de langages tels que Java, Action Script, C, C#... pour ne citer qu'eux.
Les mots sérialisation et désérialisation sont sans doute les mots les plus courants mais vous entendrez / lirez peut-être quelque part les mots linéarisation / délinéarisation ou bien encore marshalling / unmarshalling. Sérialiser, linéariser, marshaller (respectivement désérialiser, délinéariser et unmarshaller) définissent les notions de sérialisation et désérialisation.
Syntaxiquement parlant, la serialisation de données se traduit par l’utilisation de la fonction serialize(), l’action inverse se traduit par unserialize(). Voyons quelques exemples.
Serialisation / Désérialisation d’une variable de type integer
<?php$iVar = 1;echo '<pre>';echo '</pre>';?>
Le premier var_dump() affiche la chaine suivante sur la sortie standard :
string(4) "i:1;"
Et le second affiche la suivante :
int(1)
Vous déduirez bien sûr que ce n’est pas intéressant de serializer un integer (ou bien une chaîne de caractères évidemment) étant donné que vous pouvez le stocker directement. Néanmoins, cet exemple reste utile à l’illustration d’un principe simple : la serialisation d’une variable préserve le type de celle-ci. Ainsi on aura bien au retour un integer, au même titre que l’on conservera un Array ou encore un objet.
Sérialisation / désérialisation d'un tableau (Array)
Prenons à présent un tableau comme exemple. La sérialisation et la désérialisation deviennent ici beaucoup plus intéressantes dans la mesure où elles vont nous permettre de transformer (et de récupérer) l'intégralité d'un tableau.
<?phpecho '<pre>';echo '</pre>';?>
Le premier var_dump affiche alors :
string(87) "a:5:{i:0;s:4:"Riri";i:1;s:4:"Fifi";i:2;s:6:"Loulou";i:3;s:6:"Donald";i:4;s:6:"Picsou";}"
La sérialisation du tableau finalement construit une chaîne de caractères d'une longueur de 87 caractères. Etudions rapidement la structure de cette chaine.
- Le premier caractères de la chaine indique le type de la variable sérialisée. Ici, la lettre "a" indique donc que nous venons de sérialiser un tableau (array).
- Puis, nous lisons le chiffre 5 qui indique le nombre d'éléments dans le tableau. Entre les deux parenthèses, nous trouvons tous les éléments du tableau.
- La première le lettre d'un groupe élément indique le type de l'index dans le tableau suivi de sa valeur. Ici nous avons sérialisé un tableau indexé numériquement, c'est pourquoi chaque index est un entier ("i") dont la valeur est comprise entre 0 et 4 compris. Si nous avions utilisé un tableau associatif, les indexes seraient devenus des chaines de caractères ("s").
- Nous lisons ensuite pour chaque valeur stockée, son type ("s"), sa longueur (4) et sa valeur stockée dans le tableau ("Riri").
Grâce à toutes ces informations, la fonction unserialize() est capable de reconstruire entièrement le tableau en analysant la chaine formattée.
C'est pourquoi le second var_dump() affiche le résultat suivant dans lequel nous retrouvons toutes les informations de la chaîne sérialisée :
array(5) {[0]=>string(4) "Riri"[1]=>string(4) "Fifi"[2]=>string(6) "Loulou"[3]=>string(6) "Donald"[4]=>string(6) "Picsou"}
Tentons maintenant de sérialiser / désérialiser un objet.
Sérialisation et désérialisation d'un objet
Voici maintenant l’exemple le plus intéressant, la sérialisation d’un objet. Prenons une classe basique avec différentes propriétés. Nous utilisons volontairement des propriétés publiques, privées et protégées afin de visualiser comment se comporte la sérialisation d'un objet avec des propriétés différentes.
<?phpclass Dormeur {/*** Age du nain Dormeur** @var integer* @access protected*/protected $_age;/*** Le nom porte-t-il un bonnet ?** @var boolean* @access protected*/protected $_aSonBonnet;/*** Sa couleur préférée** @var string* @access private*/private $_couleurPreferee;/*** Ses hobbies** @var array* @access public*/public $_gouts;/*** Constructeur de la classe*/public function __construct() {$this->_age = 19;$this->_aSonBonnet = true;$this->_couleurPreferee = 'rouge';}}?>
Le nom de la classe vous semblera sans doute débile, mais nous trouvions plutôt de circonstance d’avoir une classe nommée « Dormeur » pour introduire une méthode nommée « __sleep() ».
Passons maintenant successivement à son instanciation, sa serialisation et sa deserialisation :
<?php$oDormeur = new Dormeur();echo '<pre>';echo '</pre>';?>
Nous obtenons alors respectivement les résultats suivants :
string(181) "O:7:"Dormeur":4:{s:7:" *_age";i:19;s:14:" *_aSonBonnet";b:1;s:25:" Dormeur_couleurPreferee";s:5:"rouge";s:6:"_gouts";a:3:{i:0;s:7:"musique";i:1;s:7:"cinéma";i:2;s:7:"curling";}}
object(Dormeur)#2 (4) {["_age:protected"]=>int(19)["_aSonBonnet:protected"]=>bool(true)["_couleurPreferee:private"]=>string(5) “rouge”["_gouts"]=>array(3) {[0]=>string(7) “musique”[1]=>string(7) “cinéma”[2]=>string(7) “curling”}}
Vous saviez déjà que le type des variables était préservé, il en va de même pour la visiblité (private, protected et public), cela ne change en aucun cas la structure de la classe. Nous vous parlions de stockage d’instance, il faut bien faire attention à ce que votre classe soit déclarée avant la désérialisation, sans quoi vous aurez une erreur :
Fatal error: Class ‘Dormeur’ not found in /home/path/to/your/script.php on line xx
Vous remarquerez le « o » comme premier caractère de la chaîne sérialisée qui indique que nous venons bien de sérialiser un objet.
Méthodes magiques __sleep() et __wakeup()
Maintenant que nous avons bien saisi le principe de la serialisation, nous allons pouvoir entrer dans le vif du sujet et parler des méthodes magiques __sleep() et __wakeup().
Ces méthodes seront respectivement appelée par votre script lors de l’utilisation de serialize() et de unserialize(). Voici une démonstration, nous allons ajouter les méthodes __sleep() et _wakeup() à notre Dormeur.
La méthode magique __sleep() doit cependant effectuer une action pour que la serialisation se passe bien. C’est ici que l’on peut voir le premier intérêt de la serialization d’une instance de classe : vous allez selectionner quelles propriétés de votre instance vous souhaitez stocker. Pour ce faire, la méthode __sleep() doit retourner un tableau contenant les noms des propriétés à conserver :
<?phpclass Dormeur {/*** Méthode magique __sleep() Appelée lors d’un serialize()** @return Array la liste des paramètres à conserver*/public function __sleep() {echo 'Bon ben moi, je vais dormir.';}/*** Méthode magique __wakeup() Appelée lors d’un unserialize()** @return void*/public function __wakeup() {echo 'bon ben moi, je vais me faire un café.';}}?>
Ici on ne choisit de garder que l’âge de Dormeur, sa couleur préférée et si oui, ou non, il a son bonnet sur la tête.
En exécutant de nouveau le code précédent, vous allez voir les deux echo s’afficher à la suite («Bon ben moi, je vais dormir. » puis « bon ben moi, je vais me faire un café. ») confirmant que les méthodes __sleep() et __wakeup() ont bien été successivement appelées.
Le résultat de la sérialisation est le suivant :
string(108) "O:7:"Dormeur":3:{s:7:"*_age";i:19;s:14:"*_aSonBonnet";b:1;s:25:"Dormeur_couleurPreferee";s:5:"rouge";}"
On constate que l’on ne conserve pas les goûts de Dormeur. Et voici ce que vous affichera le deuxième var_dump :
object(Dormeur)#2 (4) {["_age:protected"]=>int(19)["_aSonBonnet:protected"]=>bool(true)["_couleurPreferee:private"]=>string(5) “rouge”["_gouts"]=>NULL}
Vous remarquerez que seules les propriétés spécifiées dans le __sleep() ont été mémorisées, la propriété « _gouts », quant à elle, est égale à NULL, son contenu n’a pas été conservé. Bien entendu, l’intérêt ne s’arrête pas là, vous allez pouvoir effectuer les actions que vous désirez dans ces méthodes : synchronisation avec une base de données, fermer une connexion à une base de données lors de la sérialisation, la réouvrir lors de la deserialisation, etc.
Pour les amoureux du bricolage, il est également possible d’implémenter la notion de « transtypage » d’objet, totalement absente de PHP à l’heure actuelle.
Conclusion
Nous venons de découvrir les notions de sérialisation et de désérialisation d'une variable, d'un tableau ou bien d'un objet. Puis, nous avons montré comment il était possible de surcharger le comportement original de sérialisation / désérialisation d'un objet grâce aux méthodes magiques __sleep() et __wakeup().
Les commentaires
2. Par Niko le 07/07/2009 22:00
Excellent vraiment.
3. Par abdelseo le 12/08/2009 06:02
Merci pour beaucoup,excellent tuto
4. Par Ixabro le 19/08/2009 00:08
Excellent, tutoriel.
Tout est clair pour moi.
C'est très bien expliqué.
Et les exemples sont très compréhensifs.
5. Par mani le 22/09/2009 23:57
Toujours aussi excellent je vais me coucher et je reviendrais demain matin continuer ma lecture. Je crois tout de même avoir relevé deux coquilles.
-Entre les deux parenthèses, nous trouvons tous les éléments du tableau.-
Ce sont des accolades.
-Le nom porte-t-il un bonnet ?- ce n'est pas le nom mais le nain qui porte ou pas un bonnet .
Cela n'enlève rien à la qualité de votre tuto merci encore.
6. Par aznrei le 14/01/2013 15:13
Personnellement je vois pas trop à quoi va servir sleep et wakeup =/
Peut-être qu'en relisant 3 fois j'arriverais à comprendre xd
1. Par Monem le 24/12/2008 09:51