Constructeurs et destructeurs

Constructeur

__construct(mixed ...$values = ""): void

PHP permet aux développeurs de déclarer des constructeurs pour les classes. Les classes qui possèdent une méthode constructeur appellent cette méthode à chaque création d'une nouvelle instance de l'objet, ce qui est intéressant pour toutes les initialisations dont l'objet a besoin avant d'être utilisé.

Note: Les constructeurs parents ne sont pas appelés implicitement si la classe enfant définit un constructeur. Pour utiliser un constructeur parent, il est nécessaire de faire appel à parent::__construct() depuis le constructeur enfant. Si l'enfant ne définit pas un constructeur alors, il peut être hérité de la classe parente, exactement de la même façon qu'une méthode le serait (si elle n'a pas été déclarée comme privée).

Exemple #1 Constructeur lors de l'héritage

<?php
class BaseClass {
function
__construct() {
print
"Dans le constructeur de BaseClass\n";
}
}

class
SubClass extends BaseClass {
function
__construct() {
parent::__construct();
print
"Dans le constructeur de SubClass\n";
}
}

class
OtherSubClass extends BaseClass {
// Constructeur hérité de BaseClass
}

// Dans le constructeur de BaseClass
$obj = new BaseClass();

// Dans le constructeur de BaseClass
// Dans le constructeur de SubClass
$obj = new SubClass();

// Dans le constructeur de BaseClass
$obj = new OtherSubClass();
?>

À la différence des autres méthodes, __construct() est exclue des règles de compatibilités des signatures usuelles quand elle est étendue.

Les constructeurs sont des méthodes ordinaires qui sont appelées lors de l'instanciation de leur objet correspondant. Par conséquent, ils peuvent définir un nombre arbitraire d'arguments, qui peuvent être requis, avoir un type, et peuvent avoir une valeur par défaut. Les arguments d'un constructeur sont appelés en plaçant les arguments dans des parenthèses après le nom de la classe.

Exemple #2 Utiliser les arguments d'un constructeur

<?php
class Point {
protected
int $x;
protected
int $y;

public function
__construct(int $x, int $y = 0) {
$this->x = $x;
$this->y = $y;
}
}

// Passe les deux paramètres.
$p1 = new Point(4, 5);
// Passe uniquement le paramètre requis. $y prendra sa valeur par défaut de 0.
$p2 = new Point(4);
// Avec les arguments nommés (à partir de PHP 8.0) :
$p3 = new Point(y: 5, x: 4);
?>

Si une classe n'a pas de constructeur, ou que le constructeur n'a pas d'arguments requis, les parenthèses peuvent être omises.

Style ancien des constructeurs

Avant PHP 8.0.0, les classes dans le nom d'espace global interpréteront une méthode qui a le même nom que la classe comme un constructeur de style ancien. Cette syntaxe est obsolète, et résultera en une erreur E_DEPRECATED mais appellera quand même cette fonction comme un constructeur. Si __construct() et une méthode du même nom sont définies, __construct() sera appelé.

Les classes dans les espaces de nom, ou toute classe à partir de PHP 8.0.0, une méthode du même nom que la classe n'a jamais de signification particulière.

Toujours utiliser __construct() dans du nouveau code.

Promotion du Constructeur

À partir de PHP 8.0.0, les paramètres du constructeur peuvent être promus pour correspondre à une propriété de l'objet. Il est très courant pour les paramètres d'un constructeur d'être assignés à une propriété sans effectuer d'opérations dessus. La promotion du constructeur fournit un raccourci pour ce cas d'utilisation. L'exemple ci-dessus peut être réécrit de la façon suivante.

Exemple #3 Utilisant la promotion de propriété de constructeur

<?php
class Point {
public function
__construct(protected int $x, protected int $y = 0) {
}
}

Quand un argument de constructeur inclut un modificateur, PHP l'interprétera comme une propriété d'objet et un argument du constructeur, et assigne la valeur de l'argument à la propriété. Le corps du constructeur peut être alors vide ou peut contenir d'autres déclarations. Toutes les déclarations additionnelles seront exécutées après que la valeur de l'argument a été assignée à sa propriété correspondante.

Tous les arguments ne doivent pas être promus. Il est possible de mélanger et assortir les arguments promus et non-promus, dans n'importe quel ordre. Les arguments promus n'ont aucun impact sur du code appelant le constructeur.

Note:

Utiliser un modificateur de visibilité (public, protected ou private) est la manière la plus probable d'appliquer la promotion de propriété, mais tout autre modificateur unique (comme readonly) aura le même effet.

Note:

Les propriétés d'objet ne peuvent être typées callable à cause des ambiguïtés du moteur que ceci introduirait. Ainsi, les arguments promus, ne peuvent pas non plus être typés callable. Cependant, toute autre déclaration de type est permise.

Note:

Comme les propriétés promues sont à la fois désucrées à une propriété et aussi comme le paramètre d'une fonction, toutes les restrictions de nommage pour les propriétés et les paramètres s'appliquent.

Note:

Les attributs placés sur un argument de constructeur promu seront reproduits à la fois sur la propriété et sur l'argument. Les valeurs par défaut sur un argument de constructeur promu ne seront répliquées que sur l'argument et non sur la propriété.

New in initializers

À partir de PHP 8.1.0, les objets peuvent être utilisés comme valeur par défaut pour les paramètres, variables statiques, constantes globales et les arguments d'attributs. Les objets peuvent être désormais passés à define().

Note:

L'utilisation d'un nom de classe dynamique ou non-string ou une class anonyme n'est pas permise. L'utilisation du déballage d'arguments n'est pas permise. L'utilisation d'expressions non supportées en tant qu'argument n'est pas permise.

Exemple #4 Utilisation de new dans des initialiseurs

<?php

// Tous autorisés :
static $x = new Foo;

const
C = new Foo;

function
test($param = new Foo) {}

#[
AnAttribute(new Foo)]
class
Test {
public function
__construct(
public
$prop = new Foo,
) {}
}

// Aucun n'est autorisé (erreur de compilation) :
function test(
$a = new (CLASS_NAME_CONSTANT)(), // nom de classe dynamique
$b = new class {}, // classe anonyme
$c = new A(...[]), // déballage d'arguments
$d = new B($abc), // expression de constante non supportée
) {}
?>

Méthode de création statique

PHP supporte uniquement un constructeur unique par classe. Cependant, dans certains cas il peut être désirable de permettre à un objet d'être construit de manière différente avec des entrées différentes. La façon recommandée de faire ceci est en utilisant des méthodes statiques comme une enveloppe du constructeur.

Exemple #5 Utilisant la création via une méthode statique

<?php
$some_json_string
= '{ "id": 1004, "name": "Elephpant" }';
$some_xml_string = "<animal><id>1005</id><name>Elephpant</name></animal>";

class
Product {

private ?
int $id;
private ?
string $name;

private function
__construct(?int $id = null, ?string $name = null) {
$this->id = $id;
$this->name = $name;
}

public static function
fromBasicData(int $id, string $name): static {
$new = new static($id, $name);
return
$new;
}

public static function
fromJson(string $json): static {
$data = json_decode($json, true);
return new static(
$data['id'], $data['name']);
}

public static function
fromXml(string $xml): static {
$data = simplexml_load_string($xml);
$new = new static();
$new->id = (int) $data->id;
$new->name = $data->name;
return
$new;
}
}

$p1 = Product::fromBasicData(5, 'Widget');
$p2 = Product::fromJson($some_json_string);
$p3 = Product::fromXml($some_xml_string);

var_dump($p1, $p2, $p3);

Le constructeur peut être rendu privé ou protégé pour empêcher son appel depuis l'extérieur. Dans ce cas, seule une méthode statique sera capable d'instancier la classe. Puisqu'elles sont dans la même définition de classe, elles ont accès aux méthodes privées, même dans une instance différente de l'objet. Un constructeur privé est optionnel et peut ou non avoir du sens en fonction du cas d'utilisation.

Les trois méthodes statiques publiques démontrent alors des manières différentes d'instancier l'objet.

  • fromBasicData() prend les paramètres exacts qui sont nécessaires, puis crée l'objet en appelant le constructeur puis retournant le résultat.
  • fromJson() accepte une chaîne de caractères JSON et fait du pré-traitement sur lui-même pour se convertir dans le format désiré par le constructeur. Elle retourne alors le nouvel objet.
  • fromXml() accepte une chaîne de caractères XML, fait du pré-traitement, puis crée un objet brut. Le constructeur est appelé, mais comme tous les paramètres sont optionnels la méthode les ignore. Elle assigne alors les valeurs aux propriétés de l'objet directement avant de retourner le résultat.

Dans les trois cas, le mot-clé static est traduit en le nom de la classe dont le code y est. Dans ce cas, Product.

Destructeur

__destruct(): void

PHP possède un concept de destructeur similaire à celui d'autres langages orientés objet, comme le C++. La méthode destructeur est appelée dès qu'il n'y a plus de référence sur un objet donné, ou dans n'importe quel ordre pendant la séquence d'arrêt.

Exemple #6 Exemple avec un Destructeur

<?php

class MyDestructableClass
{
function
__construct() {
print
"Dans le constructeur\n";
}

function
__destruct() {
print
"Destruction de " . __CLASS__ . "\n";
}
}

$obj = new MyDestructableClass();

Tout comme le constructeur, le destructeur parent ne sera pas appelé implicitement par le moteur. Pour exécuter le destructeur parent, il faut appeler explicitement la fonction parent::__destruct() dans le corps du destructeur. Tout comme les constructeurs, une classe enfant peut hériter du destructeur du parent s'il n'en implémente pas un lui-même.

Le destructeur sera appelé même si l'exécution du script est stoppée en utilisant la fonction exit(). Appeler la fonction exit() dans un destructeur empêchera l'exécution des routines d'arrêt restantes.

Si un destructeur crée de nouvelles références à son objet, il ne sera pas appelé une seconde fois lorsque le compteur de références atteint de nouveau zéro ou durant la séquence d'arrêt.

À partir de PHP 8.4.0, lorsque la collecte des cycles se produit pendant l'exécution d'une fibre, les destructeurs des objets programmés pour la collecte sont exécutés dans une fibre distincte, appelée gc_destructor_fiber. Si cette fibre est suspendue, une nouvelle fibre sera créée pour exécuter les destructeurs restants. La précédente gc_destructor_fiber ne sera plus référencée par le ramasse-miettes et pourra être collectée si elle n'est pas référencée ailleurs. Les objets dont le destructeur est suspendu ne seront pas collectés tant que le destructeur n'a pas terminé ou que la fibre elle-même n'est pas collectée.

Note:

Les destructeurs appelés durant l'arrêt du script sont dans une situation où les en-têtes HTTP ont déjà été envoyés. Le dossier de travail dans la phase d'arrêt du script peut être différent avec certaines SAPIs (par exemple Apache).

Note:

Tenter de lancer une exception depuis un destructeur (appelé à la fin du script) entraîne une erreur fatale.