<?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>Filesystem on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/filesystem/</link><description>Recent content in Filesystem on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Wed, 12 Jan 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/filesystem/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 6.0 : PHP 8.1 uniquement, et le système de sécurité reconstruit</title><link>https://guillaumedelre.github.io/fr/2022/01/12/symfony-6.0-php-8.1-uniquement-et-le-syst%C3%A8me-de-s%C3%A9curit%C3%A9-reconstruit/</link><pubDate>Wed, 12 Jan 2022 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2022/01/12/symfony-6.0-php-8.1-uniquement-et-le-syst%C3%A8me-de-s%C3%A9curit%C3%A9-reconstruit/</guid><description>Part 7 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 6.0 exige PHP 8.1, supprime l&amp;#39;ancien système de sécurité et reconstruit l&amp;#39;authentification sur des fondations plus propres.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 6.0 est sorti le 29 novembre 2021. La caractéristique définissante : PHP 8.1 est le minimum. Pas supporté, requis. L&rsquo;équipe de releases a attendu que PHP 8.1 sorte, puis a coupé Symfony 6.0 le lendemain.</p>
<p>Ce n&rsquo;est pas juste un bump de version. C&rsquo;est un engagement à construire contre le langage actuel plutôt que le plancher historique.</p>
<h2 id="le-système-de-sécurité-enfin-reconstruit">Le système de sécurité, enfin reconstruit</h2>
<p>Le composant de sécurité Symfony a deux systèmes. L&rsquo;ancien (<code>AnonymousToken</code>, <code>GuardAuthenticatorInterface</code>, un enchevêtrement d&rsquo;interfaces qui vous faisaient implémenter des méthodes dont vous n&rsquo;aviez pas besoin) avait été déprécié. 6.0 le supprime entièrement.</p>
<p>Le nouveau système de sécurité (<code>security.enable_authenticator_manager: true</code> en 5.x) est maintenant le seul système. C&rsquo;est plus propre : une seule interface à implémenter, séparation claire entre authentification et autorisation, vérification des credentials basée sur des passeports. La migration depuis les anciens guard authenticators n&rsquo;est pas indolore, mais la destination est beaucoup moins confuse.</p>
<h2 id="la-classe-path-du-filesystem">La classe Path du Filesystem</h2>
<p>Travailler avec des chemins de fichiers en PHP est fondamentalement un problème de manipulation de strings. <code>__DIR__</code>, concaténation, <code>realpath()</code>, séparateurs spécifiques à la plateforme : la bibliothèque standard donne des primitives mais pas vraiment un modèle.</p>
<p>La nouvelle classe <code>Path</code> gère ç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-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">Symfony\Component\Filesystem\Path</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#39;/var/www&#39;</span>, <span style="color:#e6db74">&#39;html&#39;</span>, <span style="color:#e6db74">&#39;../uploads&#39;</span>); <span style="color:#75715e">// /var/www/uploads
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">makeRelative</span>(<span style="color:#e6db74">&#39;/var/www/html&#39;</span>, <span style="color:#e6db74">&#39;/var/www&#39;</span>); <span style="color:#75715e">// html
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">isAbsolute</span>(<span style="color:#e6db74">&#39;./relative/path&#39;</span>); <span style="color:#75715e">// false
</span></span></span></code></pre></div><p>Multiplateforme, sans effets de bord, sans accès au filesystem nécessaire. Aussi dans 6.0 : support des patterns <code>.gitignore</code> imbriqués dans Finder.</p>
<h2 id="les-enums-dans-le-système-de-formulaires">Les enums dans le système de formulaires</h2>
<p>En s&rsquo;appuyant sur les fondations posées par 5.4, 6.0 pousse le support des enums plus loin. Les valeurs <code>BackedEnum</code> font des allers-retours à travers les formulaires et le sérialiseur sans transformateurs personnalisés. Le composant de formulaire comprend les cases d&rsquo;enum comme options de choix nativement.</p>
<h2 id="ce-que-60-supprime">Ce que 6.0 supprime</h2>
<p>La liste des suppressions est extensive : l&rsquo;ancien système de sécurité, le composant <code>Templating</code>, le support des annotations PHP (remplacées par les attributs natifs), le support du cache Doctrine, <code>ContainerAwareTrait</code>. Six années de marqueurs <code>@deprecated</code> accumulés, finalement nettoyés.</p>
<p>Les applications qui avaient pris au sérieux les warnings de dépréciation de 5.4 avaient un chemin de migration propre. Celles qui ne l&rsquo;avaient pas fait avaient du travail à faire.</p>
<h2 id="la-complétion-automatique-était-toujours-le-manque">La complétion automatique était toujours le manque</h2>
<p>Le composant Console a reçu l&rsquo;autocomplétion shell, et c&rsquo;est proprement intégré : définir une méthode <code>complete()</code> sur sa commande, et Tab dans Bash suggère des valeurs valides pour les options et arguments.</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">class</span> <span style="color:#a6e22e">DeployCommand</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Command</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">complete</span>(<span style="color:#a6e22e">CompletionInput</span> $input, <span style="color:#a6e22e">CompletionSuggestions</span> $suggestions)<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:#66d9ef">if</span> ($input<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">mustSuggestOptionValuesFor</span>(<span style="color:#e6db74">&#39;env&#39;</span>)) {
</span></span><span style="display:flex;"><span>            $suggestions<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">suggestValues</span>([<span style="color:#e6db74">&#39;prod&#39;</span>, <span style="color:#e6db74">&#39;staging&#39;</span>, <span style="color:#e6db74">&#39;dev&#39;</span>]);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Toutes les commandes Symfony intégrées ont reçu la complétion aussi : <code>debug:router</code>, <code>cache:pool:clear</code>, <code>lint:yaml</code>, et une quinzaine d&rsquo;autres. Exécuter <code>bin/console completion bash &gt;&gt; ~/.bashrc</code> et c&rsquo;est terminé.</p>
<h2 id="messenger-maintenant-avec-attributs-et-traitement-par-lots">Messenger, maintenant avec attributs et traitement par lots</h2>
<p>L&rsquo;attribut <code>#[AsMessageHandler]</code> remplace l&rsquo;ancienne <code>MessageHandlerInterface</code>. Moins de boilerplate, et on peut maintenant configurer l&rsquo;affinité de transport et la priorité directement sur l&rsquo;attribut :</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">#[AsMessageHandler(fromTransport: &#39;async&#39;, priority: 10)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SendWelcomeEmailHandler</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 style="color:#a6e22e">UserRegistered</span> $message)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#f92672">...</span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>L&rsquo;autre ajout significatif : <code>BatchHandlerInterface</code>. Quand on insère un millier de lignes, traiter les messages un par un est du gaspillage. Les batch handlers collectent les messages et les traitent en groupes. La taille de lot par défaut est 10, contrôlée par <code>BatchHandlerTrait::shouldFlush()</code>. L&rsquo;<code>Acknowledger</code> gère le succès et l&rsquo;échec individuels dans le lot.</p>
<p><code>reset_on_message: true</code> dans la config Messenger réinitialise les services du conteneur entre les messages. Auparavant, un buffer Monolog pouvait se remplir à travers la gestion des messages et personne ne s&rsquo;en rendait compte avant la production. Ça évite cette catégorie de bugs de stateful sans nécessiter de nettoyage manuel.</p>
<h2 id="le-conteneur-di-devient-plus-expressif">Le conteneur DI devient plus expressif</h2>
<p>Trois changements qui comptent en pratique.</p>
<p>Les types union et intersection s&rsquo;autowire maintenant. PHP 8.1 a ajouté les types intersection, et Symfony 6.0 les câble :</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">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">NormalizerInterface</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">DenormalizerInterface</span> $serializer
</span></span><span style="display:flex;"><span>) {}
</span></span></code></pre></div><p>Ça fonctionne tant que les deux interfaces pointent vers le même service via les alias d&rsquo;autowiring.</p>
<p><code>TaggedIterator</code> et <code>TaggedLocator</code> ont reçu les options <code>defaultPriorityMethod</code> et <code>defaultIndexMethod</code>. On n&rsquo;a plus besoin de YAML pour exprimer l&rsquo;ordonnancement ou l&rsquo;indexation pour les services taggué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><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[TaggedIterator(tag: &#39;app.handler&#39;, defaultPriorityMethod: &#39;getPriority&#39;)]
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">iterable</span> $handlers,
</span></span><span style="display:flex;"><span>) {}
</span></span></code></pre></div><p><code>SubscribedService</code> (l&rsquo;attribut qui remplace la magie implicite de <code>ServiceSubscriberTrait</code>) rend l&rsquo;accès paresseux aux services explicite et typable :</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">#[SubscribedService]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">mailer</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">MailerInterface</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">container</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>(<span style="color:#66d9ef">__METHOD__</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="la-validation-reçoit-trois-nouveaux-outils">La validation reçoit trois nouveaux outils</h2>
<p><code>CssColor</code> valide les valeurs de couleurs CSS dans les formats voulus : hex, RGB, HSL, couleurs nommées, ou n&rsquo;importe quel mélange. Utile pour les champs de configuration de thème où on veut accepter <code>#ff0000</code> mais pas <code>red</code>, ou vice versa.</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">#[Assert\CssColor(formats: Assert\CssColor::HEX_LONG)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $brandColor;
</span></span></code></pre></div><p><code>Cidr</code> valide la notation CIDR pour IPv4 et IPv6, avec des options pour fixer la version et contraindre la plage de masque réseau. Les outils d&rsquo;infrastructure et les formulaires de config réseau ont enfin une contrainte de première classe.</p>
<p>Le troisième ajout n&rsquo;est pas une nouvelle contrainte. Ce sont les attributs imbriqués PHP 8.1 qui rendent les contraintes composées existantes utilisables sans XML. <code>AtLeastOneOf</code>, <code>Collection</code>, <code>All</code>, <code>Sequentially</code> : tout ça nécessitait auparavant des contournements d&rsquo;annotation. Maintenant ça fonctionne juste comme attributs :</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">#[Assert\Collection(
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fields</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;email&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\Email</span>(),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;role&#39;</span>  <span style="color:#f92672">=&gt;</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\NotBlank</span>(), <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\Choice</span>([<span style="color:#e6db74">&#39;admin&#39;</span>, <span style="color:#e6db74">&#39;user&#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:#66d9ef">private</span> <span style="color:#66d9ef">array</span> $payload;
</span></span></code></pre></div><h2 id="le-sérialiseur-nettoyé">Le sérialiseur, nettoyé</h2>
<p>Deux choses. D&rsquo;abord, le contexte de sérialisation est maintenant configurable globalement au lieu d&rsquo;être répété à chaque appel <code>serialize()</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-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># config/packages/serializer.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">serializer</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">default_context</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">enable_max_depth</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>Ensuite, l&rsquo;option <code>COLLECT_DENORMALIZATION_ERRORS</code> change comment le sérialiseur gère les erreurs de type à la désérialisation. Au lieu de lever une exception au premier problème, il les collecte tous et les expose via <code>PartialDenormalizationException</code>. Si on écrit une API qui désérialise des corps de requête, c&rsquo;est la différence entre retourner &ldquo;le premier champ qui échoue&rdquo; et &ldquo;tous les champs qui échouent&rdquo; dans une seule réponse.</p>
<h2 id="les-utilitaires-de-string-que-personne-ne-savait-vouloir">Les utilitaires de string que personne ne savait vouloir</h2>
<p><code>trimPrefix()</code> et <code>trimSuffix()</code> sur les classes <code>UnicodeString</code> / <code>ByteString</code>. Pas glamour, mais supprimer un préfixe connu avec <code>ltrim()</code> est un piège subtil : ça supprime des caractères, pas des strings. Ceux-ci sont corrects :</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:#66d9ef">function</span> <span style="color:#a6e22e">Symfony\Component\String\u</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">u</span>(<span style="color:#e6db74">&#39;file-image-001.png&#39;</span>)<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">trimPrefix</span>(<span style="color:#e6db74">&#39;file-&#39;</span>);   <span style="color:#75715e">// &#39;image-001.png&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">u</span>(<span style="color:#e6db74">&#39;report.html.twig&#39;</span>)<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">trimSuffix</span>(<span style="color:#e6db74">&#39;.twig&#39;</span>);     <span style="color:#75715e">// &#39;report.html&#39;
</span></span></span></code></pre></div><p>Aussi dans cette version : <code>NilUlid</code> pour les ULIDs à valeur zéro, <code>perMonth()</code> et <code>perYear()</code> sur RateLimiter pour quand les limites horaires n&rsquo;ont pas de sens, et <code>appendToFile()</code> dans le composant Filesystem a reçu un paramètre <code>LOCK_EX</code> optionnel pour les écrivains concurrents.</p>
<h2 id="déboguer-lenvironnement">Déboguer l&rsquo;environnement</h2>
<p><code>debug:dotenv</code> est une nouvelle commande console qui montre quels fichiers <code>.env</code> ont été chargés et d&rsquo;où vient chaque valeur. Quand on a <code>.env</code>, <code>.env.local</code>, <code>.env.test</code>, et <code>.env.test.local</code> qui se battent et que quelque chose ne va pas, cette commande dit exactement quel fichier a gagné. Elle n&rsquo;apparaît que quand le composant Dotenv est utilisé, ce qui est le cas pour toute application Symfony standard.</p>
]]></content:encoded></item></channel></rss>