Les méthodes magiques avec PHP 5
Par PtiPhuS, jeudi 11 mai 2006 à 21:26 :: PHP :: #43 :: rss
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 [facile]
- 2. L'auto-chargement de classes [facile]
- 3. Surcharge de propriétés [intermédiaire]
- 4. Surcharge de méthodes [intermédiaire]
- 5. Sérialisation intelligente [difficile]
- 6. Affichage simplifié d'un objet [facile]
- 7. Clonage intelligent d'un objet [intermédiaire]
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($method, 0, 3);
$suffix = chr(ord(substr($method, 3, 1)) + 32);
$suffix .= substr($method, 4);
$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. | ![]() |


Commentaires
1. Le mercredi 19 juillet 2006 à 20:51, par alil3zawi
2. Le mercredi 26 juillet 2006 à 21:27, par Comité anti crétin
3. Le dimanche 30 juillet 2006 à 20:05, par padaben
4. Le mercredi 16 août 2006 à 13:47, par dunbar
5. Le jeudi 17 août 2006 à 14:28, par Draeli
6. Le vendredi 1 septembre 2006 à 17:08, par PtiPhuS
7. Le jeudi 14 septembre 2006 à 15:00, par DooKie
8. Le jeudi 28 septembre 2006 à 10:17, par epsilon
9. Le dimanche 1 octobre 2006 à 11:58, par Fab
10. Le dimanche 26 novembre 2006 à 00:51, par Invité
11. Le dimanche 3 décembre 2006 à 16:20, par blacky
12. Le jeudi 14 décembre 2006 à 23:25, par Nitro
13. Le lundi 15 janvier 2007 à 11:49, par kakpc
14. Le lundi 22 janvier 2007 à 01:34, par arnolem
15. Le mardi 23 janvier 2007 à 19:09, par coincoin
16. Le vendredi 9 février 2007 à 11:55, par Sana
17. Le dimanche 18 février 2007 à 15:05, par fat
18. Le mardi 20 février 2007 à 16:40, par Bouah
19. Le jeudi 1 mars 2007 à 15:41, par ahmed
20. Le lundi 12 mars 2007 à 15:29, par virtuose
21. Le mardi 15 mai 2007 à 09:56, par Le Cuisinier
22. Le mercredi 20 juin 2007 à 22:45, par Johan Chouquet
23. Le dimanche 5 août 2007 à 11:41, par Evil.dead
24. Le vendredi 31 août 2007 à 17:48, par styvodiabolo
25. Le mercredi 12 septembre 2007 à 05:20, par Kuroro
26. Le samedi 22 septembre 2007 à 10:56, par Philou
27. Le lundi 24 septembre 2007 à 01:31, par Kendyan
28. Le jeudi 27 septembre 2007 à 15:56, par kr3w
29. Le jeudi 18 octobre 2007 à 16:02, par guerrier_du_soleil
30. Le mardi 6 novembre 2007 à 17:11, par joeballa
31. Le mardi 6 novembre 2007 à 17:18, par joeballa
32. Le mercredi 7 novembre 2007 à 11:51, par chichy89
33. Le jeudi 3 janvier 2008 à 20:11, par canton
34. Le mardi 5 février 2008 à 13:31, par bibi
35. Le jeudi 14 février 2008 à 12:42, par x@v
36. Le jeudi 14 février 2008 à 12:43, par x@v
37. Le jeudi 14 février 2008 à 12:46, par x@v
38. Le mercredi 16 avril 2008 à 06:24, par Sebastien
39. Le vendredi 2 mai 2008 à 17:23, par Leon
40. Le dimanche 25 mai 2008 à 16:21, par zniko07
41. Le vendredi 13 juin 2008 à 19:13, par Mouchkar
42. Le lundi 11 août 2008 à 23:50, par gc
43. Le jeudi 4 septembre 2008 à 13:30, par fifix
44. Le samedi 27 septembre 2008 à 13:17, par Nono
45. Le dimanche 12 octobre 2008 à 15:36, par Virtuose
Ajouter un commentaire