Ce qui se passe
Pendant plusieurs semaines, Nicolas Grekas a passé au profileur l'application Symfony Demo et un projet d'API, et chaque point lent est devenu une pull request. L'ensemble arrive avec Symfony 8.2, attendue en novembre, sans rien à modifier côté application.
Les API d'abord
Le Serializer concentre les gains les plus nets.
- Normalisation : le contexte de chaque attribut est calculé une seule fois, le tableau normalisé n'est plus recopié à chaque attribut, et la table de discriminants est lue une fois par objet. Sur des listes de 150 à 900 objets avec groupes et convertisseur de noms : 14 à 24 % de temps gagné (#66383).
- PropertyAccess : 40 % d'instructions PHP en moins par lecture de propriété (#66397).
- Convertisseur
snake_case: résultats mémorisés au lieu d'une regex par attribut, 2 à 5 % sur les listes (#66384). - Dénormalisation (
#[MapRequestPayload]) : les métadonnées de types du Serializer et du Validator sont préchauffées dans un seul fichier de cache, et les fichiers de classes ne sont plus relus à chaque requête. Les POST gagnent 4 à 5 % en production (#66386).
En développement, où PropertyInfo n'est pas mis en cache, les docblocks de constructeur ne sont plus analysés qu'une fois par classe : 24 à 38 % de gain sur les POST, soit 10 à 14 ms par requête (#66382).
Le changement qui touche vos journaux
Les erreurs client (réponses 4xx) sont désormais journalisées au niveau
warning et non plus error
(#66391). Avec la recette
Monolog par défaut — fingers_crossed et action_level: error — une réponse
400, 403 ou 422 ne vide plus le tampon de la requête. La 422 renvoyée par
#[MapRequestPayload] y gagne 3 à 4 %.
Le revers : une alerte ou un tableau de bord construit sur ces messages en
error va se taire. Le niveau reste réglable exception par exception, par
framework.exceptions ou l'attribut #[WithLogLevel].
Production, développement, déploiement
| Mesure (Symfony Demo) | Avant | Après |
|---|---|---|
| Requête suivant l'édition d'un fichier de traduction | 790 ms | 85 ms |
cache:warmup à froid, schéma XLIFF allégé |
1,77 s | 1,57 s |
cache:warmup à froid, thèmes de formulaire utiles seulement |
2,27 s | 2,11 s |
| Dump du conteneur pour le débogage | 49 ms | 14 ms |
| Rendu de l'importmap en dev (AssetMapper) | 4,7 ms | 2,4 ms |
| Journalisation d'une 422 | 125 µs | 70 µs |
Autres retouches : PhpFilesAdapter vérifie qu'un fichier existe avant de
l'inclure, ce qui fait gagner 830 µs par requête sur un cache en lecture seule
sans APCu ; 8 des 18 lectures de variables d'environnement d'une requête de
production disparaissent ; et le HtmlSanitizer ne lance plus le
parseur sur un texte sans < ni &, ce qui le rend 5 fois moins cher et lui
évite d'allouer 1,1 Mo.
Jusque dans PHP
Le travail a débordé sur Twig, TwigComponent, Lexbor (le parseur HTML5 de
Dom\HTMLDocument) et PHP lui-même : new ArrayIterator() et
new ArrayObject() exécutent environ 180 instructions de moins
(php-src #23946). D'autres
propositions sont encore en revue, dont un htmlspecialchars() qui renvoie
son entrée quand il n'y a rien à échapper — 3,5 fois moins cher sur Symfony
Demo, qui l'appelle 711 fois par requête.
Source : New in Symfony 8.2: Performance Improvements, blog Symfony, 2 octobre 2026.