PHP 8.1 a introduit une nouvelle fonctionnalité vraiment intéressante : les propriétés readonly
.
Celle-ci permet de déclarer que la valeur de la propriété ne peut être modifiée qu’une seule fois. Bien souvent on va vouloir le faire dans un constructeur mais ça peut être fait dans une autre méthode. En revanche, impossible de le faire à l’extérieur de la classe.
C’est bien toute modification qui est impossible : c’est à dire qu’il est impossible d’utiliser les opérateurs d’incrémentation/décrémentation, unset()
, etc…
class Foo
{
public readonly string $bar;
public function __construct(string $bar)
{
$this->bar = $bar;
}
}
De cette manière, on pourra accéder en lecture à la propriété bar
mais il sera impossible de la modifier après l’instantiation de l’objet.
$foo = new Foo('baz');
$foo->bar = 'foobar';
// Fatal error: Uncaught Error: Cannot modify readonly property
// Foo::$bar
Ce qui est très cool ici, c’est le fait de pouvoir se passer de beaucoup de code pour protéger l’accès aux propriétés des objets (l’encapsulation).
Si j’avais voulu reproduire le même comportement avant PHP 8.1 il aurait fallu faire quelque chose comme ça :
class Foo
{
private string $bar;
public function __construct(string $bar)
{
$this->bar = $bar;
}
public function getBar(): string
{
return $this->bar;
}
}
On voit qu’on a beaucoup plus de code. En encore, je dis “reproduire” mais ce n’est pas vrai puisque dans cette dernière implémentation, $bar
peut être modifiée (par une ou plusieurs méthodes de la classe) après l’instantiation, ce qui n’est pas possible avec readonly
.
Bien sur cela fonctionne également avec la promotion de propriété de constructeur :
class Foo
{
public function __construct(public readonly string $bar)
{
}
}
Quelques détails
Propriétés typées uniquement
Cela fonctionne uniquement sur les propriétés typées. En effet, si la propriété n’est pas typée, elle a null
pour valeur par défaut et donc ne peut plus être affectée par la suite.
Pas de valeur par défaut
Il est impossible de définir une valeur par défaut à une propriété readonly
sauf si celle-ci est une promotion de propriété de constructeur.
class Foo
{
public readonly string $bar = 'baz'; // KO
public function __construct(public readonly string $bar = 'baz') // OK
{
}
}
Pas d’immutabilité sur les objets
Attention, utiliser readonly
sur un objet ne lui assure pas l’immutabilité :
class Bar {
public string $baz;
}
class Foo {
public function __construct(public readonly Bar $bar) {
}
}
$bar = new Bar();
$bar->baz = 'foobar';
$foo = new Foo($bar);
$bar->baz = 'barfoo'; // OK
Pas d’héritage
Il est impossible d’ajouter ou enlever le readonly
sur une propriété via un héritage.
Conclusion
Ce qu’il faut retenir c’est qu’on peut écrire (et donc maintenir, etc) nettement moins de code. Cela épure énormément nos classes (en particulier nos DTOs et autre Value Object) et on apprécie !
Attention quand même à bien comprendre les impacts, les possibilités et les contraintes que cela induit.