Dans Symfony 8.2, chaque composant fournit son propre bundle, avec sa configuration, ses services et les classes associées. FrameworkBundle n'a plus à tout porter : 29 bundles apparaissent, de CacheBundle à MessengerBundle. Annoncé le 18 septembre 2026 sur le blog officiel, ce chantier arrive dans Symfony 8.2, dont la sortie est prévue en novembre 2026.
Ouvre config/packages/mailer.yaml. Première ligne : framework:. Ouvre messenger.yaml, cache.yaml, http_client.yaml : même ligne, même indentation superflue. Depuis Symfony 2, FrameworkBundle fait office de colle entre les composants et ton application. Ça marche, mais ça a un coût qu'on a fini par ne plus voir : du couplage. Bonne nouvelle pour ton backlog, tu n'as rien à migrer. Mauvaise nouvelle pour ta CI, quelques détails vont quand même casser.
Cet article ouvre une série consacrée aux nouveautés de Symfony 8.2, une par article. Pour le contexte plus large de la version, relis notre aperçu de Symfony 8.2 : Cron, ObjectMapper et AutoconfigureTag.
Pourquoi FrameworkBundle était devenu un goulot d’étranglement
Le billet officiel, signé Javier Eguiluz, pose trois symptômes. Tu en as probablement vécu au moins deux :
- Double modification : ajouter une option à un composant, par exemple au Mailer, obligeait à toucher le composant et FrameworkBundle.
- Dépendances cachées : activer les formulaires activait aussi la validation, sans que rien ne te le signale.
- Une racine inutile : chaque fichier de config commençait par
framework:, un niveau d'indentation qui n'apportait aucune information.
La PR #65948 de Nicolas Grekas donne l'ampleur du ménage : FrameworkExtension.php passe de 4 007 à 758 lignes, Configuration.php de 3 207 à 628, soit 5 828 lignes en moins à elles deux. Les méthodes register*Configuration() tombent de 35 à 11.
Un détail éclaire la logique de l'ensemble. Une racine de configuration correspond à une extension du conteneur. Tant qu'une seule extension possédait toutes les clés, le niveau framework: ne pouvait pas disparaître. Supprimer ce niveau imposait de donner à chaque section sa propre extension, donc à chaque composant son propre bundle.
Faut-il modifier son application pour Symfony 8.2 ?
Non. Les nouveaux bundles s'enregistrent automatiquement dès que le composant est installé : rien à ajouter dans config/bundles.php. Grâce à un mécanisme d'alias de configuration, les clés framework.mailer, framework.messenger et toutes les autres continuent de fonctionner à l'identique. Rien n'est déprécié.
La PR #65948 liste les 29 bundles concernés :
AssetBundle,AssetMapperBundle,CacheBundle,HtmlSanitizerBundle,HttpClientBundle,JsonPathBundleJsonStreamerBundle,LockBundle,MailerBundle,MessengerBundle,MimeBundle,NotifierBundleObjectMapperBundle,ProcessBundle,PropertyAccessBundle,PropertyInfoBundle,RateLimiterBundle,RemoteEventBundleRouterBundle,SchedulerBundle,SemaphoreBundle,SerializerBundle,TranslationBundle,TypeInfoBundleUidBundle,ValidationBundle,WebLinkBundle,WebhookBundle,WorkflowBundle
Certains de ces bundles ne possèdent aucune clé de configuration (JsonPath, Mime, ObjectMapper, Process) : ils existent pour déclarer les services de leur composant, pas pour exposer une section YAML.
Comment retirer le niveau framework: de tes fichiers de config
Si tu préfères la nouvelle forme, le billet montre le diff à appliquer, fichier par fichier. C'est optionnel : l'ancienne écriture reste valide.
# config/packages/mailer.yaml
- framework:
- mailer:
- dsn: '%env(MAILER_DSN)%'
+ mailer:
+ dsn: '%env(MAILER_DSN)%'Même principe avec la configuration en PHP :
// config/packages/mailer.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return App::config([
'mailer' => [
'dsn' => env('MAILER_DSN'),
],
]);La plupart des clés gardent leur nom une fois remontées à la racine. Trois changent, et ce sont elles qui piégeront un rechercher-remplacer trop rapide :
framework.assetsdevientassetframework.translatordevienttranslationframework.workflowsdevientworkflow
Dernier ajout, qui corrige un vrai piège : lock et semaphore acceptent désormais un DSN directement, comme raccourci de l'option resources.
# config/packages/lock.yaml
lock: '%env(LOCK_DSN)%'La PR #66021 explique pourquoi c'est notable. Avant, une valeur non-tableau à la racine d'une extension était remplacée par un tableau vide, sans erreur ni log. Avec lock: 'redis://…', tu obtenais un store local : un verrou censé être partagé entre serveurs devenait silencieusement un verrou local. Ce genre de bug ne se voit qu'en production, sous charge.
Attention aussi à la double écriture. D'après la PR #65884, si framework.mailer et mailer coexistent dans deux fichiers, la nouvelle clé l'emporte. La fusion est déterministe, mais relire une config où la même section vit à deux endroits est un piège pour le prochain développeur. Choisis une écriture par section.
Migration Symfony en vue ?
Bear Upgrade migre votre codebase 2,5× à 4,5× moins cher qu'une ESN, selon sa maturité.
Les petites ruptures à auditer avant la mise à jour
La compatibilité des clés ne couvre pas tout. Le billet liste des « changements mineurs » qui, eux, peuvent faire échouer un pipeline :
debug:config framework mailerne fonctionne plus : utilisedebug:config mailer. Cherche cette commande dans tes scripts, ton Makefile et ta CI.- Activer les formulaires n'active plus la validation : c'est l'installation de
symfony/validatorqui l'active. Si ton application comptait sur l'effet de bord sans déclarer le paquet danscomposer.json, ajoute-le. FrameworkBundle\Controller\RedirectControllerest déprécié au profit de celui du composant Routing, etTemplateControllerrejoint TwigBundle. Ils sont souvent référencés en chaîne dans lecontroller:de tes routes.AsRoutingConditionServicemigre vers le composant Routing, etRouteLoaderInterfaceest remplacée par l'attribut#[AsRouteLoader].
Le passage de l'interface à l'attribut mérite une ligne d'attention. Un attribut ne s'hérite pas comme une interface : selon la PR #65948, une classe qui étend un loader annoté doit porter l'attribut elle-même. Les anciens identifiants de services restent en alias dépréciés, donc tes routes continuent de marcher et le signalent dans les logs de dépréciation. Encore faut-il que ta CI les remonte.
29 bundles de plus ralentissent-ils l’application ?
Le billet répond non. La plupart de ces bundles ne servent qu'à la compilation du conteneur ; en production, Symfony n'instancie plus leurs objets à chaque requête, seulement quand du code les demande. La PR #65948 attribue ce comportement à la PR #65925, qui instancie à la demande les bundles sans travail au démarrage. Aucun benchmark chiffré n'est publié : mesure sur ta propre application avant de promettre quoi que ce soit à ton équipe.
aliasOf() et appendFromCallback() : les outils offerts aux auteurs de bundles
Pour tenir la compatibilité, l'équipe a étendu le composant Config, et ces outils sont publics. aliasOf() fait suivre la valeur d'un nœud racine vers la configuration d'une autre extension. C'est ce qui permet à framework.workflows de continuer à fonctionner alors que le composant Workflow possède désormais la config workflow :
$rootNode
->children()
->variableNode('workflows')->aliasOf('workflow')->end()
->end();La PR #65884 fixe les règles du jeu : seuls les enfants directs d'un nœud racine peuvent être des alias, les alias ne se chaînent pas, et une extension cible absente lève une erreur au lieu d'être ignorée. Le transfert a lieu après tous les prepend() et avant tout load(), donc un bundle tiers qui préfixe encore framework.workflows continue de fonctionner, quel que soit son ordre d'enregistrement.
appendFromCallback() ajoute plusieurs nœuds depuis un callback sans casser la chaîne fluide du TreeBuilder. Utile quand un même groupe d'options revient à plusieurs endroits de ta configuration :
$rootNode
->children()
->arrayNode('default_options')
->children()
->appendFromCallback($this->addCommonOptions(...))
->end()
->end()
->end();
// ...
private function addCommonOptions(NodeBuilder $builder): void
{
$builder
->integerNode('max_redirects')->end()
->scalarNode('http_version')->end()
;
}Si tu maintiens un bundle interne à la configuration touffue, ces deux méthodes valent plus que le gain de syntaxe côté application. Les auteurs de bundles tiers ont aussi un point à vérifier : un bundle qui lisait la config framework.* d'une autre section au chargement doit désormais passer par le conteneur fusionné, à la compilation.
FrameworkBundle va-t-il être déprécié ?
La question est posée sous le billet officiel. Nicolas Grekas répond que non : FrameworkBundle se spécialise sur le cœur du framework HTTP, et ses fonctionnalités optionnelles en sont désormais séparées. Il ajoute qu'on pourra construire une application console avec seulement ConsoleBundle et, par exemple, WorkflowBundle, sans FrameworkBundle. C'est un commentaire de contributeur, pas une annonce de roadmap, mais la direction est nette.
Restent dans FrameworkBundle les sections qui font « le framework » plutôt qu'un composant : form, http_cache, esi, ssi, fragments, profiler, php_errors, session, request, secrets et csrf_protection. C'est le vrai sujet de cette nouveauté : Symfony devient assemblable par morceaux, dans la continuité du mode HTTP-less introduit en Symfony 8.1.
Que faire concrètement avant la sortie de novembre 2026
- composer.json : rien à changer, sauf si tu utilises les formulaires en comptant sur la validation implicite. Dans ce cas, déclare
symfony/validator. - bundles.php : n'ajoute rien. Les nouveaux bundles sont auto-enregistrés ; ne les déclare pas à la main.
- Config : migre fichier par fichier, quand tu touches déjà ces fichiers. Évite qu'une même section vive sous deux écritures, et pense aux trois renommages.
- CI et scripts : remplace
debug:config framework <section>pardebug:config <section>. - Code : cherche
RedirectController,TemplateController,AsRoutingConditionServiceetRouteLoaderInterface, et fais remonter les dépréciations en CI.
Si tu pars d'une version plus ancienne, commence par ce qu'il faut savoir avant de migrer depuis la 7.4 LTS : les dépréciations accumulées pèsent bien plus lourd que ce découpage.
Ma lecture
Pour une application existante, cette nouveauté ne te coûtera presque rien. Retire le niveau framework: sur les nouveaux fichiers et laisse l'existant tranquille : un commit purement cosmétique sur trente fichiers YAML ne vaut pas le bruit qu'il ajoute à l'historique. Le travail réel tient dans l'audit en trois points : les debug:config framework de tes scripts, la dépendance implicite entre formulaires et validation, les classes déplacées.
Pour les nouveaux projets, et surtout les outils en ligne de commande, le gain est plus net : une application avec le seul ConsoleBundle devient envisageable. La 8.2 n'est pas encore sortie, et tout ce qui précède repose sur le billet officiel et les PR publiques. Je referai le point sur une vraie base de code à la sortie.
Rien d'urgent, donc. Mais ce genre de détail (clés renommées, classes déplacées, dépréciations à traiter avant le saut suivant) est exactement ce qui s'accumule quand personne n'a le temps de suivre les versions. Si c'est ton cas, c'est le travail que couvre Bear Upgrade : une montée de version Symfony orchestrée avec Rector et Claude Code, livrée en Merge Requests relues par un développeur senior.

Écrit par
Flavien Métivier
Expert PHP/Symfony indépendant depuis 2014, ancien CTO de Kartable et d'Extrabat. Plus de 80 projets menés, de l'audit à la migration.
Mon parcours