<?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>Secrets on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/secrets/</link><description>Recent content in Secrets on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Thu, 14 May 2026 15:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/secrets/index.xml" rel="self" type="application/rss+xml"/><item><title>Ce qui survit au build</title><link>https://guillaumedelre.github.io/fr/2026/05/14/ce-qui-survit-au-build/</link><pubDate>Thu, 14 May 2026 15:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2026/05/14/ce-qui-survit-au-build/</guid><description>Part 2 of 8 in &amp;quot;Symfony vers le Cloud : Douze Facteurs, Treize Services&amp;quot;: Comment des fichiers .env committés alimentent un build qui grave les credentials dans les layers Docker — et ce qu&amp;#39;il faut pour vider le fichier jusqu&amp;#39;à quatre lignes.</description><category>symfony-to-the-cloud</category><content:encoded><![CDATA[<p>À un moment de l&rsquo;audit de migration cloud, quelqu&rsquo;un a lancé ça :</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-bash" data-lang="bash"><span style="display:flex;"><span>docker run --rm &lt;image&gt; php -r <span style="color:#e6db74">&#34;var_dump(require &#39;.env.local.php&#39;);&#34;</span>
</span></span></code></pre></div><p>La sortie montrait tout ce que <code>composer dump-env prod</code> avait compilé dans l&rsquo;image au moment du build. Ce qui voulait dire tout ce qui se trouvait dans le fichier <code>.env</code> quand l&rsquo;image avait été construite. Ce qui voulait dire, entre autres, ça :</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">INFLUXDB_INIT_ADMIN_TOKEN=&lt;influxdb-admin-token&gt;
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=admin123
BLACKFIRE_CLIENT_ID=&lt;blackfire-client-id&gt;
BLACKFIRE_CLIENT_TOKEN=&lt;blackfire-client-token&gt;
BLACKFIRE_SERVER_ID=&lt;blackfire-server-id&gt;
BLACKFIRE_SERVER_TOKEN=&lt;blackfire-server-token&gt;
NGROK_AUTHTOKEN=replace-me-optionnal
</code></pre><p>Vingt-cinq variables au total. Chaque credential accumulé dans le <code>.env</code> racine sur trois ans, désormais permanent dans un layer d&rsquo;image.</p>
<h2 id="comment-dump-env-fonctionne">Comment <code>dump-env</code> fonctionne</h2>
<p><code>composer dump-env prod</code> est une optimisation Symfony légitime. Au lieu de parser les fichiers <code>.env</code> à chaque requête, le runtime charge un tableau PHP pré-compilé depuis <code>.env.local.php</code>. Plus rapide et plus simple.</p>
<p>Le problème, c&rsquo;est ce qu&rsquo;il lit. Le Dockerfile copie le dépôt dans l&rsquo;image avec <code>COPY . ./</code>, <code>.env</code> inclus. Ensuite <code>dump-env prod</code> lit ce fichier et compile chaque variable dans <code>.env.local.php</code>. L&rsquo;image est livrée avec une capture figée des credentials qui se trouvaient dans <code>.env</code> au moment du build.</p>
<p>Les layers Docker sont des archives immuables. Même si une étape ultérieure supprimait <code>.env</code> du système de fichiers du container, le layer qui le contient existerait toujours dans l&rsquo;image. <code>docker save &lt;image&gt;</code> produit une archive tar de chaque layer ; extraire un fichier spécifique de n&rsquo;importe quel point de l&rsquo;historique de build est une opération simple. Les credentials sont invisibles à l&rsquo;exécution. Ils ne sont pas partis.</p>
<p>Le <a href="https://12factor.net/build-release-run" target="_blank" rel="noopener noreferrer">Facteur V</a>
 est explicite là-dessus : un artefact de build doit être agnostique à l&rsquo;environnement, la config arrivant à l&rsquo;étape de release depuis l&rsquo;extérieur. Dès que des credentials sont compilés dedans, l&rsquo;image n&rsquo;est plus portable. On ne peut plus la promouvoir entre environnements. On builde deux fois en espérant que le deuxième se comporte comme le premier.</p>
<h2 id="comment-vingt-cinq-variables-saccumulent">Comment vingt-cinq variables s&rsquo;accumulent</h2>
<p>Avant de voir comment on a réparé ça, il vaut la peine de comprendre comment on en est arrivé là.</p>
<p>Les tokens <code>BLACKFIRE_*</code> sont le cas facile à comprendre. Un membre de l&rsquo;équipe configure le profiling, a besoin de partager la configuration, et le dépôt est déjà ouvert à tout le monde. Une ligne dans <code>.env</code> est la voie de moindre résistance. Les credentials InfluxDB et Grafana suivent la même logique — outillage partagé, dépôt partagé, un commit.</p>
<p>Puis il y a les variables qui révèlent une autre dérive. Dans certains <code>.env</code> de services :</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">APP__RATINGS__SERIALS=&#39;{&#34;marque1&#34;:{&#34;fr&#34;:&#34;12345&#34;},...}&#39;  # ~40 lignes de JSON
APP__YOUTUBE__CREDENTIALS=&#39;{&#34;marque1&#34;:{&#34;client_id&#34;:&#34;xxx&#34;,&#34;refresh_token&#34;:&#34;yyy&#34;},...}&#39;
</code></pre><p>Des numéros de série pour la mesure d&rsquo;audience. Des refresh tokens YouTube par marque. Ce ne sont pas des secrets au sens des tokens Blackfire. Ce sont des données métier — le genre de valeurs qui varient entre marques et environnements, que quelqu&rsquo;un a décidé de versionner dans <code>.env</code> parce qu&rsquo;elles se comportaient comme de la configuration et que <code>.env</code> était l&rsquo;endroit où vivait la configuration.</p>
<p>Vingt-cinq variables, c&rsquo;est la somme de décisions incrémentales, dont aucune ne semblait fausse isolément. Le problème est structurel : quand <code>.env</code> est la seule réponse disponible, tout finit par y ressembler.</p>
<h2 id="où-les-choses-appartiennent-vraiment">Où les choses appartiennent vraiment</h2>
<p>Vider le fichier exigeait de répondre à une question pour chaque variable : <em>où est-ce que ça appartient vraiment ?</em></p>
<p>Les réponses ont révélé trois catégories que l&rsquo;équipe n&rsquo;avait jamais explicitement nommées :</p>
<p><strong>La config statique</strong> vit dans le code. Règles métier, logique de routing, fichiers de paramètres Symfony — tout ce qui ne varie pas entre les déploiements. Un changement exige un rebuild. Les blocs JSON de numéros de série se sont révélés ne pas être de la config statique du tout : ils étaient interrogés depuis un service Config dédié à l&rsquo;exécution. Ils n&rsquo;avaient rien à faire dans un fichier.</p>
<p><strong>La config environnementale</strong> varie entre les déploiements : hostnames, chaînes de connexion, credentials de services tiers. C&rsquo;est ce que le <a href="https://12factor.net/config" target="_blank" rel="noopener noreferrer">Facteur III</a>
 désigne par &ldquo;config dans les variables d&rsquo;environnement&rdquo; — de vraies variables au niveau OS, injectées à l&rsquo;exécution, jamais des fichiers qui voyagent avec le code. Dans Kubernetes, c&rsquo;est un ConfigMap pour les valeurs non sensibles et un Kubernetes Secret pour les credentials. Le choix retenu pour les secrets a été SOPS — les credentials sont chiffrés et committés dans git, plutôt que stockés dans un coffre-fort externe comme Azure Key Vault ou HashiCorp Vault. Un coffre-fort échange la simplicité contre l&rsquo;auditabilité : rotation automatique, logs d&rsquo;audit centralisés, accès via workload identity sans clé à protéger. SOPS échange ces capacités contre un modèle opérationnel plus simple — pas de service externe à interroger au déploiement, les secrets transitent par le processus de review normal du code, l&rsquo;historique git fait office de piste d&rsquo;audit. Les contreparties acceptées sont la rotation manuelle et la responsabilité de protéger la clé de déchiffrement elle-même. Pour la taille de l&rsquo;équipe, le compromis était délibéré.</p>
<p><strong>La config dynamique</strong> change sans déploiement : paramètres éditoriaux, seuils par marque, configuration de modération de contenu. Elle appartient à une base de données, gérée via le service Config de l&rsquo;application. Une partie de ce qui s&rsquo;était accumulé dans les <code>.env</code> de services était cette catégorie depuis le début, passant pour des valeurs par défaut statiques parce qu&rsquo;elle changeait assez rarement pour que personne ne le remarque.</p>
<p>Une fois les catégories nommées, les variables se sont triées. Le <code>.env</code> racine est arrivé à quatre lignes :</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">DOMAIN=platform.127.0.0.1.sslip.io
XDEBUG_MODE=off
SERVER_NAME=:80
APP_ENV=dev
</code></pre><p>Des valeurs par défaut sûres. Rien de sensible. <code>dump-env prod</code> compile maintenant des chaînes vides ; les vraies valeurs arrivent à l&rsquo;exécution depuis Kubernetes.</p>
<h2 id="limage-postgresql">L&rsquo;image PostgreSQL</h2>
<p>L&rsquo;image PostgreSQL utilisée en CI a un mot de passe codé en dur :</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-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> <span style="color:#e6db74">postgres:15</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">ENV</span> POSTGRES_PASSWORD<span style="color:#f92672">=</span>admin123
</span></span></code></pre></div><p>Ça ressemble au même problème. Ce n&rsquo;en est pas un, parce que le modèle de menace est différent. La base CI est éphémère — elle existe le temps d&rsquo;un run de pipeline, ne contient pas de vraies données, tourne dans un réseau isolé. Un mot de passe codé en dur sur une base de test jetable est un risque acceptable, pas une entorse à la règle.</p>
<p>En production, la question ne se pose pas : la plateforme utilise Azure Flexible Server, un service PostgreSQL managé. Il n&rsquo;y a pas d&rsquo;image Docker. Les credentials arrivent via injection dans les charts Helm, sans jamais toucher un layer.</p>
<h2 id="ce-qui-survit-au-build-maintenant">Ce qui survit au build maintenant</h2>
<p>L&rsquo;image qui part en production contient maintenant une garantie : <code>var_dump(require '.env.local.php')</code> ne retourne que des chaînes vides et des valeurs par défaut sûres. Les credentials ne sont pas là parce qu&rsquo;ils n&rsquo;y ont jamais été mis — ils arrivent à l&rsquo;exécution, depuis l&rsquo;extérieur.</p>
<p>C&rsquo;est la frontière de responsabilité que <code>dump-env</code> avait silencieusement effacée : l&rsquo;image est l&rsquo;application, le runtime est l&rsquo;environnement. Ils ne devraient pas connaître les secrets de l&rsquo;autre.</p>
]]></content:encoded></item><item><title>Symfony 5.0 : String, Notifier et le coffre-fort de secrets</title><link>https://guillaumedelre.github.io/fr/2020/01/06/symfony-5.0-string-notifier-et-le-coffre-fort-de-secrets/</link><pubDate>Mon, 06 Jan 2020 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2020/01/06/symfony-5.0-string-notifier-et-le-coffre-fort-de-secrets/</guid><description>Part 5 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 5.0 ajoute un composant String Unicode-aware, un Notifier multi-canal, et un coffre-fort de secrets intégré.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 5.0 est sorti le 21 novembre 2019, le même jour que la 4.4. Là où la 4.4 mise sur la stabilité et une longue fenêtre de support, la 5.0 ouvre un nouveau chapitre : plus de code déprécié, PHP 7.2.5 minimum, et quelques nouveaux composants qui comblent enfin des lacunes accumulées depuis des années.</p>
<h2 id="le-composant-string">Le composant String</h2>
<p>La gestion des chaînes en PHP est notoirement éparpillée : des fonctions avec préfixe par-ci (<code>str_</code>), avec suffixe par-là (<code>strpos</code>), un support d&rsquo;encodage incohérent, et rien d&rsquo;orienté objet en vue. Le composant String enveloppe tout ça dans une API fluide orientée objet avec support Unicode :</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">use</span> <span style="color:#a6e22e">Symfony\Component\String\UnicodeString</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$str <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">UnicodeString</span>(<span style="color:#e6db74">&#39;  Hello World  &#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $str<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">trim</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">lower</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">replace</span>(<span style="color:#e6db74">&#39; &#39;</span>, <span style="color:#e6db74">&#39;-&#39;</span>); <span style="color:#75715e">// hello-world
</span></span></span></code></pre></div><p>L&rsquo;ajout pratique, c&rsquo;est le <code>Slugger</code>, un générateur de slug locale-aware qui gère vraiment correctement les caractères accentués :</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>$slug <span style="color:#f92672">=</span> $slugger<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">slug</span>(<span style="color:#e6db74">&#39;L\&#39;été à Montréal&#39;</span>); <span style="color:#75715e">// l-ete-a-montreal
</span></span></span></code></pre></div><p>Avant, il fallait intégrer une bibliothèque tierce ou en écrire une soi-même. Maintenant ça ship avec FrameworkBundle, disponible par défaut.</p>
<h2 id="notifier">Notifier</h2>
<p>Le courrier électronique est géré par Mailer. SMS, notifications push, messages de chat : pas de solution first-party, jusqu&rsquo;à maintenant. Le composant Notifier en ajoute une : une interface unifiée sur des dizaines de canaux et fournisseurs.</p>
<p>La même notification peut atterrir sur Slack, déclencher un SMS via Twilio, ou finir comme notification push, tout configuré via des DSN. Ajouter un nouveau canal, c&rsquo;est un changement de config, pas un changement de code.</p>
<h2 id="le-coffre-fort-de-secrets">Le coffre-fort de secrets</h2>
<p>Stocker des secrets dans des fichiers <code>.env</code> fonctionne, mais les valeurs sont en clair, les environnements partagés sont une galère, et il n&rsquo;y a aucun moyen natif de chiffrer quoi que ce soit au repos.</p>
<p>Symfony 5.0 ajoute une famille de commandes <code>secrets:</code> et un mécanisme de coffre-fort. Les secrets sont chiffrés avec une paire de clés stockée hors du dépôt. Les fichiers chiffrés sont commités ; la clé de déchiffrement ne l&rsquo;est pas. En production, la clé arrive comme variable d&rsquo;environnement ou est injectée depuis un gestionnaire de secrets.</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-bash" data-lang="bash"><span style="display:flex;"><span>php bin/console secrets:set DATABASE_PASSWORD
</span></span><span style="display:flex;"><span>php bin/console secrets:decrypt-to-local --force
</span></span></code></pre></div><p>Pas une solution de gestion de secrets à part entière, mais un vrai pas en avant par rapport à un fichier <code>.env</code> en clair qui traîne non chiffré dans le dépôt.</p>
<h2 id="mailer-reçoit-une-couche-de-notification">Mailer reçoit une couche de notification</h2>
<p>Le composant Mailer est arrivé en 4.4. Ce que la 5.0 ajoute par-dessus, c&rsquo;est la <code>NotificationEmail</code> : un email pré-stylisé et responsive construit sur Foundation for Emails, avec une API explicite pour les niveaux d&rsquo;importance et les boutons d&rsquo;appel à l&rsquo;action :</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">use</span> <span style="color:#a6e22e">Symfony\Bridge\Twig\Mime\NotificationEmail</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$email <span style="color:#f92672">=</span> (<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">NotificationEmail</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">from</span>(<span style="color:#e6db74">&#39;alerts@example.com&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">to</span>(<span style="color:#e6db74">&#39;admin@example.com&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">subject</span>(<span style="color:#e6db74">&#39;Disk usage critical&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">markdown</span>(<span style="color:#e6db74">&#39;The disk on **prod-01** is at 94%. Check it now.&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">action</span>(<span style="color:#e6db74">&#39;Open dashboard&#39;</span>, <span style="color:#e6db74">&#39;https://example.com/servers&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">importance</span>(<span style="color:#a6e22e">NotificationEmail</span><span style="color:#f92672">::</span><span style="color:#a6e22e">IMPORTANCE_URGENT</span>);
</span></span></code></pre></div><p>Pas de template à écrire, pas de CSS inline à dompter. Pour les alertes transactionnelles, les notifications de facturation et les emails système, ça couvre 80 % de ce dont on a besoin sans toucher à quoi que ce soit.</p>
<h2 id="les-firewalls-paresseux-et-le-problème-de-cache">Les firewalls paresseux et le problème de cache</h2>
<p>Chaque firewall stateful dans Symfony charge l&rsquo;utilisateur depuis la session à chaque requête, que l&rsquo;action en ait besoin ou non. Ce qui signifie que toute réponse est non-cacheable par défaut, même pour des pages qui ne touchent jamais à <code>$this-&gt;getUser()</code>.</p>
<p>La 5.0 ajoute le mode <code>lazy</code> pour les firewalls, qui diffère l&rsquo;accès à la session jusqu&rsquo;à ce que le code appelle réellement <code>is_granted()</code> ou accède au token utilisateur :</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-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># config/packages/security.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">security</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">firewalls</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">main</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">pattern</span>: <span style="color:#ae81ff">^/</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">anonymous</span>: <span style="color:#ae81ff">lazy</span>
</span></span></code></pre></div><p>Les pages qui n&rsquo;ont pas besoin de l&rsquo;utilisateur redeviennent cacheables. Les nouveaux projets obtiennent ça par défaut via la recette Flex ; les existants ont besoin d&rsquo;un changement de config en une ligne.</p>
<h2 id="les-migrations-de-mots-de-passe-sans-grand-soir">Les migrations de mots de passe sans grand soir</h2>
<p>Migrer une app en production de bcrypt vers argon2id impliquait jusqu&rsquo;ici de forcer une réinitialisation du mot de passe pour chaque utilisateur. Le <code>PasswordUpgraderInterface</code> rend ça progressif : à la connexion, Symfony vérifie si le hash stocké correspond à l&rsquo;algorithme courant. Sinon, il le re-hash sur place et appelle votre upgrader pour le sauvegarder :</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">// src/Repository/UserRepository.php
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UserRepository</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">ServiceEntityRepository</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">PasswordUpgraderInterface</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">upgradePassword</span>(<span style="color:#a6e22e">UserInterface</span> $user, <span style="color:#a6e22e">string</span> $newHashedPassword)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        $user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setPassword</span>($newHashedPassword);
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getEntityManager</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">flush</span>();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Associez ça à <code>algorithm: auto</code> dans la config de l&rsquo;encodeur, et les anciens hashs migrent silencieusement à mesure que les utilisateurs se connectent. Pas de script de migration, pas de downtime, pas de friction pour l&rsquo;utilisateur.</p>
<h2 id="errorhandler-remplace-debug">ErrorHandler remplace Debug</h2>
<p>Le composant Debug est parti. Son remplaçant, ErrorHandler, fait le même travail (convertir les erreurs PHP en exceptions, afficher de belles pages d&rsquo;erreur) mais sans nécessiter Twig. Pour les apps API qui ne rendent jamais de HTML, ça compte : ErrorHandler génère les erreurs dans le format de la requête (JSON, XML, texte) en suivant RFC 7807 :</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-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;Not Found&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;status&#34;</span>: <span style="color:#ae81ff">404</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;detail&#34;</span>: <span style="color:#e6db74">&#34;Sorry, the page you are looking for could not be found&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>La config de routing passe de <code>TwigBundle</code> à <code>FrameworkBundle</code>, et c&rsquo;est la seule étape de migration pour la plupart des projets. Une ligne, c&rsquo;est fait.</p>
<h2 id="les-listeners-dévénements-enfin-moins-verbeux">Les listeners d&rsquo;événements, enfin moins verbeux</h2>
<p>Enregistrer un listener d&rsquo;événement kernel impliquait auparavant de nommer explicitement l&rsquo;événement dans le tag de service. Symfony 5.0 l&rsquo;infère depuis la signature de méthode :</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">// Pas de configuration de tag au-delà de kernel.event_listener
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">final</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SecurityListener</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">onKernelRequest</span>(<span style="color:#a6e22e">RequestEvent</span> $event)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Symfony lit le type hint et détermine l&#39;événement
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><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-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># config/services.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">App\EventListener\SecurityListener</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">tags</span>: [<span style="color:#ae81ff">kernel.event_listener]</span>
</span></span></code></pre></div><p>Utilisez <code>__invoke()</code> et ça fonctionne de la même façon. Enregistrez en masse tout un répertoire de listeners avec un seul bloc resource, et Symfony détermine quel événement chacun gère.</p>
<h2 id="httpclient-grandit">HttpClient grandit</h2>
<p>Le composant HttpClient est arrivé en 4.4 comme stable. La 5.0 ajoute quelques choses utiles par-dessus :</p>
<p>L&rsquo;authentification NTLM pour les environnements d&rsquo;entreprise, le buffering conditionnel via un callback (bufferiser les grandes réponses seulement quand le content-type correspond), une option <code>max_duration</code> qui plafonne le temps total de requête indépendamment des conditions réseau, et <code>toStream()</code> pour transformer n&rsquo;importe quelle réponse en un flux PHP standard pour le code qui attend du <code>fread()</code> :</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>$response <span style="color:#f92672">=</span> $client<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">request</span>(<span style="color:#e6db74">&#39;GET&#39;</span>, <span style="color:#e6db74">&#39;https://api.example.com/large-export&#39;</span>, [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;max_duration&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">30.0</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;buffer&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">fn</span>(<span style="color:#66d9ef">array</span> $headers)<span style="color:#f92672">:</span> <span style="color:#a6e22e">bool</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">str_contains</span>($headers[<span style="color:#e6db74">&#39;content-type&#39;</span>][<span style="color:#ae81ff">0</span>] <span style="color:#f92672">??</span> <span style="color:#e6db74">&#39;&#39;</span>, <span style="color:#e6db74">&#39;json&#39;</span>),
</span></span><span style="display:flex;"><span>]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Le streamer plutôt que de tout charger en mémoire
</span></span></span><span style="display:flex;"><span>$stream <span style="color:#f92672">=</span> $response<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">toStream</span>();
</span></span></code></pre></div><p>Le client a aussi obtenu une interopérabilité complète avec PSR-18 et HTTPlug v1/v2, donc toute bibliothèque qui dépend de ces abstractions fonctionne directement avec lui.</p>
<h2 id="ce-que-la-50-supprime">Ce que la 5.0 supprime</h2>
<p>La 5.0 abandonne tout ce qui était déprécié en 4.4. Les plus notables :</p>
<ul>
<li><code>WebServerBundle</code> (utilisez <code>symfony server:start</code> depuis l&rsquo;outil CLI à la place)</li>
<li>L&rsquo;<code>AnonymousToken</code> de l&rsquo;ancien système de sécurité (remplacé par <code>NullToken</code>)</li>
<li>Les anciens noms d&rsquo;événements de formulaire</li>
<li>Le ClassLoader interne de Symfony</li>
<li>Le composant Debug (remplacé par ErrorHandler)</li>
</ul>
<p>Si vous avez fait tourner votre app 4.4 avec les notices de dépréciation actives et corrigé les avertissements, la mise à niveau vers la 5.0 ne nécessite aucun changement de code.</p>
]]></content:encoded></item></channel></rss>