Les méthodes magiques avec PHP 5

PHP cours tutorial

Les méthodes magiques


Le principe

Une méthode magique est une méthode événementielle c'est-à-dire qu’elle sera exécutée si un événement est déclenché, si non rien ne se passe.

Nous avons déjà vu une méthode magique dans un chapitre précédent, il s’agit de la méthode

__construct et oui c’est une méthode magique qui est déclanchée pas l’événement instanciation d’une classe (création d’objet).

Si vous voyez une fonction qui commence par un double souligné ( __ ) et bien c’est une méthode magique.

Attention

PHP réserve tous les noms de fonctions commençant par un double souligné __ pour les méthodes magiques.

Il est recommandé de ne pas utiliser de noms de fonctions commençant par __ sauf si vous voulez des fonctionnalités magiques documentées.

Les méthodes magiques


Les méthodes  magiques documentées de PHP sont :

 
__construct         __destruct
__call     __callStatic
__get     __set
__isset     __unset
__sleep     __wakeup
__toString     __invoke
__set_state            

Vous ne pouvez pas utiliser ces noms de méthode dans vos classes, sauf si vous voulez implémenter le comportement associé à ces méthodes magiques.

Constructeur et Destructeur

Le constructeur __construct() :

Cette méthode est déclenchée quand un objet est né par l’appel du mot clé « new », c'est-à-dire à sa création.

Le destructeur __destruct() :

Cette méthode est déclenchée quand l’objet est mort c'est-à-dire lorsque la variable qui le contient n'existe plus ou à la fin du script

Le constructeur et le destructeur  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 :

Sélectionner le code

  <?php
  class MaClass {
     // 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";
   $MaClass = new MaClass();
   // La destruction de l'objet est implicite à la fin du script
   echo "Fin du script\n";
   echo "</pre>";
   ?>

 

 Surcharge des attributs (propriétés)

 Si un attribut est appelé mais n'existe pas ou on n’a pas l’autorisation d’accès à cet attribut, on appelle cet événement la surcharge d’attribut.

On peut "attraper" l'appel de cet attribut manquant avec une méthode magique. 

Les méthodes magiques spéciales __set() et __get()  seront déclanchées par cette surcharge.

Lors de l’appel d’un attribut qui n’existe pas ou dont on n’a pas l’autorisation d’accès :

 __set() est automatiquement sollicitée s’il s’agit d’une affectation de valeur

et __get() s’il s’agit de demander une valeur. 

La méthode __set()

Cette méthode prend deux paramètres : le premier est le nom de l'attribut auquel on a tenté d'assigner une valeur, le second paramètre est la valeur que l'on a tenté d'assigner à l'attribut. Cette méthode ne retourne rien. Vous pouvez  faire ce que vous voulez.

Exemple :

Sélectionner le code

  <?php
      class MaClasse
      {
          private $AttributPrive;
          public function __set ($nom, $valeur)
          {
              echo "Pas possible d'assigner à l'attribut <strong>", $nom,
  "</strong> la valeur <strong>", $valeur, "</strong>!<br />";
          }
      }
     
      $obj = new MaClasse;
     
      $obj->attribut = 'Test attribut inéxistant';
      $obj->AttributPrive = 'Test attribut privé';
  ?>

 

La méthode __get()

Le principe est le même que __set()

Cette méthode prend un paramètre : le nom de l'attribut auquel on a essayé d'accéder

Exemple

Sélectionner le code

  <?php
      class MaClasse
      {
          private $AttributPrive;
          public function __get ($nom)
          {
              return "Impossible d'accéder à l'attribut <strong>" . $nom .
  "!</strong><br />";
          }
      }
     
      $obj = new MaClasse;
     
      echo $obj->attribut;
      echo $obj->AttributPrive;
  ?>

 

Les méthode __isset() et __unset()


La méthode  __isset ( $name ) est sollicitée lorsque la fonction isset() ou la fonction empty() sont appelées avec des propriétés inaccessibles.

La méthode  __unset( $name) est sollicitée lorsque unset() est appelée avec des propriétés inaccessibles.

L’argument $name est le nom de l'attribut que l'on a envoyé à la fonction isset().

Exemple

Sélectionner le code

<?php
class PropertyTest {
    /**  Variable pour les données surchargées.  */
    private $data = array();
    /**  La surcharge n'est pas utilisée sur les propriétés déclarées.  */
    public $declared = 1;
    /**  La surcharge n'est lancée que lorsque l'on accède à la classe depuis
l'extérieur.  */
    private $hidden = 2;
    public function __set($name, $value) {
        echo "Définition de '$name' à la valeur '$value'\n";
        $this->data[$name] = $value;
    }
    public function __get($name) {
        echo "Récupération de '$name'\n";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
        $trace = debug_backtrace();
        trigger_error(
            'Propriété non-définie via __get(): ' . $name .
            ' dans ' . $trace[0]['file'] .
            ' à la ligne ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }
    /**  Depuis PHP 5.1.0  */
    public function __isset($name) {
        echo "Est-ce que '$name' est défini ?\n";
        return isset($this->data[$name]);
    }
    /**  Depuis PHP 5.1.0  */
    public function __unset($name) {
        echo "Effacement de '$name'\n";
        unset($this->data[$name]);
    }
    /**  Cette méthode n'est pas magique, nécessaire ici que pour l'exemple.  */
    public function getHidden() {
        return $this->hidden;
    }
}
echo "<pre>\n";
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
echo "Manipulons maintenant la propriété privée nommée 'hidden':\n";
echo "'hidden' est visible depuis la classe, donc __get() n'est pas utilisée...\n";
echo $obj->getHidden() . "\n";
echo "'hidden' n'est pas visible en dehors de la classe, donc __get() est
utlisée...\n";
echo $obj->hidden . "\n";
echo "</pre>";
?>

 

Cet exemple va afficher :

Définition de 'a' à la valeur '1'
Récupération de 'a'
1

Est-ce que 'a' est défini ?
bool(true)
Effacement de 'a'
Est-ce que 'a' est défini ?
bool(false)

1

Manipulons maintenant la propriété privée nommée 'hidden':
'hidden' est visible depuis la classe, donc __get() n'est pas utilisée...
2
'hidden' n'est pas visible en dehors de la classe, donc __get() est utlisée...
Récupération de 'hidden'


Notice: Propriété non-définie via __get(): hidden dans <chemin du fichier> à la ligne 69 in <chemin du fichier> on line 28

Surcharge de méthodes


Si des méthodes inaccessibles  sont invoqué, c'est-à-dire qu’on fait appel à une méthode qui n'existe pas ou dont on n’a pas l’autorisation d’accès, on parle de surcharge de méthode.

On peut capter cette événement par les méthodes magiques __call() et  __callStatic() .

__call( $name , array $arguments ) est lancée lorsque l'on invoque des méthodes inaccessibles dans le contexte de l'objet.

__callStatic( $name , array $arguments ) est lancée lorsque l'on invoque des méthodes inaccessibles dans un contexte statique.

la méthode __callStatic n'est disponible que depuis PHP 5.3 !

L'argument $name est le nom de la méthode appelée.

L'argument $arguments est un tableau contenant les paramètres passés à la méthode $name.

Exemple :  

Sélectionner le code

  <?php
      class MaClasse
      {
          public function __call ($nom, $arguments)
          {
              echo 'Impossible d\'appeler la méthode <strong>', $nom,
  '</strong>, \'elle n\'existe pas ! les arguments qui lui étaient passés sont
  les suivants : <strong>', implode ($arguments, '</strong>,
  <strong>'), '</strong>';
          }
         public static function __callStatic ($nom, $arguments)
          {
              echo 'Impossible d\'appeler la méthode <strong>', $nom,
  '</strong> elle est appelée dans un contexte statique alors qu\'elle n\'existe
  pas ! les arguments qui lui étaient passés sont les suivants : <strong>',
  implode ($arguments, '</strong>, <strong>'), '</strong><br
  />';
          }
      }
     
      $obj = new MaClasse;
     
      $obj->methode ('arg1', 'arg2');
             echo '<br />';
             MaClasse::methodeStatique ("param1", "param2");
  ?>

 

Methodes magiques __sleep et __wakeup


Quand on utilise la fonction serialize(), elle vérifie si votre classe a la méthode  magique __sleep. Si c'est le cas, cette méthode sera exécutée avant toute linéarisation.

Elle peut nettoyer l'objet et elle est supposée retourner un tableau avec les noms de toutes les variables de l'objet qui doivent être linéarisées. Si la méthode ne retourne rien, alors NULL est linéarisé et une alerte de type E_NOTICE est émise.

Note:

Il n'est pas possible pour __sleep de retourner les noms des propriétés privées des classes parentes. Le faire résulte en une erreur de niveau E_NOTICE. À la place, vous devriez utiliser l'interface Serializable.

Le but avoué de __sleep est de valider des données en attente ou d'effectuer les opérations de nettoyage. De plus, cette fonction est utile si vous avez de très gros objets qui n'ont pas besoin d'être sauvegardés en totalité.

Réciproquement, la fonction unserialize() vérifie la présence d'une méthode dont le nom est le nom magique __wakeup. Si elle est présente, cette fonction peut reconstruire toute ressource que l'objet possède.

Le but avoué de __wakeup est de rétablir toute connexion de base de données qui aurait été perdue durant la linéarisation et d'effectuer des tâches de réinitialisation.

Exemple : Utilisation de sleep() et wakeup()

Sélectionner le code

<?php 
class Connection 
{ 
    protected $link; 
    private $server, $username, $password, $db; 

    public function __construct($server, $username, $password, $db) 

    { 
        $this->server = $server; 
        $this->username = $username; 
        $this->password = $password; 
        $this->db = $db; 
        $this->connect(); 
    } 

    private function connect() 
    { 
        $this->link = mysql_connect($this->server, $this->username, $this->password); 

        mysql_select_db($this->db, $this->link); 
    } 

    public function __sleep() 
    { 
        return array('server', 'username', 'password', 'db'); 
    } 

    public function __wakeup() 
    { 
        $this->connect(); 
    } 
} 
?> 

 

La methode magique __toString


La méthode __toString détermine comment l'objet doit réagir lorsqu'il est traité comme une chaîne de caractères. Par exemple, ce que echo $obj; affichera.

L’événement donc qui déclanche cette méthode est le fait de traiter un objet comme chaîne de caractères

 Cette méthode doit retourner une chaine sinon une erreur E_RECOVERABLE_ERROR sera levée.

Exemple :

Sélectionner le code

  <?php
  // Déclaration d'une classe simple
  class ClasseTest
  {
      public $foo;
      public function __construct($foo)
      {
          $this->foo = $foo;
      }
      public function __toString()
      {
          return $this->foo;
      }
  }
  $class = new ClasseTest('Bonjour');
  echo $class;
  ?>

 

Que s’est il passé ?

En passant l’objet $class comme paramètre de la fonction echo, revient à traiter l’objet comme chaîne de caractères

Et sans la méthode magique __toString   une erreur fatal sera émise car la fonction echo ne sait pas comme

réagir , et c’est  la méthode __toString  détermine comment l'objet doit réagir lorsqu'il est traité comme une chaîne de caractères.

Note :

Il est important de noter qu'avant PHP 5.2.0, la méthode __toString n'était appelée que si elle était directement combinée avec echo() ou print().

Depuis PHP 5.2.0, la méthode __toString est appelée dans tous les contextes de chaîne de caractères (e.g. dans printf() avec le modificateur %s) mais pas dans les autres types de contextes (e.g. avec le modificateur %d). Depuis PHP 5.2.0, convertir un objet sans la méthode __toString en chaîne de caractères émettra une E_RECOVERABLE_ERROR.

La méthode magique __invoke()


La méthode __invoke() est appelée lorsque le script tente d'appeler un objet comme une fonction.

Note:

Cette fonctionnalité est disponible depuis PHP 5.3.0.

Exemple avec __invoke()

Sélectionner le code

  <?php
  class CallableClass
  {
      public function __invoke($x)
      {
          var_dump($x);
      }
  }
  $obj = new CallableClass;
  $obj(5);
  var_dump(is_callable($obj));
  ?>

 

La méthode magique __set_state()


Cette méthode statique est appelée pour les classes exportées par la fonction var_export() depuis PHP 5.1.0.

Le seul paramètre de cette méthode est un tableau contenant les propriétés exportées sous la forme array ( 'propriété' => valeur, ...).

Exemple  de __set_state (depuis PHP 5.1.0)

Sélectionner le code

  <?php
  class A
  {
      public $var1;
      public $var2;
      public static function __set_state($an_array) // Depuis PHP 5.1.0
      {
          $obj = new A;
          $obj->var1 = $an_array['var1'];
          $obj->var2 = $an_array['var2'];
          return $obj;
      }
  }
  $a = new A;
  $a->var1 = 5;
  $a->var2 = 'foo';
  eval('$b = ' . var_export($a, true) . ';'); 
  // $b = A::__set_state(array(
 //    'var1' => 5,
  //    'var2' => 'foo',
 // ));
  var_dump($b);
  ?>

 

Ce qui affiche :

object(A)[2]

public 'var1' => int 5
public 'var2' => string 'foo' (length=3)


Par carabde 20 Aout 2014