<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Formulaires on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/formulaires/</link><description>Recent content in Formulaires on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Mon, 12 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/formulaires/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 8.0 : PHP 8.4 minimum, objets paresseux natifs et FormFlow</title><link>https://guillaumedelre.github.io/fr/2026/01/12/symfony-8.0-php-8.4-minimum-objets-paresseux-natifs-et-formflow/</link><pubDate>Mon, 12 Jan 2026 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2026/01/12/symfony-8.0-php-8.4-minimum-objets-paresseux-natifs-et-formflow/</guid><description>Part 11 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 8.0 exige PHP 8.4, remplace son générateur de code proxy par des objets paresseux natifs et introduit FormFlow.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 8.0 est sorti le 27 novembre 2025, le même jour que 7.4. Il exige PHP 8.4 et abandonne tout ce qui était déprécié dans 7.4. Les deux changements les plus intéressants sont ce qu&rsquo;il arrête de faire et ce qu&rsquo;il commence à faire avec PHP 8.4.</p>
<h2 id="les-objets-paresseux-natifs">Les objets paresseux natifs</h2>
<p>Le système de proxy de Symfony, utilisé pour l&rsquo;initialisation paresseuse des services et les proxies d&rsquo;entités de Doctrine, a historiquement reposé sur la génération de code. Les classes proxy étaient générées au cache warmup, stockées sous forme de fichiers, et chargées à la demande. Ça fonctionnait, mais ça ajoutait une vraie complexité : des fichiers générés à gérer, un cache à invalider, du code qui ne ressemblait en rien à la classe qu&rsquo;il proxyifiait.</p>
<p>PHP 8.4 a ajouté des objets paresseux natifs. Symfony 8.0 les utilise. Le <code>LazyGhostTrait</code> et le <code>LazyProxyTrait</code> qui alimentaient l&rsquo;ancien système sont supprimés. La création de proxy est maintenant une opération à l&rsquo;exécution soutenue par le moteur lui-même, pas une étape de génération de code.</p>
<p>Pour les développeurs d&rsquo;applications, le changement est essentiellement invisible : les services paresseux fonctionnent toujours. Pour les auteurs de frameworks et bibliothèques, une surface significative de complexité vient de disparaître.</p>
<h2 id="formflow">FormFlow</h2>
<p>Les formulaires multi-étapes ont toujours été un exercice DIY dans Symfony. Gestion de session, suivi des étapes, validation partielle, navigation entre les étapes : chaque projet roulait sa propre solution ou importait un bundle tiers.</p>
<p>8.0 introduit FormFlow : un mécanisme intégré pour les wizards de formulaires multi-étapes. Les étapes sont définies comme une séquence de types de formulaires, la validation partielle est scopée à l&rsquo;étape courante, et la gestion de session est gérée automatiquement.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">#[AsFormFlow]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CheckoutFlow</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">AbstractFormFlow</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">protected</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">defineSteps</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Steps</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Steps</span><span style="color:#f92672">::</span><span style="color:#a6e22e">create</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#39;shipping&#39;</span>, <span style="color:#a6e22e">ShippingType</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#39;payment&#39;</span>, <span style="color:#a6e22e">PaymentType</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#39;review&#39;</span>, <span style="color:#a6e22e">ReviewType</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="la-config-xml-et-php-fluent-supprimées">La config XML et PHP fluent supprimées</h2>
<p>La dépréciation de 7.4 du format de configuration PHP fluent devient une suppression définitive dans 8.0. La configuration XML sort aussi comme format de première classe. Les formats supportés pour la configuration applicative sont maintenant YAML et tableaux PHP. L&rsquo;empreinte rétrécit, mais ce qui reste est genuinement meilleur.</p>
<h2 id="ce-qui-est-supprimé-dautre">Ce qui est supprimé d&rsquo;autre</h2>
<ul>
<li>Le support PHP 8.2 et 8.3 (8.4 minimum)</li>
<li><code>ContainerAwareInterface</code> et <code>ContainerAwareTrait</code></li>
<li>L&rsquo;usage interne de <code>LazyGhostTrait</code> et <code>LazyProxyTrait</code> par Symfony</li>
<li>La surcharge de méthode HTTP pour GET et HEAD (seul POST a du sens sémantiquement)</li>
</ul>
<p>Symfony 8.0 est une rupture propre, et ce genre de rupture ne devient possible que quand le plancher PHP s&rsquo;élève. Les objets paresseux de PHP 8.4 sont l&rsquo;exemple le plus clair : la fonctionnalité existe maintenant dans le langage, donc le framework peut simplement arrêter de l&rsquo;implémenter.</p>
<h2 id="console-devient-plus-ergonomique-pour-les-commandes-invocables">Console devient plus ergonomique pour les commandes invocables</h2>
<p>Les commandes invocables reçoivent une mise à niveau significative. L&rsquo;attribut <code>#[Input]</code> transforme un DTO en bag d&rsquo;arguments/options de la commande. Fini d&rsquo;appeler <code>$input-&gt;getArgument()</code> dans le handler :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">#[AsCommand(name: &#39;app:send-report&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SendReportCommand</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__invoke</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#[Input] SendReportInput $input,
</span></span></span><span style="display:flex;"><span>    )<span style="color:#f92672">:</span> <span style="color:#a6e22e">int</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// $input-&gt;email, $input-&gt;dryRun, etc.
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Command</span><span style="color:#f92672">::</span><span style="color:#a6e22e">SUCCESS</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>BackedEnum</code> est supporté dans les commandes invocables, donc une option déclarée comme enum <code>Status</code> est validée et castée automatiquement. Les commandes interactives reçoivent les attributs <code>#[Interact]</code> et <code>#[Ask]</code> pour déclarer les prompts de questions en ligne. <code>CommandTester</code> fonctionne avec les commandes invocables sans câblage supplémentaire.</p>
<h2 id="le-routing-trouve-ses-propres-contrôleurs">Le Routing trouve ses propres contrôleurs</h2>
<p>Les routes définies via <code>#[Route]</code> sur les classes de contrôleurs sont auto-enregistrées sans avoir besoin d&rsquo;une entrée <code>resource:</code> explicite dans <code>config/routes.yaml</code>. Le tag <code>routing.controller</code> est appliqué automatiquement. On contrôle toujours quels répertoires sont scannés, mais la config YAML rétrécit jusqu&rsquo;à un pointeur vers un répertoire plutôt qu&rsquo;une liste de fichiers manuelle.</p>
<p><code>#[Route]</code> reçoit aussi un paramètre <code>_query</code> pour définir des paramètres de query à la génération, et plusieurs environnements dans l&rsquo;option <code>env</code>.</p>
<h2 id="sécurité--csrf-et-oidc-reçoivent-de-meilleurs-outils">Sécurité : CSRF et OIDC reçoivent de meilleurs outils</h2>
<p><code>#[IsCsrfTokenValid]</code> reçoit un argument <code>$tokenSource</code> pour spécifier d&rsquo;où vient le token (header, cookie, champ de formulaire) plutôt que de s&rsquo;appuyer sur une convention fixe. <code>SameOriginCsrfTokenManager</code> ajoute la validation du header <code>Sec-Fetch-Site</code>, un mécanisme de protection CSRF natif au navigateur qui n&rsquo;a pas besoin d&rsquo;injection de token du tout.</p>
<p>La commande <code>security:oidc-token:generate</code> crée des tokens pour tester les endpoints protégés OIDC en local. Plusieurs endpoints de découverte OIDC sont maintenant supportés, utile dans les setups multi-tenant où chaque tenant a son propre identity provider.</p>
<p>Deux nouvelles fonctions Twig : <code>access_decision()</code> et <code>access_decision_for_user()</code> exposent le résultat du voter d&rsquo;autorisation dans les templates sans passer par la façade de sécurité. <code>#[IsGranted]</code> peut être sous-classé pour les patterns d&rsquo;autorisation répétés qui méritent leur propre attribut nommé.</p>
<h2 id="objectmapper-et-jsonstreamer-sortent-dexpérimental">ObjectMapper et JsonStreamer sortent d&rsquo;expérimental</h2>
<p>Les deux composants introduits dans 7.x sont stables dans 8.0. <code>ObjectMapper</code> mappe entre objets sans transformateurs écrits à la main, via une configuration basée sur des attributs. <code>JsonStreamer</code> lit et écrit de grands JSON sans charger le document entier en mémoire, et il supporte maintenant les propriétés synthétiques : des champs virtuels calculés à la sérialisation.</p>
<p><code>JsonStreamer</code> abandonne aussi sa dépendance sur <code>nikic/php-parser</code>. La génération de code pour le reader/writer utilise maintenant un mécanisme interne plus simple, supprimant une lourde dépendance de dev.</p>
<h2 id="uid-par-défaut-vers-uuidv7">Uid par défaut vers UUIDv7</h2>
<p><code>UuidFactory</code> génère maintenant UUIDv7 par défaut au lieu d&rsquo;UUIDv4. La différence : v7 est ordonné dans le temps, donc les UUIDs générés se trient chronologiquement. Ça compte beaucoup pour la performance des index de base de données. <code>MockUuidFactory</code> fournit une génération déterministe d&rsquo;UUID dans les tests.</p>
<h2 id="yaml-lève-une-erreur-sur-les-clés-dupliquées">Yaml lève une erreur sur les clés dupliquées</h2>
<p>Auparavant, un fichier YAML avec deux clés identiques gardait silencieusement la dernière. 8.0 lève une erreur de parsing. Ça attrape de vrais bugs : les clés dupliquées dans <code>services.yaml</code> ou <code>config/packages/*.yaml</code> sont presque toujours des erreurs de copier-coller et on veut définitivement être informé.</p>
<h2 id="validator--contrainte-video-et-protocoles-wildcard">Validator : contrainte Video et protocoles wildcard</h2>
<p>Une contrainte <code>Video</code> rejoint la contrainte <code>Image</code> pour valider les fichiers vidéo uploadés (type MIME, durée, codec). La contrainte <code>Url</code> accepte <code>protocols: ['*']</code> pour autoriser n&rsquo;importe quel schéma conforme à la RFC 3986, utile pour stocker des URLs arbitraires qui incluent <code>git+ssh://</code>, <code>file://</code>, ou des schémas d&rsquo;application personnalisés.</p>
<h2 id="messenger--retry-natif-sqs-et-nouveaux-événements">Messenger : retry natif SQS et nouveaux événements</h2>
<p>Le transport SQS peut maintenant utiliser sa propre configuration native de retry et de dead-letter queue au lieu du middleware de retry de Symfony. Pour les queues à haut volume sur AWS, ça supprime un aller-retour par PHP pour les échecs transitoires. Un <code>MessageSentToTransportsEvent</code> se déclenche après qu&rsquo;un message est dispatché, portant des informations sur quels transports l&rsquo;ont réellement reçu.</p>
<p><code>messenger:consume</code> reçoit <code>--exclude-receivers</code> pour se combiner avec <code>--all</code>.</p>
<h2 id="mailer--transport-microsoft-graph">Mailer : transport Microsoft Graph</h2>
<p>Un nouveau transport envoie des emails via l&rsquo;API Microsoft Graph, ce que Microsoft recommande pour les applications sur Azure Active Directory ces jours-ci. Les autres options (relai SMTP, Exchange EWS) fonctionnent encore, mais Graph est le bon choix pour les nouveaux déploiements Azure.</p>
<h2 id="workflow--transitions-pondérées">Workflow : transitions pondérées</h2>
<p>Les transitions peuvent maintenant déclarer des poids. Quand plusieurs transitions sont activées depuis la même place, celle avec le poids le plus élevé gagne. Ça permet d&rsquo;exprimer la priorité directement dans la définition du workflow sans ajouter un guard qui lit un compteur externe.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">return</span> (<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Definition</span>(<span style="color:#a6e22e">states</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;draft&#39;</span>, <span style="color:#e6db74">&#39;review&#39;</span>, <span style="color:#e6db74">&#39;published&#39;</span>]))
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">addTransition</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Transition</span>(<span style="color:#e6db74">&#39;publish&#39;</span>, <span style="color:#e6db74">&#39;review&#39;</span>, <span style="color:#e6db74">&#39;published&#39;</span>, <span style="color:#a6e22e">weight</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">10</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">addTransition</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Transition</span>(<span style="color:#e6db74">&#39;reject&#39;</span>, <span style="color:#e6db74">&#39;review&#39;</span>, <span style="color:#e6db74">&#39;draft&#39;</span>, <span style="color:#a6e22e">weight</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">1</span>));
</span></span></code></pre></div><h2 id="lock--lockkeynormalizer">Lock : LockKeyNormalizer</h2>
<p><code>LockKeyNormalizer</code> normalise une clé de lock vers une string cohérente avant le hachage. Utile quand la clé est dérivée d&rsquo;entrées utilisateur ou de données externes qui peuvent varier en espaces blancs ou casse : le normalizer s&rsquo;assure que la même clé logique correspond toujours au même lock.</p>
<h2 id="httpfoundation--méthode-query-et-parsing-de-corps-plus-propre">HttpFoundation : méthode QUERY et parsing de corps plus propre</h2>
<p>La méthode IETF <code>QUERY</code> (une méthode sûre et idempotente avec un corps, contrairement à <code>GET</code>) est maintenant supportée partout dans la stack : <code>Request</code>, cache HTTP, WebProfiler et HttpClient. Si on construit des APIs de recherche qui nécessitent un corps de requête structuré et veulent aussi du cache, <code>QUERY</code> est le bon choix sémantique.</p>
<p><code>Request::createFromGlobals()</code> parse maintenant automatiquement le corps des requêtes <code>PUT</code>, <code>DELETE</code>, <code>PATCH</code> et <code>QUERY</code>.</p>
<h2 id="config--schéma-json-pour-la-validation-yaml">Config : schéma JSON pour la validation YAML</h2>
<p>Symfony 8.0 auto-génère un fichier JSON Schema pour chaque section de configuration. Les IDEs qui supportent JSON Schema pour les fichiers YAML (VS Code, PhpStorm) peuvent maintenant valider <code>config/packages/*.yaml</code> contre ces schémas et fournir de l&rsquo;autocomplétion sans plugin. Le schéma est généré pendant le cache warmup et placé dans <code>config/reference.php</code>.</p>
<h2 id="runtime--auto-détection-frankenphp">Runtime : auto-détection FrankenPHP</h2>
<p>Le composant Runtime détecte FrankenPHP automatiquement et active le mode worker sans package supplémentaire ni variable d&rsquo;environnement. Si <code>$_SERVER['APP_RUNTIME']</code> est défini, cette classe de runtime a la priorité. On peut aussi choisir le renderer d&rsquo;erreurs basé sur <code>APP_RUNTIME_MODE</code>, ce qui est utile quand on fait tourner la même codebase dans des contextes HTTP et CLI avec des besoins de présentation d&rsquo;erreurs différents.</p>
]]></content:encoded></item></channel></rss>