Le 28 juin 2025, l'équipe Doctrine a publié ORM 3.4.0 — une version qui mérite ton attention immédiate si tu maintiens une application Symfony. Pas parce qu'elle introduit une rupture de compatibilité, mais parce qu'elle marque le début de la fin d'un mécanisme vieux de quinze ans : les proxies PHP générés à la volée. Leur remplaçant, les Lazy Objects natifs de PHP 8.4, est désormais activable en production avec un simple flag de configuration. Voilà ce que ça change concrètement.
Pourquoi les proxies générés atteignaient leurs limites
Depuis ses débuts, Doctrine charge les associations en mode lazy en générant à la volée une classe PHP qui hérite de ton entité — un proxy stocké dans var/cache/*/doctrine/orm/Proxies/. Ce mécanisme a tenu pendant des années, mais il embarque des contraintes structurelles de plus en plus pesantes : les entités ne peuvent pas être déclarées final, les propriétés readonly compliquent sérieusement le mapping, et les Property Hooks de PHP 8.4 — qui associent une logique get/set directement à une propriété — étaient tout simplement incompatibles. La classe générée écrasait le hook. L'approche atteignait ses limites structurelles au moment précis où PHP devenait plus expressif.
Activer les Lazy Objects natifs dans Symfony 7.3
PHP 8.4 a introduit une API de réflexion bas niveau permettant de créer des objets dont l'initialisation est différée nativement par le moteur : ReflectionClass::newLazyGhost(). Doctrine ORM 3.4.0 exploite cette API pour éliminer définitivement la génération de fichiers proxy. L'activation ne demande qu'une ligne dans ta configuration Symfony :
# config/packages/doctrine.yaml
doctrine:
orm:
# Nécessite PHP 8.4 minimum et Doctrine ORM >= 3.4
use_native_lazy_objects: trueAprès activation, tu peux supprimer le répertoire de proxies générés et retirer la commande doctrine:generate:proxies de tes pipelines CI/CD. Aucune migration de données n'est requise — les entités existantes restent compatibles telles quelles, à condition de cibler PHP 8.4.
# Nettoyage post-activation (inutile de garder les anciens proxies)
php bin/console cache:clear
rm -rf var/cache/prod/doctrine/orm/Proxies/
# Vérifier qu'aucun proxy n'est régénéré au warmup
php bin/console cache:warmup --env=prodProperty Hooks sur tes entités, sans compromis
Besoin d'un expert Symfony ?
Réserver un appel →C'est la conséquence la plus structurante pour le code métier. Avec les Lazy Objects natifs, tes entités peuvent désormais déclarer des Property Hooks PHP 8.4 sans que Doctrine ne les court-circuite. Tu peux normaliser, valider ou dériver une valeur directement au niveau de la propriété persistée :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Product
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
public readonly int $id;
#[ORM\Column(length: 255)]
public string $name {
set(string $value) {
if (trim($value) === '') {
throw new \InvalidArgumentException('Le nom ne peut pas être vide.');
}
$this->name = mb_strtolower(trim($value));
}
}
#[ORM\Column]
public readonly \DateTimeImmutable $createdAt;
public function __construct(string $name)
{
$this->name = $name; // passe par le hook set
$this->createdAt = new \DateTimeImmutable();
}
}Avec l'ancien système de proxies, cet exemple aurait purement ignoré le hook sur l'objet hydraté par Doctrine — la normalisation n'aurait jamais été appliquée. Avec les Lazy Objects natifs, le comportement est cohérent entre une entité instanciée directement en PHP et une entité chargée depuis la base de données. C'est une garantie que les proxies générés ne pouvaient pas offrir.
Roadmap ORM 4.0 : ce que ça implique pour ta migration
ORM 3.4.0 est explicitement balisé comme le premier jalon vers ORM 4.0, qui rendra les Lazy Objects natifs obligatoires et retirera toute la machinerie de génération de proxies. Si tu actives le flag dès aujourd'hui, tu réduis le delta de migration à quasiment zéro. Quelques points de vigilance avant de basculer :
- PHP 8.4 est un prérequis strict — il n'existe aucun fallback sur 8.3 ou inférieur.
- Les entités
finalsont désormais supportées : c'est une opportunité concrète de renforcer l'encapsulation de ton domaine. - Les bibliothèques tierces qui étendent tes entités (rare, mais ça existe) peuvent casser — audite tes dépendances avant de basculer.
- Les tests qui comparent des noms de classe de proxy (
Proxies\__CG__\App\Entity\...) devront être mis à jour. doctrine/orm ^3.4est requis ; vérifie la compatibilité dedoctrine/doctrine-bundle(>= 2.13 recommandé).
La migration progressive est la bonne stratégie : active le flag sur l'environnement de staging, fais tourner ta suite de tests complète, et valide sur un périmètre fonctionnel limité avant de passer en production. La compatibilité ascendante est intégralement maintenue — tu n'as pas à tout migrer d'un coup.
Doctrine ORM 3.4.0 est une de ces releases qui semblent anodines en surface mais signalent un tournant architectural majeur. Si tu gères une base Symfony en PHP 8.4, activer les Lazy Objects natifs aujourd'hui, c'est moins de code généré à maintenir, des entités plus expressives et une migration vers ORM 4.0 qui devient triviale le moment venu. Si tu veux un regard externe sur l'état de ton stack Doctrine/Symfony — dette technique, mapping, performance des requêtes — c'est exactement ce qu'on couvre dans un Bear Scan.
Besoin d'un expert Symfony ?
20 ans d'expérience sur l'écosystème PHP/Symfony.