Articles

Le mois de la fonction PHP : linéarisation maison

  • Ecrit par Damien Seguy
  • lundi 23 avril 2007
Image pour le titre du contenu

This document is also available in English en 


Il est bien connu que les sessions ne supportent pas les ressources. Si vous placez dans une session une connexion à MySQL, par exemple, elle reviendra sous la forme d'une chaîne, assez narquoise. En PHP 5, vous obtiendrez une erreur fatale, bien plus conforme avec l'incapacité de PHP de restaurer la ressource.

Le problème vient du fait que les sessions stockent les données sous forme de fichier, et doivent transformer les ressources en mémoire en une représentation sous forme de chaîne de caractère. Pour les nombres, les chaînes, les tableaux ou les objets, c'est simple. Mais pour les ressources, cela peut être très difficile, comme pour un objet PDF de PDFlib, ou voire même impossible, comme pour une socket ou une connexion à un serveur SQL.

Cependant, tout espoir n'est pas perdu. En effet, s'il est impossible de sauver la ressource directement en session, il est possible de faire semblant. Pour cela, il suffit de créer deux méthodes magiques dans un objet : __sleep() et __wakeup().

__sleep() sera appelée automatiquement par PHP lors de la serialisation de l'objet. Cela va donner la possibilité au programme de ranger la ressource comme il le faut. Inversement, __wakeup() sera appelée lors de l'ouverture de la session, et donne une chance de rétablir la ressource dans son état initial.

Prenons un exemple. Créons un objet de connexion MySQL : cet exemple est simplifié pour être aussi clair que possible.

<?php
 
class maDB  {
    function __construct() {
        $this->res = mysqli_connect('localhost','user','pass','base');
    }
    
    function __sleep() {
        mysqli_close($this->res);
        return array();
    }
 
    function __wakeup() {
        $this->__construct();
    }
}
 
$maBase = new maDB();
 
$x = serialize($maBase);
 
$y = unserialize($x);
 
>


Notez ici deux choses : nous avons introduit ces fonctions comme étant liées aux sessions. En fait, elles sont techniquement liées à serialize() et unserialize(), qui sont appelées par les sessions. Donc, il est possible d'utiliser ces fonctions magiques hors du contexte des fonctions.

Ensuite, __sleep() doit retourner un tableau contenant la liste des membres de maDB qui doivent être sauvés. Tous les autres seront ignorés. Si __sleep() ne retourne rien, alors l'objet sera entièrement perdu.

Que faire avec __sleep() et __wake() ?
L'exemple ci-dessus montre comment conserver une connexion ouverte entre deux sollications de scripts. Maintenant, il est possible de ranger sa connexion à MySQL dans la session, et ne plus s'en soucier : elle reviendra automatiquement.
Mais voici quelques autres applications de ces fonctions.

Protéger les données de sessions en les chiffrant
<?php
 
class maClasse  {
    function __construct() {
        $this->clair = 'php';
    } 
    
    function __sleep() {
        $this->chiffre = str_rot13($this->clair);
        return array('chiffre');
    }
 
    function __wakeup() {
        $this->clair = str_rot13($this->chiffre);
        unset($this->chiffre);
    }
}
 
$maClasse = new maClasse();
 
print $x = serialize($maClasse);
 
$y = unserialize($x);
 
>

Note : utilisez un algorithme plus robuste que rot13, et un nom de membre un peu moins voyant que chiffré.

Protéger les données de sessions en les signant

<?php
 
class maClasse  {
    function __construct() {
        $this->a = 'b';
        $this->c = 'd';
    } 
    
    function __sleep() {
        $membres = get_object_vars($this);
        $this->signature = md5('S3l'.join('|', array_values($membres)));
        $sauve = array_keys($membres);
        $sauve[] = 'signature';
        return $sauve;
    }
 
    function __wakeup() {
        $membres = get_object_vars($this);
        $signature = $membres['signature'];
        unset($membres['signature']);
 
        $test = md5('S3l'.join('|', array_values($membres)));
        if ($test != $signature) {
            print "Attention, les donn&eacute;es ont &eacute;t&eacute; modifi&eacute;es";
        }
    }
}
 
 
$maClasse = new maClasse();
 
 
 
print $x = serialize($maClasse);
 
 
 
$y = unserialize($x);
 
 
 
??>
gt;


Réduire la taille des données
Utilisez les fonctions de compression intégrées à PHP, comme gzip, bzip2 ou zip, pour réduire la taille des données qui seront stockées sur le disque. Moins de données représente souvent plus de rapidité.

<?php
 
class maClasse  {
    function __construct() {
        $this->a = str_repeat('abc', 1000);
        $this->premiers = array(2,3,5,7,11,13,17,19);
    } 
    
    function __sleep() {
        $membres = get_object_vars($this);
        $this->compresse = bzcompress(serialize($membres));
        return array('compresse');
    }
 
    function __wakeup() {
        $membres = unserialize(bzdecompress($this->compresse));
        unset($this->compresse);
        foreach($membres as $nom => $valeur) {
            $this->$nom = $valeur;
        }
        $this->premiers = array(2,3,5,7,11,13,17,19);
    }
}
 
$maClasse = new maClasse();
 
$x = serialize($maClasse);
$y = unserialize($x);
>

Vous pouvez aussi voir si vous ne pourriez pas recalculer les données d'une autre manière. Par exemple, ci-dessus, nous avons supprimé les nombres premiers qui sont faciles à recalculer, voire même qui apparaissent sous forme de constantes.

A mémoriser
  • Les méthodes qui commencent par __ sont des méthodes magiques de PHP. Elles sont beaucoup plus nombreuses en PHP 5 qu'en PHP 4, mais __sleep() et __wakeup() fonctionnent déjà en PHP 4
  • __sleep() et __wakeup() sont surtout destinées à faire le ménage et préparer les données pour le stockage. Ne vous lancez pas dans de grands chambardements, ce n'est pas l'endroit.
  • ces fonctions sont distinctes du gestionnaire de sessions, mais fonctionnent bien avec lui.
  • N'utilisez serialize() qu'avec des données qui restent sur le serveur. Ne vous en servez pas pour faire passer des structures complexes via le navigateur.
< Précédent   Suivant >

Vous devez vous connecter pour commenter