Image de couverture : PHP 8.4 : Les Lazy Objects natifs surpassent les proxies Doctrine (et comment migrer)
tech

PHP 8.4 : Les Lazy Objects natifs surpassent les proxies Doctrine (et comment migrer)

25 May 2026
6 min de lecture
4 vues
Sébastien Muler

PHP 8.4 : Les Lazy Objects natifs surpassent les proxies Doctrine

Depuis novembre 2024, PHP 8.4 intègre une fonctionnalité attendue depuis longtemps : les objets lazy natifs. Pour les équipes qui maintiennent des applications Symfony/Doctrine, cette évolution n'est pas anodine — elle touche directement les performances de chargement des entités et la lisibilité du code.

Cet article s'appuie sur l'analyse publiée par Gabriel Anhaia sur dev.to et la replace dans le contexte d'applications métiers PHP/Symfony.


Le problème historique : les proxies générés par Doctrine

Depuis 2010, Doctrine ORM utilise un mécanisme de proxy classes pour implémenter le chargement différé (lazy loading) des entités. Le principe est simple : plutôt que d'interroger la base de données dès qu'une entité est référencée, Doctrine génère une sous-classe intermédiaire — un proxy — qui retarde l'hydratation jusqu'au premier accès réel à une propriété.

Ce mécanisme fonctionne, mais il traîne quelques inconvénients bien connus :

  • Génération de fichiers sur le disque : Doctrine écrit des classes proxy dans un répertoire var/cache, ce qui alourdit les déploiements et complique les environnements sans écriture disque.
  • Ruptures de typage : un proxy est une sous-classe de votre entité. Résultat, $entity instanceof User peut retourner true alors que l'objet n'est pas encore initialisé, et certaines vérifications strictes échouent de façon subtile.
  • Overhead mécanique : chaque appel de méthode passe par la couche proxy avant d'atteindre l'entité réelle, ce qui représente un coût non négligeable sur des hydratations massives.

Sur un lot de 1 000 entités, la différence de temps entre proxies générés et lazy objects natifs est mesurable — et en faveur de PHP 8.4.


Ce que PHP 8.4 apporte : deux nouvelles méthodes sur ReflectionClass

PHP 8.4 introduit deux méthodes sur ReflectionClass :

  • newLazyGhost() — crée un objet dont l'initialisation est différée. L'objet existe en mémoire, mais ses propriétés ne sont peuplées qu'au premier accès.
  • newLazyProxy() — crée un objet qui délègue toutes ses interactions à un objet cible créé au moment de l'initialisation.

Voici un exemple minimaliste avec newLazyGhost() :

$reflector = new ReflectionClass(User::class);

$lazyUser = $reflector->newLazyGhost(function (User $user) {
    // Cette closure n'est appelée qu'au premier accès à une propriété
    $user->__construct($id, fetchNameFromDatabase($id));
});

// Aucune requête SQL à ce stade
// ...

echo $lazyUser->getName(); // Déclenchement ici

La différence fondamentale avec l'approche Doctrine : aucune sous-classe n'est générée. L'objet retourné est une instance de User à part entière. instanceof User retourne true sans ambiguïté. Les outils d'analyse statique (PHPStan, Psalm) fonctionnent correctement. Les tests unitaires n'ont plus à gérer des cas limites liés aux proxies.


Doctrine ORM 4.0 : la migration transparente

Bonne nouvelle pour les projets Symfony : Doctrine ORM 4.0 utilise déjà les lazy objects natifs en interne. Si vous êtes sur cette version, vous bénéficiez des gains de performance sans aucune modification de votre code applicatif.

Pour les projets encore sur Doctrine ORM 2.x ou 3.x, la mise à jour vers la version 4 est le chemin le plus direct. La migration au niveau de Doctrine elle-même se résume, côté implémentation interne, à remplacer la génération de proxy par un appel à newLazyGhost() — une seule méthode.

Du point de vue de votre code métier, rien ne change : vos repositories, vos entités, vos services restent identiques.


Au-delà des ORMs : trois cas d'usage concrets

L'intérêt des lazy objects natifs dépasse Doctrine. Voici trois situations où cette fonctionnalité apporte une valeur directe dans une application Symfony :

1. Conteneurs d'injection de dépendances

Symfony compile son conteneur de services, et certains services lourds (clients API, connexions externes, moteurs de rendu) sont instanciés au boot même s'ils ne sont pas utilisés dans la requête courante. Avec les lazy objects, ces services peuvent être déclarés comme différés sans proxy généré, réduisant le temps de démarrage à froid.

2. Objets de configuration volumineux

Une configuration applicative qui agrège de nombreuses sources (fichiers YAML, variables d'environnement, base de données) peut être encapsulée dans un lazy object. La lecture effective n'a lieu qu'au moment où la configuration est réellement consultée.

3. Services métiers coûteux à initialiser

Tout service qui effectue des appels réseau ou des lectures disques à l'instanciation est un candidat idéal. Le lazy object permet de câbler le service dans le conteneur sans payer le coût d'initialisation sur les requêtes qui n'en ont pas besoin.


Ce que cela change concrètement pour vos projets

La valeur de cette évolution est double :

  • Performance : moins d'overhead mémoire et CPU sur les opérations répétitives comme l'hydratation d'entités en masse.
  • Maintenabilité : suppression des fichiers proxy générés, disparition des cas limites instanceof, meilleure compatibilité avec l'analyse statique.

Pour une application métier qui traite plusieurs centaines de requêtes par seconde, ou qui charge régulièrement des collections de plusieurs centaines d'entités, le gain cumulé est loin d'être négligeable — sans aucune réécriture fonctionnelle.


Conclusion

Les lazy objects natifs de PHP 8.4 illustrent une tendance de fond : les fonctionnalités que l'écosystème implémentait en espace utilisateur (proxies générés, fichiers sur disque, hacks de réflexion) migrent progressivement vers le cœur du langage, avec de meilleures performances et moins de cas limites.

Si votre stack est sur PHP 8.4 et Doctrine ORM 4.0, vous bénéficiez déjà de ces gains. Si vous êtes sur des versions antérieures, c'est une raison supplémentaire — parmi d'autres — de planifier la mise à jour.

Chez MulerTech, nous accompagnons régulièrement des équipes dans ces migrations PHP/Symfony. N'hésitez pas à nous contacter si vous souhaitez évaluer l'impact sur votre application.

Partager cet article