Aller au contenu principal

PHP 8.4 Property Hooks : quand Doctrine 3.4 révolutionne vos Getters/Setters

Découvrez les Property Hooks PHP 8.4 avec Doctrine 3.4. Fini les getters/setters verbeux !
Catégorie

Symfony

Architecture, composants et patterns avancés du framework Symfony.

Lecture
4 min
Niveau
Intermédiaire
août 12 2025
Partager

Il était une fois une entité Doctrine User. Elle avait 10 propriétés... et 200 lignes de code. Pourquoi ? Parce que pour chaque propriété, je devais écrire un getFoo() et un setFoo(). C'était verbeux, c'était moche, et ça n'apportait aucune valeur.

Heureusement, PHP 8.4 est arrivé avec les Property Hooks. Et c'est la fonctionnalité que j'attendais depuis 10 ans (jalousement, en regardant mes amis faire du C#).

C'est quoi un Hook ?

C'est la fusion de la propriété et de ses accesseurs. Au lieu de séparer la donnée (private $name) de son accès (public getName()), on définit tout au même endroit.

Avant

PHP
class User {
    private string $name;
    
    public function getName(): string 
    {
        return strtoupper($this->name); // Logique de display
    }
    
    public function setName(string $name): void 
    {
        $this->name = trim($name); // Logique de nettoyage
    }
}

Après

PHP
class User {
    public string $name {
        // Le 'get' remplace getName()
        get => strtoupper($this->name);
        
        // Le 'set' remplace setName()
        set {
            $this->name = trim($value);
        }
    }
}

C'est tout. C'est propre, c'est concis, et c'est directement lié à la donnée.

J'ai migré mes Entités

Sur ce projet, j'ai décidé de sauter le pas. J'ai migré mes entités principales vers les Property Hooks. Le résultat ? J'ai supprimé 60% de mon code (les getters/setters) et j'ai gagné en expressivité.

Voici les patterns puissants que j'utilise désormais en production :

La visibilité asymétrique

C'est LE truc qui manquait à PHP. Avant, pour avoir une propriété publique en lecture mais privée en écriture (pour garantir l'intégrité), on devait faire private $prop + public getProp(). C'est fini.

PHP
class Post {
    // Tout le monde peut lire le statut,
    // mais seule la classe peut le modifier (workflow).
    #[ORM\Column]
    public private(set) string $status = 'draft';

    public function publish(): void {
        $this->status = 'published';
    }
}

C'est propre, c'est net. On expose l'intention (lecture seule publique) directement dans la signature.

Le casse-tête des relations (ManyToOne / OneToMany)

C'est souvent là que les détracteurs des hooks m'attendaient. "Comment tu gères la synchronisation des deux côtés de la relation ? "Facile : je fais rien".

Regardez cette relation entre Post et Category :

PHP
class Post {
    #[ORM\JoinColumn(name: 'category_id', referencedColumnName: 'id')]
    #[ORM\ManyToOne(inversedBy: 'posts')]
    public ?Category $category = null;
}

class Category {
    #[ORM\OneToMany(targetEntity: Post::class, mappedBy: 'category', fetch: 'EXTRA_LAZY')]
    public Collection $posts;
  
    public function __construct() {
        $this->posts = new ArrayCollection();
    }
}

Plus besoin de setCategory() complexe : j'assigne $post->category = $category et Doctrine flush la relation.

Les propriétés virtuelles

Pourquoi stocker en base ce qu'on peut calculer à la volée ? Avec les hooks, on peut créer des propriétés qui n'existent pas réellement en mémoire, mais qui se comportent comme telles (c'était déjà le cas avant, mais là, c'est plus facile de concentrer tout le code).

PHP
class User {
    #[ORM\Column]
    public string $firstName;
    
    #[ORM\Column]
    public string $lastName;

    // Cette propriété n'est pas stockée !
    public string $fullName {
        get => sprintf('%s %s', $this->firstName, mb_strtoupper($this->lastName));
    }
}

C'est l'élégance même. $user->fullName fonctionne, mais ne pollue pas l'état de l'objet.

4. Validation à la volée

On peut aller encore plus loin et interdire les états invalides directement au niveau de la propriété.

PHP
class Product {
    #[ORM\Column]
    public int $price {
        set {
            if ($value < 0) {
                throw new \InvalidArgumentException("Un prix ne peut pas être négatif, voyons.");
            }
            // On applique une TVA par défaut si on modifie le brut
            $this->price = $value;
            $this->taxedPrice = $value * 1.20;
        }
    }
}

Ce n'est pas juste une mode

Certains diront que c'est cosmétique. Je ne suis pas d'accord. Quand je lis une entité moderne en PHP 8.4, je vois la donnée et ses règles au même endroit. Je ne scrolle plus à travers 500 lignes de boilerplate pour trouver la petite règle métier cachée dans un setStatus. La charge mentale diminue. La robustesse augmente.

Le mot de la fin

PHP 8.4 continue de transformer le langage en un outil moderne, expressif et typé. Les Property Hooks ne sont pas juste une mode syntaxique : ils encouragent à rapprocher la logique de la donnée (encapsulation réelle vs Getter/Setter).

Poursuivre la lecture

Sélectionné avec soin pour vous.

UX

Comment j'ai transformé EasyAdmin en tableur intelligent

Implémentez l'édition inline dans EasyAdmin avec Symfony UX Turbo et Stimulus. Textes, enums, dates, associations : modifiez vos champs sans quitter la liste.

5 min de lecture
PHP

PHP 8.4 : rattrapage de retard ou véritable modernisation ?

PHP 8.4 introduit les Property Hooks, la visibilité asymétrique et un parser HTML5 natif. Découvrez les nouveautés majeures de cette version attendue depuis des années.

4 min de lecture
Sécurité

Sécurité & 2FA : la forteresse numérique

Sécurisez vos utilisateurs avec une 2FA robuste (OTPHP, QrCode).

3 min de lecture