← Retour aux articles
PHP

Property hooks et visibilité asymétrique : dites adieu aux getters et setters

Par Mendel · 01/10/2026 à 00:31

Property hooks et visibilité asymétrique : dites adieu aux getters et setters

Depuis toujours en PHP, la règle d'or de la programmation objet tient en une phrase : « les propriétés sont privées, on y accède par des getters et des setters ». Résultat, des classes remplies de méthodes qui ne font rien d'autre que lire et écrire une valeur. PHP 8.4 propose une alternative élégante avec deux nouveautés complémentaires : les property hooks et la visibilité asymétrique.

Le problème des getters et setters

Voici une classe très classique :

class User
{
    private string $email;

    public function getEmail(): string
    {
        return $this->email;
    }

    public function setEmail(string $email): void
    {
        $this->email = strtolower(trim($email));
    }
}

Pourquoi ne pas simplement rendre $email publique ? Parce qu'on perdrait la possibilité d'ajouter une logique à l'écriture, ici la normalisation de l'email. Et si on rendait la propriété publique aujourd'hui, ajouter cette logique demain obligerait à modifier tout le code qui écrit $user->email = .... Alors, par précaution, on écrit des getters et des setters partout.

Les property hooks

Un hook permet d'attacher du code directement à la lecture (get) ou à l'écriture (set) d'une propriété :

class User
{
    public string $email {
        set(string $value) {
            $this->email = strtolower(trim($value));
        }
    }
}

$user = new User();
$user->email = '  Alice@Exemple.FR ';

echo $user->email; // alice@exemple.fr

La propriété est publique, on la lit et on l'écrit comme n'importe quelle propriété, mais chaque écriture passe par le hook. Le jour où une règle doit être ajoutée, on l'ajoute dans le hook, sans rien changer au code qui utilise la classe. La raison d'être des getters et setters « par précaution » disparaît.

Pour un hook d'une seule expression, une syntaxe courte existe. Avec set =>, le résultat de l'expression est affecté à la propriété :

public string $email {
    set => strtolower(trim($value));
}

Si l'on ne nomme pas le paramètre, il s'appelle automatiquement $value.

Valider à l'écriture

Un hook set est l'endroit idéal pour garantir qu'un objet n'est jamais dans un état invalide :

class Article
{
    public string $title {
        set {
            if (trim($value) === '') {
                throw new InvalidArgumentException('Le titre ne peut pas être vide.');
            }

            $this->title = trim($value);
        }
    }
}

Impossible désormais de créer un article sans titre, quel que soit l'endroit du code qui tente de le faire.

Les propriétés calculées

Un hook get peut calculer une valeur à la volée. Si les hooks ne font jamais référence à la propriété elle-même, celle-ci est virtuelle : elle n'occupe aucune mémoire et n'existe que par son calcul.

class User
{
    public function __construct(
        public string $firstName,
        public string $lastName,
    ) {
    }

    public string $fullName {
        get => $this->firstName . ' ' . $this->lastName;
    }
}

$user = new User('Ada', 'Lovelace');

echo $user->fullName; // Ada Lovelace

fullName n'ayant qu'un hook get, elle est en lecture seule : tenter d'y écrire provoque une erreur. Pratique pour exposer un temps de lecture estimé, un prix TTC ou un slug, avec la même syntaxe qu'une propriété ordinaire.

La visibilité asymétrique

Deuxième nouveauté de PHP 8.4 : une propriété peut maintenant avoir une visibilité différente en lecture et en écriture.

class Article
{
    public private(set) int $views = 0;

    public function incrementViews(): void
    {
        $this->views++;
    }
}

$article = new Article();
$article->incrementViews();

echo $article->views; // 1
$article->views = 1000; // Error : écriture interdite hors de la classe

public private(set) signifie : tout le monde peut lire, seule la classe peut écrire. Le compteur de vues est lisible partout, mais personne ne peut le falsifier depuis l'extérieur. Il existe aussi protected(set), qui autorise l'écriture dans les classes enfants.

C'est particulièrement agréable avec la promotion de propriétés dans le constructeur :

final class Money
{
    public function __construct(
        public private(set) int $amount,
        public private(set) string $currency,
    ) {
    }
}

Une ligne par propriété, lecture publique, écriture protégée : l'essentiel de ce qu'on écrivait auparavant avec un getter par propriété.

Des propriétés dans les interfaces

Autre conséquence intéressante : une interface peut désormais déclarer des propriétés, en précisant les hooks attendus.

interface HasSlug
{
    public string $slug { get; }
}

class Category implements HasSlug
{
    public function __construct(public string $name)
    {
    }

    public string $slug {
        get => strtolower(str_replace(' ', '-', $this->name));
    }
}

L'interface exige seulement qu'on puisse lire $slug. Chaque classe est libre de l'implémenter par une propriété classique ou par une propriété calculée.

Quelques limites

  • Pas de hooks sur une propriété readonly. Pour une propriété lisible mais non modifiable depuis l'extérieur, la visibilité asymétrique est souvent la bonne alternative.
  • Attention aux tableaux. Sur une propriété de type tableau avec un hook set, on ne peut pas modifier un élément directement ($obj->tags[] = 'php') : il faut réaffecter le tableau entier.
  • Un coût léger. Chaque accès à une propriété avec hook exécute du code. C'est négligeable dans l'immense majorité des cas, mais inutile d'en mettre partout.

Faut-il supprimer tous ses getters ?

Pas forcément. Les getters et setters restent parfaitement valables, et beaucoup d'outils les utilisent encore. Mais pour du nouveau code, les deux nouveautés couvrent les principaux besoins :

  • une propriété qui doit être lisible partout mais modifiable uniquement par la classe : visibilité asymétrique ;
  • une propriété qui doit être validée ou transformée à l'écriture : hook set ;
  • une valeur calculée à partir d'autres propriétés : propriété virtuelle avec un hook get.

Le résultat : des classes plus courtes, qui disent directement ce qu'elles font, sans perdre aucune des garanties de l'encapsulation.

Et vous, avez-vous commencé à utiliser les property hooks dans vos projets ? Dites-le en commentaire !

Commentaires (0)

Connecte-toi ou crée un compte pour laisser un commentaire.

Aucun commentaire pour le moment.