<?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>Enums on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/enums/</link><description>Recent content in Enums on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Mon, 10 Jan 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/enums/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 5.4 LTS : support des enums, alias de routes, et le pont vers PHP 8.1</title><link>https://guillaumedelre.github.io/fr/2022/01/10/symfony-5.4-lts-support-des-enums-alias-de-routes-et-le-pont-vers-php-8.1/</link><pubDate>Mon, 10 Jan 2022 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2022/01/10/symfony-5.4-lts-support-des-enums-alias-de-routes-et-le-pont-vers-php-8.1/</guid><description>Part 6 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 5.4 LTS intègre le support natif des enums et l&amp;#39;essentiel des fonctionnalités de 6.0, tout en préservant la compatibilité ascendante.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 5.4 est sorti le 29 novembre 2021, le même jour que Symfony 6.0 et un jour après PHP 8.1. Pas un hasard.</p>
<p>5.4 est la version LTS, et son rôle est de porter autant que possible le jeu de fonctionnalités de 6.0 tout en conservant la compatibilité 5.x. C&rsquo;est aussi la première version de Symfony qui comprend réellement les fonctionnalités de PHP 8.1.</p>
<h2 id="support-des-enums">Support des enums</h2>
<p>PHP 8.1 a introduit les enums natifs. Symfony 5.4 les embrasse immédiatement :</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:#a6e22e">enum</span> <span style="color:#a6e22e">Status</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">Active</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;active&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">Inactive</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;inactive&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Le type de formulaire <code>EnumType</code> rend les enums sous forme de listes déroulantes, sans transformateurs personnalisés. Le validateur comprend les backed enums. Le sérialiseur mappe les valeurs d&rsquo;enum sur leur type de backing et inversement. Trois composants mis à jour d&rsquo;un coup, ce qui a rendu la migration des bases de code des pseudo-constantes enum vers les vrais enums PHP 8.1 étonnamment fluide.</p>
<h2 id="cache-des-voters-de-sécurité">Cache des voters de sécurité</h2>
<p>La <code>CacheableVoterInterface</code> permet aux voters qui s&rsquo;abstiennent toujours sur un attribut donné de le signaler au système de sécurité, qui peut alors les ignorer lors des vérifications suivantes. Pour les applications avec de nombreux voters, le gain sur les vérifications de permissions s&rsquo;accumule vite. Petit changement, perceptible en pratique.</p>
<h2 id="messenger-continue-de-mûrir">Messenger continue de mûrir</h2>
<p>Le traitement par batch de Messenger (gérer plusieurs messages en une seule transaction au lieu d&rsquo;un par un) est maintenant stable. Rate limiting par transport. Les dead letter queues bénéficient de meilleurs outils. Après des années en mode « expérimental », Messenger en 5.4 est enfin la fondation async sur laquelle on peut s&rsquo;appuyer pour des charges sérieuses.</p>
<h2 id="la-console-a-eu-sa-touche-tab">La Console a eu sa touche Tab</h2>
<p>Symfony 5.4 embarque l&rsquo;autocomplétion shell pour toutes les commandes. Appuyez sur Tab et le shell suggère les noms de commandes, les valeurs d&rsquo;arguments et les valeurs d&rsquo;options. Pour les commandes intégrées, ça fonctionne sans configuration. Pour les commandes personnalisées, ajoutez une méthode <code>complete()</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><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">Symfony\Component\Console\Completion\CompletionInput</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">Symfony\Component\Console\Completion\CompletionSuggestions</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;format&#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;json&#39;</span>, <span style="color:#e6db74">&#39;xml&#39;</span>, <span style="color:#e6db74">&#39;csv&#39;</span>]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Pas d&rsquo;interface requise, juste la méthode et Symfony s&rsquo;en charge. La communauté a aussi passé en revue toutes les commandes intégrées (<code>debug:router</code>, <code>cache:pool:clear</code>, <code>secrets:remove</code>, <code>lint:twig</code>, et une dizaine d&rsquo;autres) pour ajouter les compléments avant la sortie.</p>
<h2 id="les-routes-peuvent-être-des-alias-maintenant">Les routes peuvent être des alias maintenant</h2>
<p>Le composant de routing supporte désormais les alias : une route peut pointer vers une autre. Le cas d&rsquo;usage évident, c&rsquo;est renommer une route sans casser tout ce qui génère encore des URLs avec l&rsquo;ancien nom.</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/routes.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">admin_dashboard</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">path</span>: <span style="color:#ae81ff">/admin</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ancien nom conservé pendant la transition</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">dashboard</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">alias</span>: <span style="color:#ae81ff">admin_dashboard</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">deprecated</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">package</span>: <span style="color:#e6db74">&#39;acme/my-bundle&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">version</span>: <span style="color:#e6db74">&#39;2.3&#39;</span>
</span></span></code></pre></div><p>Générer une URL avec <code>dashboard</code> fonctionne toujours, mais déclenche un avertissement de dépréciation. Des chemins de renommage propres pour les bundles qui doivent maintenir des noms de routes publics tout en avançant.</p>
<h2 id="les-exceptions-sont-mappées-aux-codes-http-dans-la-config">Les exceptions sont mappées aux codes HTTP dans la config</h2>
<p>Avant 5.4, mapper une classe d&rsquo;exception à un code HTTP signifiait implémenter <code>HttpExceptionInterface</code> ou écrire un listener. Maintenant c&rsquo;est juste une entrée YAML :</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/framework.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">framework</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">exceptions</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">App\Exception\PaymentRequiredException</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">status_code</span>: <span style="color:#ae81ff">402</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">log_level</span>: <span style="color:#ae81ff">warning</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">App\Exception\MaintenanceException</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">status_code</span>: <span style="color:#ae81ff">503</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">log_level</span>: <span style="color:#ae81ff">info</span>
</span></span></code></pre></div><p>L&rsquo;exception n&rsquo;a rien à implémenter. Le framework lit la map, définit le code de statut, loggue au niveau configuré. Pratique pour les exceptions métier qui n&rsquo;ont aucune raison de connaître HTTP.</p>
<h2 id="deux-nouvelles-contraintes-de-validation">Deux nouvelles contraintes de validation</h2>
<p>5.4 ajoute <code>Cidr</code> et <code>CssColor</code> au composant Validator.</p>
<p><code>Cidr</code> valide la notation réseau (adresse IP plus masque de sous-réseau) avec un contrôle sur la version IP acceptée et les bornes de la valeur du masque :</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\Cidr(version: 4, netmaskMin: 16, netmaskMax: 28)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $allowedSubnet;
</span></span></code></pre></div><p><code>CssColor</code> valide qu&rsquo;une chaîne est une couleur CSS valide. Utile pour les éditeurs de thème, la config CMS, ou toute interface qui laisse les utilisateurs choisir des couleurs :</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(
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">formats</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">Assert\CssColor</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HEX_LONG</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">message</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;La couleur d&#39;accentuation doit être une valeur hex à 6 chiffres.&#34;</span>,
</span></span><span style="display:flex;"><span>)]
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $accentColor;
</span></span></code></pre></div><h2 id="attributs-php-imbriqués-pour-les-contraintes-de-validation">Attributs PHP imbriqués pour les contraintes de validation</h2>
<p>Symfony 5.2 avait ajouté les contraintes de validation en attributs PHP, mais PHP 8.0 avait une limitation sur les attributs imbriqués. Les contraintes complexes comme <code>All</code>, <code>Collection</code>, ou <code>AtLeastOneOf</code> étaient impossibles à exprimer avec la syntaxe d&rsquo;attribut seule. PHP 8.1 a levé cette restriction, et 5.4 en tire le meilleur parti :</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\Validator\Constraints</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">Assert</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CartItem</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[Assert\All([
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\NotNull</span>(),
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\Range</span>(<span style="color:#a6e22e">min</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">1</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> $quantities;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[Assert\AtLeastOneOf(
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">constraints</span><span style="color:#f92672">:</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\Email</span>(), <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\Url</span>()],
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">message</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Doit être un email ou une URL valide.&#39;</span>,
</span></span><span style="display:flex;"><span>    )]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $contact;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Pas de docblocks d&rsquo;annotations, pas de mapping XML. Des attributs PHP 8.1 purs, de bout en bout.</p>
<h2 id="injection-de-dépendances--trois-choses-à-savoir">Injection de dépendances : trois choses à savoir</h2>
<p>Les itérateurs taggués peuvent maintenant être injectés dans des service locators, qui n&rsquo;acceptaient auparavant que des listes de services explicites. L&rsquo;autowiring des types union fonctionne quand les deux côtés de l&rsquo;union résolvent vers le même service, ce qui est courant avec les interfaces du serializer :</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><code>#[SubscribedService]</code> remplace l&rsquo;introspection automatique que <code>ServiceSubscriberTrait</code> faisait implicitement. C&rsquo;est maintenant un attribut explicite sur les méthodes, ce qui rend la dépendance visible sans aucune magie :</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\Contracts\Service\Attribute\SubscribedService</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SomeService</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">ServiceSubscriberInterface</span>
</span></span><span style="display:flex;"><span>{
</span></span><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">router</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">RouterInterface</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><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="messenger--attributs-état-des-workers-et-reset-de-services">Messenger : attributs, état des workers et reset de services</h2>
<p>Les handlers Messenger peuvent abandonner <code>MessageHandlerInterface</code> en faveur de <code>#[AsMessageHandler]</code>, qui permet aussi de lier un handler à un transport spécifique et de définir sa priorité, sans toucher au YAML :</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">ProcessOrderHandler</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">ProcessOrder</span> $message)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#75715e">/* ... */</span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>L&rsquo;état des workers est maintenant inspectable via <code>WorkerMetadata</code> dans les event listeners, utile quand vous avez des workers sur plusieurs transports et avez besoin de savoir lequel a déclenché un événement donné.</p>
<p>Les workers longue durée accumulent de l&rsquo;état entre les messages : buffers de l&rsquo;entity manager, caches en mémoire, connexions ouvertes. La nouvelle option <code>reset_on_message</code> prend en charge la réinitialisation de tous les services réinitialisables entre les messages :</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:#f92672">framework</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">messenger</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">reset_on_message</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><h2 id="serializer--collecter-les-erreurs-plutôt-que-lever">Serializer : collecter les erreurs plutôt que lever</h2>
<p>Désérialiser du JSON externe dans un DTO typé levait une exception dès la première discordance de type. L&rsquo;option <code>COLLECT_DENORMALIZATION_ERRORS</code> change ça : toutes les erreurs de type sont collectées dans une <code>PartialDenormalizationException</code>, pour que vous puissiez retourner un 400 propre avec la liste complète des problèmes par champ :</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">try</span> {
</span></span><span style="display:flex;"><span>    $dto <span style="color:#f92672">=</span> $serializer<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">deserialize</span>($request<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getContent</span>(), <span style="color:#a6e22e">OrderDto</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>, <span style="color:#e6db74">&#39;json&#39;</span>, [
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">DenormalizerInterface</span><span style="color:#f92672">::</span><span style="color:#a6e22e">COLLECT_DENORMALIZATION_ERRORS</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    ]);
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">PartialDenormalizationException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">json</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">array_map</span>(<span style="color:#a6e22e">fn</span>($err) <span style="color:#f92672">=&gt;</span> [<span style="color:#e6db74">&#39;path&#39;</span> <span style="color:#f92672">=&gt;</span> $err<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getPath</span>(), <span style="color:#e6db74">&#39;expected&#39;</span> <span style="color:#f92672">=&gt;</span> $err<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getExpectedTypes</span>()], $e<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getErrors</span>()),
</span></span><span style="display:flex;"><span>        <span style="color:#ae81ff">400</span>
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Le contexte par défaut du serializer peut aussi être défini globalement en YAML, pour ne plus passer les mêmes options à chaque appel.</p>
<h2 id="négociation-de-langue-intégrée">Négociation de langue intégrée</h2>
<p>Deux nouvelles options du framework gèrent l&rsquo;en-tête <code>Accept-Language</code> sans listeners personnalisé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-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">framework</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">enabled_locales</span>: [<span style="color:#e6db74">&#39;en&#39;</span>, <span style="color:#e6db74">&#39;fr&#39;</span>, <span style="color:#e6db74">&#39;de&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">set_locale_from_accept_language</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">set_content_language_from_locale</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>Avec ça en place, Symfony lit la langue préférée du navigateur, choisit la meilleure correspondance parmi <code>enabled_locales</code>, définit la locale de la requête, et ajoute un en-tête <code>Content-Language</code> à la réponse. L&rsquo;attribut de route <code>{_locale}</code> a toujours la priorité quand il est présent.</p>
<h2 id="traduction--extraction-pas-mise-à-jour">Traduction : extraction, pas mise à jour</h2>
<p>La commande <code>translation:update</code> est renommée en <code>translation:extract</code>. L&rsquo;ancien nom reste comme déprécié. La distinction compte : la commande n&rsquo;écrit jamais dans une base de données, elle extrait les chaînes traduisibles des fichiers source. Le nouveau nom dit enfin ce qu&rsquo;elle fait.</p>
<p><code>lint:xliff</code> gagne aussi une option <code>--format=github</code> qui sort les erreurs en annotations GitHub Actions, pour que les échecs de lint de traduction apparaissent en commentaires de revue de PR plutôt que de se noyer dans les logs.</p>
<h2 id="raccourcis-du-contrôleur-élagués">Raccourcis du contrôleur élagués</h2>
<p>Trois raccourcis d&rsquo;<code>AbstractController</code> sont dépréciés : <code>getDoctrine()</code>, <code>dispatchMessage()</code>, et la méthode générique <code>get()</code> pour récupérer des services arbitraires du container. La direction, c&rsquo;est l&rsquo;injection par constructeur explicite. Pour <code>getDoctrine()</code> en particulier :</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">// avant
</span></span></span><span style="display:flex;"><span>$em <span style="color:#f92672">=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getDoctrine</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getManager</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// après — injecter directement
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(<span style="color:#66d9ef">private</span> <span style="color:#a6e22e">EntityManagerInterface</span> $em) {}
</span></span></code></pre></div><p><code>Request::get()</code> est aussi déprécié. Il cherchait dans les attributs de route, la query string et le corps POST dans un ordre non documenté, ce qui était une excellente façon d&rsquo;obtenir des résultats surprenants. Utilisez <code>$request-&gt;query-&gt;get()</code>, <code>$request-&gt;request-&gt;get()</code>, ou <code>$request-&gt;attributes-&gt;get()</code> et soyez explicite sur la provenance de la valeur.</p>
<h2 id="la-classe-utilitaire-path">La classe utilitaire Path</h2>
<p>Le composant Filesystem reçoit une classe <code>Path</code> portée depuis <code>webmozart/path-util</code>. Elle gère les cas tordus que <code>dirname()</code> et <code>realpath()</code> ratent :</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">canonicalize</span>(<span style="color:#e6db74">&#39;../config/../config/services.yaml&#39;</span>); <span style="color:#75715e">// &#39;../config/services.yaml&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">getDirectory</span>(<span style="color:#e6db74">&#39;C:/&#39;</span>);                               <span style="color:#75715e">// &#39;C:/&#39; (dirname() retourne &#39;.&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">getLongestCommonBasePath</span>([
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;/var/www/project/src/Controller/FooController.php&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;/var/www/project/src/Controller/BarController.php&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;/var/www/project/src/Entity/User.php&#39;</span>,
</span></span><span style="display:flex;"><span>]);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// &#39;/var/www/project/src&#39;
</span></span></span></code></pre></div><p>Utile dès que votre code manipule des chemins qui traversent les frontières des OS ou qui contiennent des segments relatifs.</p>
<h2 id="les-petites-choses-qui-saccumulent">Les petites choses qui s&rsquo;accumulent</h2>
<p><code>debug:dotenv</code> montre quels fichiers <code>.env</code> ont été chargés et quelle valeur chaque variable résout. La première chose qu&rsquo;on cherche quand un comportement spécifique à un environnement déraille.</p>
<p>Le composant String ajoute <code>trimPrefix()</code> et <code>trimSuffix()</code> pour retirer des préfixes ou suffixes connus sans écrire un calcul de substr :</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:#a6e22e">u</span>(<span style="color:#e6db74">&#39;file-image-0001.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-0001.png&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">u</span>(<span style="color:#e6db74">&#39;template.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;template.html&#39;
</span></span></span></code></pre></div><p>DomCrawler reçoit <code>innerText()</code>, qui retourne uniquement le texte direct d&rsquo;un nœud, à l&rsquo;exclusion des éléments enfants. <code>text()</code> retourne tout y compris le texte imbriqué ; <code>innerText()</code> retourne uniquement le contenu propre du nœud. Petite différence, mais ça compte quand on fait du scraping.</p>
<p>Le composant RateLimiter étend son support des intervalles avec <code>perMonth()</code> et <code>perYear()</code>, pour les applications qui ont besoin de limiter des événements sur des fenêtres plus longues : envois de newsletters, remises à zéro de quotas API, limites de forfaits annuels.</p>
<p>Le composant Finder respecte maintenant les fichiers <code>.gitignore</code> dans tous les sous-répertoires quand vous appelez <code>ignoreVCSIgnored(true)</code>, pas seulement à la racine. Les règles des répertoires enfants supplantent celles des parents, exactement comme git lui-même.</p>
<h2 id="la-fenêtre-lts">La fenêtre LTS</h2>
<p>5.4 reçoit des corrections de bugs jusqu&rsquo;en novembre 2024 et des correctifs de sécurité jusqu&rsquo;en novembre 2025. La migration de 5.4 vers 6.4 (la prochaine LTS) est intentionnellement fluide : corrigez les avertissements de dépréciation de 5.4, et le saut vers 6.x devient mécanique.</p>
<p>La couche de dépréciation en 5.4 pointe vers tout ce que 6.0 supprime : les derniers morceaux de l&rsquo;ancien système de sécurité, <code>ContainerAwareTrait</code>, et quelques patterns legacy de formulaires et de serializer.</p>
]]></content:encoded></item><item><title>PHP 8.1 : enums, fibers, et un système de types qui grandit</title><link>https://guillaumedelre.github.io/fr/2022/01/09/php-8.1-enums-fibers-et-un-syst%C3%A8me-de-types-qui-grandit/</link><pubDate>Sun, 09 Jan 2022 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2022/01/09/php-8.1-enums-fibers-et-un-syst%C3%A8me-de-types-qui-grandit/</guid><description>Part 7 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 8.1 apporte les enums natifs, les fibers pour la concurrence coopérative, les propriétés readonly, et les types d&amp;#39;intersection.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.1 est sorti le 25 novembre. Il fait suite à la refonte massive de 8.0 avec quelque chose de différent : moins de fonctionnalités, mais chacune vraiment réfléchie plutôt que greffée à la va-vite.</p>
<h2 id="les-enums">Les enums</h2>
<p>C&rsquo;est la nouveauté qui change les bases de code dès la mise à jour. Avant 8.1, les énumérations en PHP se résumaient à des constantes de classe, des chaînes ou des entiers sans rien pour les faire respecter :</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">// avant : rien n&#39;empêche de passer Status::INVALID
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">ACTIVE</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;active&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">INACTIVE</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;inactive&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// après
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">enum</span> <span style="color:#a6e22e">Status</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">Active</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;active&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">Inactive</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;inactive&#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">function</span> <span style="color:#a6e22e">activate</span>(<span style="color:#a6e22e">Status</span> $status)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#f92672">...</span> }
</span></span></code></pre></div><p>Les enums PHP sont des objets, pas des scalaires. Ils supportent les méthodes, les interfaces et les constantes. Les backed enums (avec une valeur string ou int) se sérialisent proprement et se mappent naturellement aux colonnes de base de données. Les pure enums (sans type de backing) expriment des concepts métier sans se soucier de la sérialisation.</p>
<p>L&rsquo;effet immédiat : chaque champ de statut, chaque ensemble fini d&rsquo;états dans toutes les bases de code que je maintiens est devenu un candidat à l&rsquo;enum. Le système de types a enfin un moyen natif d&rsquo;exprimer ce que chaque projet PHP simulait depuis des années.</p>
<h2 id="les-fibers">Les fibers</h2>
<p>Les fibers sont une primitive de concurrence coopérative : vous pouvez suspendre et reprendre l&rsquo;exécution d&rsquo;une fonction, en cédant le contrôle sans recourir aux threads.</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>$fiber <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Fiber</span>(<span style="color:#66d9ef">function</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    $value <span style="color:#f92672">=</span> <span style="color:#a6e22e">Fiber</span><span style="color:#f92672">::</span><span style="color:#a6e22e">suspend</span>(<span style="color:#e6db74">&#39;first&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;Repris avec : </span><span style="color:#e6db74">{</span>$value<span style="color:#e6db74">}</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>;
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $fiber<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">start</span>();    <span style="color:#75715e">// &#39;first&#39;
</span></span></span><span style="display:flex;"><span>$fiber<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">resume</span>(<span style="color:#e6db74">&#39;hello&#39;</span>);      <span style="color:#75715e">// &#34;Repris avec : hello&#34;
</span></span></span></code></pre></div><p>Les fibers sont la fondation dont les bibliothèques async comme ReactPHP et Amp avaient besoin depuis un moment du côté du runtime. Pour la plupart des développeurs d&rsquo;applications, l&rsquo;API directe compte moins que les bibliothèques construites par-dessus, mais comprendre les fibers explique ce que font ces bibliothèques en coulisses.</p>
<h2 id="pencil2-les-propriétés-readonly">:pencil2: Les propriétés readonly</h2>
<p>8.0 avait apporté la promotion des paramètres du constructeur. 8.1 ajoute <code>readonly</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><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> {
</span></span><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">public</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">int</span> $id,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">string</span> $name,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Une propriété <code>readonly</code> ne peut être écrite qu&rsquo;une seule fois, lors de l&rsquo;initialisation. Après ça, toute écriture lève une <code>Error</code>. Combiné avec la promotion des paramètres, les value objects et les DTOs deviennent concis et signifient réellement ce qu&rsquo;ils annoncent.</p>
<h2 id="la-syntaxe-callable-de-première-classe">La syntaxe callable de première classe</h2>
<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>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">strlen</span>(<span style="color:#f92672">...</span>);
</span></span><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">process</span>(<span style="color:#f92672">...</span>);
</span></span><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">MyClass</span><span style="color:#f92672">::</span><span style="color:#a6e22e">create</span>(<span style="color:#f92672">...</span>);
</span></span></code></pre></div><p><code>...</code> après un callable crée une <code>Closure</code> sans le boilerplate de <code>Closure::fromCallable()</code>. Utile quand on passe des méthodes comme callbacks.</p>
<p>8.1 est précis. Les enums justifient à eux seuls la mise à jour.</p>
<h2 id="les-types-dintersection">Les types d&rsquo;intersection</h2>
<p>Les types union ont débarqué en 8.0. Les types d&rsquo;intersection suivent en 8.1. Là où un union dit « l&rsquo;un ou l&rsquo;autre », une intersection dit « tous à la fois » :</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">function</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">Countable</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Iterator</span> $collection)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">foreach</span> ($collection <span style="color:#66d9ef">as</span> $item) { <span style="color:#75715e">/* ... */</span> }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">count</span>($collection);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Une contrainte : les types d&rsquo;intersection ne peuvent pas être mélangés avec les types union dans la même déclaration (ça arrivera en 8.2 avec les DNF types). Mais ça débloque déjà une vérification de types précise pour les objets qui doivent satisfaire plusieurs interfaces à la fois, un pattern que les frameworks utilisent constamment et qui devait rester sans typage jusqu&rsquo;ici.</p>
<h2 id="le-type-de-retour-never">Le type de retour <code>never</code></h2>
<p>Une fonction qui ne retourne jamais (elle lève toujours une exception ou sort) a maintenant un type pour le dire :</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">function</span> <span style="color:#a6e22e">redirect</span>(<span style="color:#a6e22e">string</span> $url)<span style="color:#f92672">:</span> <span style="color:#a6e22e">never</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">header</span>(<span style="color:#e6db74">&#34;Location: </span><span style="color:#e6db74">{</span>$url<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">exit</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">function</span> <span style="color:#a6e22e">fail</span>(<span style="color:#a6e22e">string</span> $message)<span style="color:#f92672">:</span> <span style="color:#a6e22e">never</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\RuntimeException</span>($message);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>L&rsquo;avantage concret : les analyseurs statiques peuvent prouver que le code après une fonction <code>never</code> est inatteignable, et les appelants savent qu&rsquo;il n&rsquo;y a pas de valeur de retour à gérer. Avant ça, ça vivait dans des docblocks sans enforcement.</p>
<h2 id="les-constantes-de-classe-finales">Les constantes de classe finales</h2>
<p>Avant 8.1, n&rsquo;importe quelle sous-classe pouvait silencieusement surcharger la constante de classe d&rsquo;un parent. Maintenant vous pouvez y mettre un terme :</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">Base</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">VERSION</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;1.0&#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">class</span> <span style="color:#a6e22e">Child</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Base</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Fatal error: Cannot override final constant Base::VERSION
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">VERSION</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;2.0&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Parallèlement, les constantes d&rsquo;interface sont désormais surchargeables par les classes implémentant l&rsquo;interface par défaut. Un correctif de comportement qui était incohérent depuis l&rsquo;introduction des interfaces.</p>
<h2 id="new-dans-les-initialiseurs"><code>new</code> dans les initialiseurs</h2>
<p>Les valeurs par défaut des paramètres étaient autrefois limitées aux scalaires et aux tableaux. 8.1 lève cette restriction :</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">Logger</span> {
</span></span><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">Handler</span> $handler <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">NullHandler</span>(),
</span></span><span style="display:flex;"><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">function</span> <span style="color:#a6e22e">createUser</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Validator</span> $validator <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">DefaultValidator</span>(),
</span></span><span style="display:flex;"><span>)<span style="color:#f92672">:</span> <span style="color:#a6e22e">User</span> { <span style="color:#75715e">/* ... */</span> }
</span></span></code></pre></div><p>Idem pour les arguments d&rsquo;attributs et les initialiseurs de variables statiques. Ce qui signifie que l&rsquo;injection de dépendances avec des valeurs par défaut sensées ne nécessite plus une vérification de null et une instanciation paresseuse dans le corps de la méthode.</p>
<h2 id="le-déballage-de-tableaux-avec-des-clés-string">Le déballage de tableaux avec des clés string</h2>
<p>Le déballage de tableau via l&rsquo;opérateur spread ne fonctionnait qu&rsquo;avec des tableaux à clés entières avant 8.1. Les clés string fonctionnent aussi maintenant :</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>$defaults <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;color&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;red&#39;</span>, <span style="color:#e6db74">&#39;size&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;M&#39;</span>];
</span></span><span style="display:flex;"><span>$custom <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;size&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;L&#39;</span>, <span style="color:#e6db74">&#39;weight&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;200g&#39;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$merged <span style="color:#f92672">=</span> [<span style="color:#f92672">...</span>$defaults, <span style="color:#f92672">...</span>$custom];
</span></span><span style="display:flex;"><span><span style="color:#75715e">// [&#39;color&#39; =&gt; &#39;red&#39;, &#39;size&#39; =&gt; &#39;L&#39;, &#39;weight&#39; =&gt; &#39;200g&#39;]
</span></span></span></code></pre></div><p>Les clés ultérieures écrasent les précédentes. Même comportement que <code>array_merge()</code>, mais exprimé inline. La différence de performance est marginale ; la différence de lisibilité, elle, ne l&rsquo;est pas.</p>
<h2 id="fsync-et-fdatasync"><code>fsync</code> et <code>fdatasync</code></h2>
<p>Deux fonctions qui n&rsquo;avaient aucune bonne raison d&rsquo;être absentes d&rsquo;un langage orienté système de fichiers :</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>$fp <span style="color:#f92672">=</span> <span style="color:#a6e22e">fopen</span>(<span style="color:#e6db74">&#39;/tmp/important.dat&#39;</span>, <span style="color:#e6db74">&#39;w&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fwrite</span>($fp, $data);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fsync</span>($fp);   <span style="color:#75715e">// vide les buffers OS vers le stockage physique
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">fclose</span>($fp);
</span></span></code></pre></div><p><code>fdatasync()</code> fait la même chose mais saute la synchronisation des métadonnées quand on ne se soucie que de la durabilité des données. Les deux retournent <code>false</code> en cas d&rsquo;échec. Si vous écrivez quoi que ce soit qui nécessite une sécurité en cas de crash, vous aviez besoin de ça.</p>
<h2 id="passer-null-aux-paramètres-non-nullables-des-fonctions-internes">Passer <code>null</code> aux paramètres non-nullables des fonctions internes</h2>
<p>Un changement plus discret mais aux conséquences réelles : les fonctions internes qui acceptent des chaînes, des entiers, etc. ont toujours avalé silencieusement <code>null</code> et l&rsquo;ont coercé. En 8.1, ça commence à émettre un avertissement de dépréciation.</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:#a6e22e">str_contains</span>(<span style="color:#e6db74">&#34;foobar&#34;</span>, <span style="color:#66d9ef">null</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Deprecated: Passing null to parameter #2 ($needle) of type string is deprecated
</span></span></span></code></pre></div><p>Ça aligne les fonctions internes sur les fonctions définies par l&rsquo;utilisateur, qui refusaient déjà les arguments nullable pour des paramètres non-nullables. PHP 9.0 transforme ça en erreur fatale. Si vous passez <code>null</code> dans des fonctions de chaînes, c&rsquo;est maintenant un meilleur moment pour le corriger que pendant un incident de production.</p>
<h2 id="mysqli-lève-des-exceptions-par-défaut">MySQLi lève des exceptions par défaut</h2>
<p>Avant 8.1, MySQLi échouait silencieusement sauf si vous appeliez explicitement <code>mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)</code>. C&rsquo;est maintenant la valeur par défaut :</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">// Ceci lève \mysqli_sql_exception en cas d&#39;échec de connexion en 8.1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Auparavant retournait false et définissait une erreur que vous deviez vérifier manuellement
</span></span></span><span style="display:flex;"><span>$connection <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">mysqli</span>(<span style="color:#e6db74">&#39;localhost&#39;</span>, <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#e6db74">&#39;wrong_password&#39;</span>, <span style="color:#e6db74">&#39;db&#39;</span>);
</span></span></code></pre></div><p>Toute base de code qui attrape les erreurs MySQLi en vérifiant les valeurs de retour doit être revue. Les échecs silencieux qui causaient des bugs difficiles à diagnostiquer lèvent maintenant des exceptions, ce qui est le bon comportement, même si ça peut surprendre si vous l&rsquo;attrapez en pleine mise à jour.</p>
]]></content:encoded></item></channel></rss>