Méthodes magiques
  
   Les méthodes magiques sont des méthodes spéciales qui écrasent l'action
   par défaut de PHP quand certaines actions sont réalisées sur un objet.
  
 Attention
  
   Toutes les méthodes commençant par __ sont réservées par PHP.
   Ainsi, il n'est pas recommandé d'utiliser un tel nom de méthode sauf lors
   de l'écrasage du comportement de PHP.
  
  
 
  Les méthodes suivantes sont considérées magiques :
  
  __construct(),
  __destruct(),
  __call(),
  __callStatic(),
  __get(),
  __set(),
  __isset(),
  __unset(),
  __sleep(),
  __wakeup(),
  __serialize(),
  __unserialize(),
  __toString(),
  __invoke(),
  __set_state()
  __clone(), et
  __debugInfo().
 
  
  Avertissement
   
    Toutes les méthodes magiques, à l'exception de
    __construct(),
    __destruct(), et
    __clone(),
    doivent être déclarées en tant que public,
    sinon une E_WARNING est émise.
    Antérieur à PHP 8.0.0, aucun diagnostic n'était émis pour les méthodes magiques
    __sleep(),
    __wakeup(),
    __serialize(),
    __unserialize(), et
    __set_state().
   
   
 Avertissement
  
   Si des déclarations de types sont utilisées dans la définition d'une méthode
   magique, elles doivent être identiques à la signature décrite dans ce document.
   Sinon, une erreur fatale est émise.
   Antérieur à PHP 8.0.0, aucun diagnostic n'était émis.
   Cependant, __construct() et
   __destruct() ne doivent pas déclarer
   un type de retour ; sinon une erreur fatale est émise.
  
  
 
  
   
   
   
   
   
   
    serialize() vérifie si la classe a une méthode avec le
    nom magique __sleep().
    Si c'est le cas, cette méthode sera exécutée avant toute sérialisation. 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 sérialisées.
    Si la méthode ne retourne rien, alors null sera sérialisé, et une alerte de type
    E_NOTICE sera émise.
   
   Note: 
    
     Il n'est pas possible pour __sleep() de retourner
     des noms de propriétés privées des classes parentes. Le faire
     résultera en une erreur de niveau E_NOTICE.
     Utilisez __serialize() à la place.
    
   
   Note: 
    
     À partir de PHP 8.0.0, retourner une valeur qui n'est pas un tableau depuis
     __sleep() émet un avertissement.
     Auparavant une notice était émise.
    
   
   
    Le but avoué de __sleep() est de valider des données en attente
    ou d'effectuer des opérations de nettoyage.
    De plus, cette fonction est utile si un objet très large n'a pas besoin
    d'être sauvegardés dans sa 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 pourrait posséder.
   
   
    Le but avoué de __wakeup() est de rétablir
    toute connexion de base de données qui aurait été perdue
    durant la sérialisation et d'effectuer des tâches de réinitialisation.
   
   
    Exemple #1 Utilisation de sleep() et wakeup()
    
<?php
class Connection
{
    protected $link;
    private $dsn, $username, $password;
    
    public function __construct($dsn, $username, $password)
    {
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->connect();
    }
    private function connect()
    {
        $this->link = new PDO($this->dsn, $this->username, $this->password);
    }
    public function __sleep()
    {
        return array('dsn', 'username', 'password');
    }
    public function __wakeup()
    {
        $this->connect();
    }
}
?>
     
    
   
  
   
   
    public __serialize(): 
array 
   
   
    serialize() vérifie si la classe a une méthode avec le
    nom magique __serialize().
    Si c'est le cas, cette méthode sera exécutée avant toute sérialisation.
    Elle doit construire et retourner un tableau associatif de paire clé/valeur
    qui représente la forme sérialisée de l'objet. Si aucun tableau n'est
    retournée une TypeError sera lancée.
   
   Note: 
    
     Si __serialize() et
     __sleep() sont toutes les deux définies
     dans le même objet, alors seulement __serialize()
     sera appelée.
     __sleep() sera ignorée. Si l'objet
     implémente l'interface Serializable,
     la méthode serialize() de l'interface sera ignorée et
     __serialize() sera utilisée à la place.
    
   
   
    L'utilisation prévue de __serialize()
    est de définir une représentation arbitraire de l'objet pour le sérialiser
    facilement. Les éléments du tableau peuvent correspondre aux propriétés de
    l'objet mais ceci n'est pas requis.
   
   
    inversement, unserialize() vérifie la présence d'une
    fonction avec le nom magique
    __unserialize().
    Si elle est présente, cette fonction recevra le tableau restauré renvoyé
    par __serialize(). Il peut alors
    restaurer les propriétés de l'objet depuis ce tableau comme approprié.
   
   Note: 
    
     Si __unserialize() et
     __wakeup() sont toutes les deux définies
     dans le même objet, alors seulement __unserialize()
     sera appelée.
     __wakeup() sera ignorée.
    
   
   Note: 
    
     Cette fonctionnalité est disponible à partir de PHP 7.4.0.
    
   
   
    Exemple #2 Serialize et unserialize
    
<?php
class Connection
{
    protected $link;
    private $dsn, $username, $password;
    public function __construct($dsn, $username, $password)
    {
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->connect();
    }
    private function connect()
    {
        $this->link = new PDO($this->dsn, $this->username, $this->password);
    }
    public function __serialize(): array
    {
        return [
          'dsn' => $this->dsn,
          'user' => $this->username,
          'pass' => $this->password,
        ];
    }
    public function __unserialize(array $data): void
    {
        $this->dsn = $data['dsn'];
        $this->username = $data['user'];
        $this->password = $data['pass'];
        $this->connect();
    }
}?>
     
    
   
  
   
   
   
   
    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.
   
   Avertissement
    
     Un objet Stringable
     ne sera pas accepté par une déclaration de type string si la
     déclaration de type strict est activée.
     Si un tel comportement est souhaité, la déclaration de type doit accepter
     à la fois Stringable et string via un type union.
    
    
     À partir de PHP 8.0.0, la valeur de retour suit les sémantiques standard
     de PHP, signifiant que la valeur sera convertie en une string
     si possible et si le
     typage stricte
     est désactivé.
    
    
     À partir de PHP 8.0.0, toute classe qui contient une méthode
     __toString() implémente aussi
     implicitement l'interface Stringable,
     et passera donc les vérifications de types pour cette interface.
     Implémenter quand même explicitement l'interface est recommandé.
    
    
     En PHP 7.4, la valeur de retour doit être une
     string, sinon une Error est lancée.
    
    
     Antérieur à PHP 7.4.0, la valeur de retour doit
     être une string, sinon une E_RECOVERABLE_ERROR
     fatale est émise.
    
    
   Avertissement
    
     Il était impossible de lancer une exception depuis la méthode
     __toString() antérieur à PHP 7.4.0.
     Cela entraînera une erreur fatale.
    
    
   
    Exemple #3 Exemple simple
    
<?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;
?>
     
    L'exemple ci-dessus va afficher :
 
   
  
   
   
    __invoke(
 ...$values): 
mixed 
   
   
    La méthode __invoke() est appelée lorsqu'un script tente
    d'appeler un objet comme une fonction.
   
   
    Exemple #4 Exemple avec __invoke()
    
<?php
class CallableClass
{
    public function __invoke($x)
    {
        var_dump($x);
    }
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>
     
    L'exemple ci-dessus va afficher :
 
   
    Exemple #5 Exemple avec __invoke()
    
<?php
class Sort
{
    private $key;
    public function __construct(string $key)
    {
        $this->key = $key;
    }
    public function __invoke(array $a, array $b): int
    {
        return $a[$this->key] <=> $b[$this->key];
    }
}
$customers = [
    ['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'],
    ['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'],
    ['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']
];
// trier les clients par prénom
usort($customers, new Sort('first_name'));
print_r($customers);
// trier les clients par nom de famille
usort($customers, new Sort('last_name'));
print_r($customers);
?>
     
    L'exemple ci-dessus va afficher :
     
Array
(
    [0] => Array
        (
            [id] => 3
            [first_name] => Alice
            [last_name] => Gustav
        )
    [1] => Array
        (
            [id] => 2
            [first_name] => Bob
            [last_name] => Filipe
        )
    [2] => Array
        (
            [id] => 1
            [first_name] => John
            [last_name] => Do
        )
)
Array
(
    [0] => Array
        (
            [id] => 1
            [first_name] => John
            [last_name] => Do
        )
    [1] => Array
        (
            [id] => 2
            [first_name] => Bob
            [last_name] => Filipe
        )
    [2] => Array
        (
            [id] => 3
            [first_name] => Alice
            [last_name] => Gustav
        )
)
 
    
   
  
   
   
   
   
    Cette méthode statique est appelée
    pour les classes exportées par la fonction var_export().
   
   
    Le seul paramètre de cette méthode est un tableau contenant les propriétés
    exportées sous la forme ['property' => value, ...].
   
   
    Exemple #6 Utilisation de __set_state()
    
class A
{
    public $var1;
    public $var2;
    public static function __set_state($an_array)
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}
$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
$b = var_export($a, true);
var_dump($b);
eval('$c = ' . $b . ';');
var_dump($c);
?>
     
    L'exemple ci-dessus va afficher :
string(60) "A::__set_state(array(
   'var1' => 5,
   'var2' => 'foo',
))"
object(A)#2 (2) {
  ["var1"]=>
  int(5)
  ["var2"]=>
  string(3) "foo"
}
 
    
   Note: 
    
     Lors de l'exportation d'un objet, var_export() ne 
     vérifie pas si __set_state() est 
     implémentée par la classe de l'objet, ainsi la réimportation d'objets
     résultera en une exception Error,
     si __set_state() n'est pas implémentée.
     En particulier, cela affecte certaines classes internes.
    
    
     Il est de la responsabilité du programmeur de vérifier que seuls les objets dont la classe implémente __set_state() seront ré-importés.
    
   
   
  
   
   
   
    Cette méthode est appelée par var_dump() lors
    du traitement d'un objet pour récupérer les propriétés qui
    doivent être affichées. Si la méthode n'est pas définie dans un objet,
    alors toutes les propriétés publiques, protégées et privées seront
    affichées.
   
   
    Exemple #7 Utilisation de __debugInfo()
    
<?php
class C {
    private $prop;
    public function __construct($val) {
        $this->prop = $val;
    }
    public function __debugInfo() {
        return [
            'propSquared' => $this->prop ** 2,
        ];
    }
}
var_dump(new C(42));
?>
     
    L'exemple ci-dessus va afficher :
object(C)#1 (1) {
  ["propSquared"]=>
  int(1764)
}