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.

__sleep() et __wakeup()

public__sleep(): array
public__wakeup(): void

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();
}
}
?>

__serialize() et __unserialize()

public__serialize(): array
public__unserialize(array$data): void

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();
}
}
?>

__toString()

public__toString(): string

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 Stringablene 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 :

 Bonjour 

__invoke()

__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 :

 int(5) bool(true) 

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

__set_state()

static__set_state(array$properties): object

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.

__debugInfo()

__debugInfo(): array

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