Les propriétés promues en PHP 8

S’il y a bien une chose redondante et rébarbative en programmation orientée objet, c’est la définition des constructeurs d’objets. Dans la plupart des cas, la majorité du code écrit est en fait de l’information que l’interpréteur ou le compileur pourrait comprendre tout seul.

Dans cette série d’articles, je reviens avec vous sur les nouveautés de la dernière version de PHP. Découvrons ensemble ce que la version 8 de PHP a de nouveau à nous apporter et comment cela pourrait nous être utile.

Ce que je viens d’énoncer n’est pas très nuancé, c’est vrai. Mais si je parcours les codebases de mes précédents projets, la majorité des constructeurs que j’ai écrits ne servent qu’à initialiser des variables (propriétés) d’instance.

Prenons un exemple en PHP pour illustrer cela :

<?php

class MyClass extends ParentClass {
    private string $title;
    private bool $isUnique;
    private bool $isVariadic;
    
    public function __construct(
        string $title,
        bool $isUnique = false,
        bool $isVariadic = false,
        ?string $desc = null,
    ) {
        parent::__construct($text);
        
        $this->title = $title;
        $this->isUnique = $isUnique;
        $this->isVariadic = $variadic;
    }
}

Voici un exemple typique de tout ce que j’ai décris plus haut. Il y a trois propriétés qui sont répétées en tant qu’arguments du constructeur et encore deux fois au moment de l’assignation à l’intérieur du constructeur. Les types aussi sont répétés deux fois.

On a l’impression de voir trois fois quasiment le même code. C’est ennuyeux à lire, mais encore plus fatiguant à écrire ou maintenir : cette façon de faire augmente le risque d’erreur. Pourquoi ? Si je modifie une des propriétés d’instance, il faut absolument que je pense à modifier également les déclarations de propriétés du constructeur et / ou les assignations.

PHP 8 apporte une solution à ce problème grâce à la promotion des propriétés de constructeur.

<?php

class MyClass extends ParentClass {
    public function __construct(
        private string $title,
        private bool $isUnique = false,
        private bool $isVariadic = false,
        ?string $desc = null,
    ) {
        parent::__construct($text);
    }
}

Les deux exemples sont identiques. Mais observez comme les propriétés promues permettent de rendre le code plus simple, plus lisible et plus intelligent. Toutes les informations dont nous avons besoin pour comprendre le code sont sur la même ligne, et non éclatées en trois endroits distincts.

Encore une fois, il ne s’agit pas d’une fonctionnalité révolutionnaire ou de quelque chose de réellement nouveau. D’autres langages comme le TypeScript possèdent déjà cette fonctionnalité depuis un moment.

Non utilisable dans toutes les situations

Certains d’entre vous y ont peut-être déjà pensé, mais il y a des cas ou l’utilisation de la promotion des propriétés du constructeur ne va pas être possible.

Cette fonctionnalité signifie : créer des variables d’instance automatiquement et les assigner. Ce qui n’est pas possible dans un constructeur abstrait par exemple.

<?php

interface MyInterface {
    public function __construct(
        private string $title, // Erreur
    ) {}
}

abstract class MyClass {
    abstract public function __construct(
        private string $title, // Erreur
    ) {}
}

Un autre cas où cette fonctionnalité n’est pas utilisable est le cas des callable. Le type callable n’est pas supporté pour les propriétés de méthode. Il est donc impossible de promouvoir une propriété callable puisqu’elle n’existe pas.

<?php

class MyClass {
    public function __construct(
        private callable $fn, // Erreur
    ) {}
}

Je rajouterais aussi que les propriétés promues ne peuvent pas être utilisées avec les types nullable implicitement. Ou plutôt, un type nullable ne peut pas être implicite avec une propriété promue.

<?php

class MyClass {
    public function __construct(
        private string $desc = null, // Erreur
        private ?string $text = null, // OK
    ) {}
}

Du moment que vous indiquez explicitement que la propriété peut prendre la valeur null, PHP ne vous en voudra pas.

Les propriétés promues améliorent la qualité du code

Je l’ai souvent utilisé en TypeScript et je trouve la promotion des propriétés du constructeur vraiment utile dans mon quotidien de développeur. Je m’explique : d’abord j’écris trois fois moins de code. Ensuite, ma lecture et ma compréhension du code sont plus simple pour une variable d’instance donnée.

<?php

class MyClass extends ParentClass {
    public function __construct(
        private string $title,
        private bool $isUnique = false,
        private bool $isVariadic = false,
        string $desc = null,
    ) {
        parent::__construct($text);
    }
}

Mais cette méthode me donne aussi des informations sur les autres propriétés du constructeur. Dans l’exemple ci-dessus, avez-vous remarqué la propriété $desc ? Nous n’utilisons pas la promotion des propriétés du constructeur pour elle, ce qui me donne une autre information : $desc n’est pas un cas classique de variable d’instance, le développeur a dû faire autre chose avec et il serait peut-être intéressant de regarder de quoi il s’agit.

C’est contextuel : comme le développeur qui a écrit le code a utilisé des propriétés promues, pourquoi n’a-t-il pas promu toutes les propriétés ? Sûrement parce qu'il n’avait pas le choix. Peut-être qu’il avait des tests particuliers à faire sur cette propriété. Ou encore, comme dans notre exemple, peut-être qu’il devait le passer à la classe parente. Cela rend le code plus intelligent, mais cela rend la lecture du code plus intelligente aussi en indiquant les points d’attention à avoir.

Et comme c’est contextuel, vous vous en doutez, le raisonnement que je viens de décrire ne fonctionnera pas à tous les coups. Si aucune des propriétés n’est promues, cela ne veut pas forcément dire que toutes les propriétés de ce constructeur sont des cas particuliers. Peut-être que le développeur qui a écrit le code ne sait pas encore que les propriétés promues existent, peut-être qu’il ne veut pas utiliser cette fonctionnalité ou alors c’est peut-être du code qui a été écrit avant l’arrivée de PHP 8. Bref, vous avez compris l’idée.

Je terminerais cet article en rappelant que les propriétés promues sont une fonctionnalité de paresseux et le développeur étant paresseux, je suis convaincu que vous serez tous ravis de l’adopter dans vos projets à avenir. En attendant, voici de la lecture :