Symfony 6.4 est sorti le 29 novembre 2023. C’est une LTS avec une histoire : quatre composants qui sont sortis en expérimental dans des versions précédentes sont maintenant stables. Le plus important, c’est AssetMapper.
AssetMapper
La gestion frontend moderne dans Symfony, ça voulait dire Webpack Encore. Encore fonctionne : il gère la transpilation, le bundling, le versioning, le hot reload. Il nécessite aussi Node.js, une étape de build séparée, et une quantité non négligeable de configuration pour ce qui est souvent un frontend assez modeste.
AssetMapper prend une position différente. Les navigateurs modernes supportent les modules ES nativement. Au lieu de bundler, livrer les fichiers tels quels, laisser le navigateur résoudre les imports via une importmap, et gérer les dépendances vendor via des fichiers téléchargés plutôt que des packages npm.
composer require symfony/asset-mapper
php bin/console importmap:require lodash
Pas de Node.js. Pas de npm. Pas d’étape de build. Les fichiers JavaScript et CSS sont versionnés et servis directement, avec un digest dans l’URL pour le cache busting. Pour les applications où le frontend n’est pas la principale préoccupation d’ingénierie, ça supprime toute une chaîne d’outils de l’équation.
6.4 ajoute les fichiers CSS à l’importmap, le préchargement CSS automatique via WebLink, et des commandes pour auditer et mettre à jour les dépendances vendor. L’expérience package.json, sans npm.
Scheduler
Le composant Scheduler (planification de tâches périodiques et de style cron sans runner externe) sort d’expérimental et devient stable. L’API utilise des attributs :
#[AsCronTask('0 * * * *')]
class HourlyReport implements ScheduledTaskInterface
{
public function run(): void { ... }
}
Soutenu par les transports Messenger, les tâches tournent dans tout environnement où un worker est en cours d’exécution. Pour beaucoup de cas d’usage, ça remplace le pattern classique entrée cron + commande console.
Webhook et RemoteEvent
Aussi diplômés d’expérimental : le composant Webhook gère les webhooks entrants depuis des services externes. Au lieu d’écrire des contrôleurs bruts qui parsent les payloads et dispatchent des événements à la main, on configure des parseurs pour des services connus (Stripe, GitHub, Mailgun) et on obtient des événements typés.
DatePoint
Une nouvelle classe DatePoint dans le composant Clock : un wrapper DateTime immutable qui lève des exceptions sur les modificateurs invalides au lieu de retourner silencieusement false. Petite chose, mais significative pour le code qui manipule des dates et veut réellement savoir quand quelque chose va mal.
La fenêtre de support
6.4 LTS reçoit des corrections de bugs jusqu’en novembre 2026 et des correctifs de sécurité jusqu’en novembre 2027. Le chemin de 6.4 vers 7.4 (la prochaine LTS) passe par les notices de dépréciation de 6.4, comme d’habitude.
Routes sans strings magiques
Les alias de routes basés sur le FQCN sont maintenant générés automatiquement. Si une méthode de contrôleur a une seule route, Symfony crée un alias en utilisant son nom de classe complet :
// Auparavant : seul 'blog_index' fonctionnait
// Maintenant : les deux fonctionnent de manière identique
$this->urlGenerator->generate('blog_index');
$this->urlGenerator->generate(BlogController::class.'::index');
Pour les contrôleurs invocables, l’alias est juste le nom de classe. L’avantage pratique : navigation IDE et sécurité au refactoring — on référence une constante de classe, pas une string qui peut silencieusement diverger.
Deux nouveaux attributs DI
#[AutowireLocator] et #[AutowireIterator] rejoignent la famille d’attributs DI. Au lieu de configurer des service locators et des itérables taggués en YAML, on les déclare juste sur les paramètres du constructeur :
public function __construct(
#[AutowireLocator([FooHandler::class, BarHandler::class])]
private ContainerInterface $handlers,
) {}
Alias, services optionnels (préfixés avec ?), et injection de paramètres via SubscribedService sont tous supportés. Le locator charge paresseusement, donc seuls les handlers qu’on appelle vraiment sont instanciés.
Messenger reçoit des handlers intégrés
Trois nouvelles classes de message couvrent des tâches courantes qui nécessitaient auparavant des handlers personnalisés.
RunProcessMessage dispatche une commande Process via le bus. RunCommandMessage fait de même pour les commandes console. Les deux retournent un objet de contexte avec le code de sortie et la sortie. PingWebhookMessage pingue une URL, ce qui est utile pour surveiller les tâches planifiées sans mettre en place un service de health-check dédié :
$this->bus->dispatch(new RunCommandMessage('cache:clear'));
$this->bus->dispatch(new PingWebhookMessage('GET', 'https://healthchecks.io/ping/abc123'));
Le problème d’héritage des sous-processus a aussi été résolu avec PhpSubprocess. Quand on lance PHP avec une limite mémoire personnalisée (-d memory_limit=-1), les processus enfants lancés avec Process ne l’héritent pas. PhpSubprocess le fait :
$sub = new PhpSubprocess(['bin/console', 'app:heavy-import']);
Sécurité : trois corrections pour des situations réelles
Le profiler montre maintenant comment les badges de sécurité ont été résolus pendant l’authentification : lesquels ont passé, lesquels ont échoué, et pourquoi. Avant, il fallait ajouter de la sortie de debug manuellement quand un authentificateur personnalisé ne se comportait pas bien.
Le throttling de login via RateLimiter hache maintenant automatiquement les PII dans les logs. Les adresses IP et les noms d’utilisateur sont hachés avec le secret du kernel avant d’être écrits. Pas de config nécessaire, pas de regex sur les lignes de log.
Les patterns de firewall acceptent maintenant des tableaux :
firewalls:
no_security:
pattern:
- "^/register$"
- "^/api/webhooks/"
Fini les acrobaties regex pour les exclusions multi-chemins.
Déconnexion sans contrôleur bidon
La route de déconnexion nécessitait auparavant un contrôleur qui ne faisait rien que lever une exception, avec un commentaire expliquant que oui, c’est intentionnel. 6.4 élimine ça :
# config/routes/security.yaml
_security_logout:
resource: security.route_loader.logout
type: service
Le route loader s’en occupe. Le contrôleur bidon est parti. Flex met à jour la recette.
Le sérialiseur en meilleure forme
Trois améliorations du sérialiseur qui résolvent chacune un vrai problème.
Attribut #[Groups] au niveau de la classe : appliquer un groupe à la classe entière, puis surcharger par propriété. Utile quand une ressource a un groupe de sérialisation par défaut et quelques champs qui nécessitent un contrôle plus fin.
Les objets translatable ont maintenant un normaliseur dédié. Les strings translatable (enveloppant TranslatableInterface de Doctrine) sont traduites vers la locale passée via NORMALIZATION_LOCALE_KEY pendant la normalisation. Avant ça, il fallait écrire un normaliseur personnalisé.
En mode debug, les erreurs de décodage JSON utilisent maintenant seld/jsonlint pour de meilleurs messages. Au lieu de “Syntax error”, on obtient la ligne et ce qui s’est vraiment passé :
Parse error on line 1: {'foo': 'bar'}
^ Invalid string, used single quotes instead of double quotes
Profilers pour les choses qui n’étaient pas des requêtes HTTP
Le profiler de commande étend le profiler existant aux commandes console. Ajouter --profile à n’importe quelle commande et obtenir une entrée complète dans le profiler : entrée/sortie, temps d’exécution, mémoire, requêtes en base, messages de log. Les commandes qui nécessitaient --verbose plus du timing manuel ont maintenant la même expérience de debug que les requêtes HTTP.
Le profiler de workflow fait de même pour les machines à états. Un nouveau panneau montre une représentation graphique des workflows et les transitions déclenchées pendant la requête. Zéro configuration.
L’accumulation de DX
Plusieurs additions plus petites qui se combinent.
renderBlock() et renderBlockView() sur AbstractController permettent de rendre un bloc Twig nommé et de le retourner comme Response ou string. Pratique pour les réponses Turbo Stream où on veut mettre à jour un fragment sans une action de contrôleur complète.
Le processeur d’env defined retourne un booléen plutôt que la valeur : true si la variable existe et n’est pas vide, false sinon. Utile pour les feature flags pilotés par des variables d’environnement :
parameters:
is_feature_enabled: '%env(defined:FEATURE_FLAG_KEY)%'
HttpClient accepte maintenant max_retries par requête, surchargeant la stratégie globale de retry. La méthode filter() du composant Finder accepte un second argument pour élaguer des répertoires entiers tôt, ce qui compte quand on cherche dans de grands arbres.
La méthode click() de BrowserKit accepte maintenant des paramètres serveur comme en-têtes supplémentaires, utile dans les tests fonctionnels qui doivent simuler des appels API authentifiés en suivant des liens.
L’impersonation devient utilisable dans les templates
Deux nouveaux helpers Twig : impersonation_path() et impersonation_url(). Ils génèrent les URLs correctes incluant le paramètre de query switch-user, qui est configurable et n’a aucune raison d’être codé en dur dans les templates. Les associer avec l’existant impersonation_exit_path() pour le flux complet d’impersonation admin.
Contrôle des locales, partout où ça manquait
Trois lacunes comblées. TemplatedEmail a maintenant une méthode locale() pour rendre les emails dans la langue du destinataire. runWithLocale() du locale switcher passe maintenant la locale comme argument au callback, donc on n’a pas à la capturer depuis la portée extérieure. Et app.enabledLocales est disponible dans Twig, donc on peut construire des sélecteurs de langue sans coder en dur les listes de locales.
Déployer sur des filesystems en lecture seule
APP_BUILD_DIR est maintenant une variable d’environnement reconnue par le kernel. La définir pour rediriger les artefacts compilés (cache du router, proxies Doctrine, traductions préchargées) vers un répertoire qui existe, même quand le répertoire cache par défaut n’existe pas. MicroKernelTrait l’utilise automatiquement. WarmableInterface a reçu un paramètre $buildDir pour supporter cette séparation : les warmers de cache personnalisés qui écrivent des artefacts en lecture seule doivent se mettre à jour en conséquence.