Grâce à PHP 5, la manipulation des objets est plus souple et plus complète. Les méthodes magiques sont des outils très pratiques pour simplifier le code et automatiser certaines tâches. Vous pouvez grâce à elles faire du PHP de haut niveau en quelques clics !

1. Constructeur et Destructeur

Une classe est un code PHP qui sert à créer des objets. Par exemple, la classe "User" va servir à créer des objets "Guillaume", "Benoit", etc. Ces objets ont une naissance (grace à l'appel du mot clé "new") et une mort (lorsque la variable qui le contient n'existe plus ou à la fin du script). 

Le constructeur __construct() et le destructeur __destruct() sont les deux méthodes magiques les plus connues. Ce sont des méthodes (fonctions dans la classe) qui sont appelées automatiquement quand on crée un objet et quand on le "tue". 

Voici un exemple que vous pouvez tester pour comprendre le rôle du constructeur et du destructeur. 

Exemple

 class MyClass {
  
  
// Constructeur de la classe
  
public function __construct() {
    echo 
"Appel du constructeur !\n";
  }

  
// Destructeur de la classe
  
public function __destruct() {
    echo 
"Appel du destructeur !\n";
  }

}

echo 
"<pre>";

// La création d'un objet appelle le constructeur. 
echo "Création d'un objet\n";
$myClass = new MyClass();

// La destruction de l'objet est implicite à la fin du script
echo "Fin du script\n";

Cet exemple affiche le résultat suivant dans le navigateur

 Création dun objet
Appel du constructeur 
!
Fin du script
Appel du destructeur 
!

Mais à quoi cela peut servir ? Eh bien à plein de chose ! Généralement, le constructeur sert à initialiser les valeurs de l'objet et le destructeur à effectuer un nettoyage. Le constructeur peut aussi ouvrir un accès à une base de données et le destructeur fermer cet accès, etc. 

Par exemple, dans une classe "Config" qui sert à gérer un fichier de configuration, le constructeur peut lire le fichier de configuration et le destructeur le sauvegarder une fois que des modifications ont été faites. 

Exemple avec une classe "Config"

 class Config {
  
  private 
$currentPath;
  private 
$ini = array();
  private 
$file '';
  
  
// Connexion au fichier ini à l'instanciation 
  // de l'objet.
  
public function __construct($file) {
    
$this->currentPath dirname(__FILE__).'/';
    if (
file_exists($file)) {
      
$this->ini parse_ini_file($file);
    } 
    
$this->file $file;
  }
  
  
// Sauvegarde du fichier de configuration à la 
  // destruction de l'objet. 
  
public function __destruct() {
    
$this->save();
  }

  
// Méthode permettant d'ajouter une valeur
  // de configuration. 
  
public function addValue($key$value) {
    
$this->ini[trim($key)] = (string) $value;
  }
  
  
// Méthode permettant de lire une valeur de
  // Configuration. 
  
public function getValue($key) {
    return 
$this->ini[$key];
  }

  
// Enregistrement du fichier de configuration
  
public function save($file null) {
    
$iniContent '';
    foreach (
$this->ini as $key => $value) {
      
$iniContent .= $key.' = "'.$value."\"\n";
    }
    if (
$file === null) {
      
$file $this->file;
    }
    
file_put_contents($this->currentPath.$file$iniContent);
  }
}

2. L'auto-chargement de classes

Plutôt que de s’encombrer avec de multiples includes, vous pouvez maintenant déclarer la fonction __autoload() qui s’occupera de charger la classe voulue si celle-ci n’existe pas. 

Voici le contenu d'un fichier ClassTest.php

 class ClassTest {
  public function 
display($txt) {
    echo 
$txt."<br />\n";
  }
}

Voici le contenu du fichier autoload.php

 // Cette fonction peut être déclarée dans un 
// fichier include commun. 
function __autoload($class_name) {
  require_once 
$class_name.'.php';
}

Maintenant, dans chaque fichier que l'on utilise on peut écrire

 // Inclusion du fichier autoload.php
include_once 'autoload.php';

// Test de notre classe
$obj  = new ClassTest();
$obj->display('Hello !');

3. Surcharge des propriétés

Les propriétés d'un objet sont des "variables de classe". Si une propriété est appelée mais n'existe pas, on peut "attraper" l'appel de cette propriété manquante avec une méthode magique. 

Cette surcharge est définie par les méthodes spéciales __set() et __get(). Lors de l’appel d’une propriété qui n’existe pas, __set() est automatiquement sollicitée s’il s’agit d’une affectation et __get() s’il s’agit de demander une valeur. 

Vous pouvez ainsi simuler des manipulations de propriétés en contrôlant leur accès. L’exemple suivant met en œuvre une classe qui fait appel à un fichier de configuration au format .ini. 

Exemple d'utilisation de __set() et de __get()

 class IniConfig {
    
    private 
$iniValues = array();
    private 
$iniFile;

    
// Chargement du fichier de configuration dans le 
    // contexte de la classe. 
    
public function __construct($ini_file) {
        
$this->iniFile $ini_file;
        if (
file_exists($ini_file)) {
            
$this->iniValues parse_ini_file($ini_file);
        }
    }
    
    
// Met à jour le fichier .ini à chaque modification 
    // dans iniValues. 
    
private function writeConfig() {
        
$config ';; Last update : '.date('d.m.Y H:i:s');
        foreach (
$this->iniValues AS $key => $value) {
            
$config .= "n$key = "$value"";
        }
        
file_put_contents($this->iniFile$config);
    }
    
    
// Modification d'une valeur de configuration. 
    
public function __set($key$value) {
        
$this->iniValues[$key] = $value;
        
$this->writeConfig();
    }
    
    
// Lecture d'une valeur de modification. Cette méthode
    // peut être complétée par un chargement de la
    // configuration à chaque lecture au besoin. 
    
public function __get($key) {
        return 
$this->iniValues[$key];
    }
    
}

$config = new IniConfig('test.ini');
$config->firstname "Guillaume";
$config->lastname "Ponçon";
echo 
$config->firstname.' '.$config->lastname;

L’exécution de ce code renvoie la chaîne « Guillaume Ponçon ». Le contenu du fichier test.ini créé est le suivant après l'exécution de ce script : 

 ;; Last update 11.08.2005 19:01:44
firstname 
"Guillaume"
lastname "Ponçon"

4. Surcharge de méthodes

Les méthodes d'un objet sont les "fonctions contenues dans la classe". Lors d'un appel à une méthode qui n'existe pas, il est possible "d'attraper" cet appel avec une méthode spécial __call(). 

L'exemple suivant permet d'accéder aux propriétés d'un objet en simulant ses accesseurs, c'est à dire les méthodes utilisées pour écrire et lire les variables de classe. 

Note : cette classe est abstraite car elle n'est pas utilisable en l'état mais sert à compléter d'autres classes, comme nous le verrons dans le deuxième exemple ci-dessous. 

La classe Entity qui simule les accesseurs

 abstract class Entity {
    
    
// Interception des accesseurs. 
    
public function __call($method$attrs) {
        
$prefix  substr($method03);
        
$suffix  chr(ord(substr($method31)) + 32);
        
$suffix .= substr($method4);
        
$cattrs  count($attrs);
        if (
property_exists($this$suffix)) {
            if (
$prefix == 'set' && $cattrs == 1) {
                return 
$this->set($suffix$attrs[0]);
            }
            if (
$prefix == 'get' && $cattrs == 0) {
                return 
$this->get($suffix);
            }
        }
        
trigger_error("La méthode $method n'existe pas.");
    }
}

Une classe avec des propriétés que l'on veut manipuler avec des accesseurs

 require 'Entity.php';

class 
VidCam extends Entity {

    
// Mes propriétés. 
    
protected $name;
    protected 
$description;
    protected 
$shutterSpeed;
    protected 
$irisRange;

}

Pour accéder aux propriétés de la classe VidCam, il suffit de faire comme ceci

 // Création d'un objet
$vidCam = new VidCam();

// Affectation de propriétés
$vidCam->setName('HVX-200');
$vidCam->setShutterSpeed(50);

// Lecture de propriétés
echo 'Nom : '.$vidCam->getName()."<br />\n";
echo 
'Vitesse d'obt. : .$vidCam->getShutterSpeed();

5. Sérialisation intelligente

La sérialisation consiste à transformer le contenu d'une variable complexe (tableau, objet) en une chaîne de caractères que l'on peut facilement envoyer, par http ou par ftp par exemple. L'opération de désérialisation consiste à faire l'inverse : retrouver le contenu de notre variable (notre tableau ou objet) à partir d'une chaîne de caractère qui a été construite par sérialisation.

Notre exemple consiste à utiliser les méthodes magiques __sleep et __wakeup pour déclancher des actions lors de la sérialisation et de la désérialisation. Nous utilisons notre classe Config vue au début de ce tutorial. 

La classe Config ci-dessous sert à gérer un fichier de configuration au format .ini

 class Config {
  
  private 
$currentPath;
  private 
$ini = array();
  private 
$file '';
  public 
$dataToSend = array();
  
  
// Connexion au fichier ini à l'instanciation 
  // de l'objet.
  
public function __construct($file) {
    
$this->currentPath dirname(__FILE__).'/';
    if (
file_exists($file)) {
      
$this->ini parse_ini_file($file);
    } 
    
$this->file $file;
  }
  
  
// Sauvegarde du fichier de configuration à la 
  // destruction de l'objet. 
  
public function __destruct() {
    
$this->save();
  }

  
// Méthode permettant d'ajouter une valeur
  // de configuration. 
  
public function addValue($key$value) {
    
$this->ini[trim($key)] = (string) $value;
  }
  
  
// Méthode permettant de lire une valeur de
  // Configuration. 
  
public function getValue($key) {
    return 
$this->ini[$key];
  }

  
// Enregistrement du fichier de configuration
  
public function save($file null) {
    
$iniContent '';
    foreach (
$this->ini as $key => $value) {
      
$iniContent .= $key.' = "'.$value."\"\n";
    }
    if (
$file === null) {
      
$file $this->file;
    }
    
file_put_contents($this->currentPath.$file$iniContent);
  }
  
  
// 1. Sauvegarde des données de configuration
  // 2. Préparation des données à transmettre dans 
  //    l'objet sérialisé. 
  // 3. Sélection de la propriété à transmettre dans
  //    l'objet sérialisé. 
  
public function __sleep() {
    
$this->save($this->file.'.bak');
    
$this->dataToSend['ini']  =& $this->ini;
    
$this->dataToSend['file'] =& $this->file;
    return array(
'dataToSend');
  }
  
  
// Régénération du fichier de configuration sur 
  // l'environnement cible. Cette méthode crée à la 
  // désérialisation un nouveau fichier de configuration
  // sur l'environnement cible à partir des données contenues
  // dans l'objet sérialisé. Ce nouveau fichier de configuration
  // contient les mêmes valeurs que celui qui a été sauvé dans
  // l'environnement source. 
  
public function __wakeup() {
    
$this->currentPath dirname(__FILE__).'/';
    
$this->file =& $this->dataToSend['file'];
    
$this->ini  =& $this->dataToSend['ini'];
    
$this->dataToSend = array();
    
$this->save();
  }
}

Comme indiqué dans le source, __sleep va préparer les données avant la sérialisation et __wakeup va restaurer les données après la désérialisation. Pour utiliser cet objet, on peut créer un script "source.php" qui créera un objet de configuration, puis un script "target.php" qui va récupérer l'objet de configuration de "source.php" pour l'utiliser à son tour sur un autre ordinateur. 

Le fichier source.php qui créée un objet de configuration

 include 'Config.php';
$config = new Config('config.ini');
$config->addValue('host''http://www.google.fr');
$config->addValue('title''Best practices PHP 5');
echo 
serialize($config);

Le fichier target.php qui récupère l'objet créé par source à travers le réseau

 // La classe Config qui sert à construire l'objet doit être
// accessible dans l'environnement cible, sinon l'objet sera
// incomplet et le wakeup ne fonctionnera pas. 
include 'Config.php';

// Récupération de notre objet sérialisé. 
$serialized_config file_get_contents('http://localhost/workspace/code/source.php');

// Désérialisation. 
$config unserialize($serialized_config);

// Vérification du contenu de l'objet. 
var_dump($config);

6. Affichage simplifié d'un objet

La méthode magique __toString() est simple à utiliser et permet d'afficher un objet en faisant juste un echo d'une variable qui contient cet objet. 

Voici un objet avec une méthode __toString() qui affiche son contenu

 class User {

  private 
$firstName "Guillaume";
  private 
$lastName  "Ponçon";

  public function 
__toString() {
    return 
"Name : $this->firstName $this->lastName \n";
  }
}

Voici tout simplement comment faire appel à __toString()

 // renvoit "Name : Guillaume Ponçon"
$user = new User();
echo 
$user;

7. Clonage intelligent d'un objet

Le clonage d'un objet consiste tout simplement à le dupliquer. En PHP 4, pour dupliquer un objet, le simple opérateur d'affectation "=" suffisait. En PHP 5, il faut utiliser le mot clé "clone" car tout est passé par référence. 

Clonage en PHP 5

 // Création d'un objet
$user1 = new User();

// Les variables $user1 et $user2 font référence au même objet.
// (passage par référence, implicite dans PHP 5)
$user2 $user1;

// Les variables $user1 et $user2 contiennent des objets différents.
// (clonage grace au mot clé 'clone')
$user3 = clone $user1;

Il est possible d'intercepter une opération de clonage, par exemple pour incrémenter un compteur, grace à la méthode magique __clone(). Nous pouvons par exemple mettre en place une classe utilisateur qui incrémente l'identifiant d'un objet utilisateur lorsque celui-ci est cloné. 

Classe User qui incrémente un identifiant au clonage

 class User {

  private 
$id 1;
  public 
$name "Rasmus";

  public function 
getId() {
    return 
$this->id;
  }
  
  public function 
__clone() {
    
$this->id++;
  }

}

$user1 = new User();
$user2 = clone $user1$user2->name "Zeev";
$user3 = clone $user2$user3->name "Andi";
$user4 = clone $user3$user4->name "Wez";

for (
$i 1$i <= 4$i++) {
  echo ${
'user'.$i}->getId().'. '.${'user'.$i}->name."\n";
}

Le résultat de ce script affiche ceci au navigateur

 1. Rasmus
2. Zeev
3. Andi
4. Wez
Ce tutorial est inspiré de l'ouvrage français Best practices PHP 5 - Editions Eyrolles - par Guillaume Ponçon, responsable des formations PHP chez Anaska Formation.