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
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
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.
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 :
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).
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é.
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).