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)
}