<?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>Securite on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/securite/</link><description>Recent content in Securite on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Sat, 10 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/securite/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 7.4 LTS : signature de messages, tableaux PHP en config et le dernier 7.x</title><link>https://guillaumedelre.github.io/fr/2026/01/10/symfony-7.4-lts-signature-de-messages-tableaux-php-en-config-et-le-dernier-7.x/</link><pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2026/01/10/symfony-7.4-lts-signature-de-messages-tableaux-php-en-config-et-le-dernier-7.x/</guid><description>Part 10 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 7.4 LTS ajoute la signature de messages Messenger, une configuration en tableaux PHP, et clôt la ligne 7.x.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 7.4 est sorti en novembre 2025, aux côtés de 8.0. C&rsquo;est la dernière LTS de la ligne 7.x : PHP 8.2 minimum, trois ans de corrections de bugs, quatre de sécurité. Pour les équipes qui ne peuvent pas ou ne veulent pas suivre l&rsquo;exigence PHP 8.4 de 8.0, 7.4 est l&rsquo;endroit où atterrir.</p>
<h2 id="la-signature-de-messages-dans-messenger">La signature de messages dans Messenger</h2>
<p>La sécurité des transports dans Messenger a toujours été le problème de l&rsquo;application à résoudre. 7.4 ajoute la signature de messages : un mécanisme basé sur des stamps qui signe les messages dispatchés et valide les signatures à la réception.</p>
<p>Le cas d&rsquo;usage cible est les scénarios multi-tenant ou de transport externe où on a besoin d&rsquo;une preuve cryptographique qu&rsquo;un message n&rsquo;a pas été altéré ni injecté depuis l&rsquo;extérieur. La configuration vit au niveau du transport :</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">transports</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">async</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">dsn</span>: <span style="color:#e6db74">&#39;%env(MESSENGER_TRANSPORT_DSN)%&#39;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">options</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">signing_key</span>: <span style="color:#e6db74">&#39;%env(MESSENGER_SIGNING_KEY)%&#39;</span>
</span></span></code></pre></div><h2 id="configuration-en-tableaux-php">Configuration en tableaux PHP</h2>
<p>Les formats de configuration de Symfony ont toujours été YAML (défaut), XML et PHP. Le format PHP existait mais était maladroit : un DSL builder fluent qui nécessitait du chaînage de méthodes et ne donnait rien d&rsquo;utile à l&rsquo;IDE.</p>
<p>7.4 remplace le format fluent par des tableaux PHP standard. Les IDEs peuvent maintenant vraiment l&rsquo;analyser, <code>config/reference.php</code> est auto-généré comme référence avec annotations de types, et le résultat ressemble à des données plutôt qu&rsquo;à du 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">return</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">FrameworkConfig</span> $framework)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    $framework<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">router</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">strictRequirements</span>(<span style="color:#66d9ef">null</span>);
</span></span><span style="display:flex;"><span>    $framework<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">session</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">enabled</span>(<span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Le format fluent est déprécié. Les tableaux sont l&rsquo;avenir, et honnêtement c&rsquo;est un meilleur format.</p>
<h2 id="améliorations-oidc">Améliorations OIDC</h2>
<p><code>#[IsSignatureValid]</code> valide les URLs signées directement dans les contrôleurs, supprimant le boilerplate de la validation manuelle. OpenID Connect supporte maintenant plusieurs endpoints de découverte, et une nouvelle commande <code>security:oidc-token:generate</code> rend le dev et les tests beaucoup moins pénibles.</p>
<h2 id="la-fenêtre-de-support">La fenêtre de support</h2>
<p>7.4 LTS : bugs jusqu&rsquo;en novembre 2028, correctifs de sécurité jusqu&rsquo;en novembre 2029. Le chemin vers 8.4 LTS (la prochaine cible long terme) passe par les notices de dépréciation de 7.4 et la mise à jour PHP 8.4. Corriger les dépréciations maintenant et le saut vers 8.x sera beaucoup moins douloureux.</p>
<h2 id="les-attributs-deviennent-plus-précis">Les attributs deviennent plus précis</h2>
<p><code>#[CurrentUser]</code> accepte maintenant les types union, ce qui compte en pratique quand une route est accessible par plus d&rsquo;une classe 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-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">index</span>(<span style="color:#75715e">#[CurrentUser] AdminUser|Customer $user): Response
</span></span></span></code></pre></div><p><code>#[Route]</code> accepte un tableau pour l&rsquo;option <code>env</code>, donc une route de debug active seulement en <code>dev</code> et <code>test</code> n&rsquo;a plus besoin de deux définitions séparées. <code>#[AsDecorator]</code> est maintenant répétable, ce qui signifie qu&rsquo;une classe peut décorer plusieurs services à la fois. Les signatures de méthode <code>#[AsEventListener]</code> acceptent les types d&rsquo;événements union. <code>#[IsGranted]</code> reçoit une option <code>methods</code> pour limiter une vérification d&rsquo;autorisation à des verbes HTTP spécifiques sans dupliquer la route.</p>
<h2 id="la-classe-request-arrête-den-faire-trop">La classe Request arrête d&rsquo;en faire trop</h2>
<p><code>Request::get()</code> est dépréciée, et franchement bonne débarrassance. La méthode cherchait dans les attributs de route, puis les paramètres de query, puis le corps de la requête, dans cet ordre, retournant silencieusement ce qu&rsquo;elle trouvait en premier. Cette ambiguïté causait de vrais bugs. Elle est supprimée dans 8.0 ; dans 7.4 elle fonctionne encore mais déclenche une dépréciation. Les remplacements sont explicites : <code>$request-&gt;attributes-&gt;get()</code>, <code>$request-&gt;query-&gt;get()</code>, <code>$request-&gt;request-&gt;get()</code>.</p>
<p>Le parsing du corps pour les requêtes <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code> et <code>QUERY</code> arrive en même temps. Auparavant Symfony ne parsait <code>application/x-www-form-urlencoded</code> et <code>multipart/form-data</code> que pour <code>POST</code>. Ces mêmes types de contenu sont maintenant parsés pour les autres méthodes accessibles en écriture aussi, ce qui tue un contournement REST API courant.</p>
<p>La surcharge de méthode HTTP pour <code>GET</code>, <code>HEAD</code>, <code>CONNECT</code> et <code>TRACE</code> est dépréciée. Surcharger une méthode sûre avec un header était de toute façon toujours sémantiquement cassé. On peut maintenant autoriser explicitement seulement les méthodes qui ont du sens pour son application :</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">Request</span><span style="color:#f92672">::</span><span style="color:#a6e22e">setAllowedHttpMethodOverride</span>([<span style="color:#e6db74">&#39;PUT&#39;</span>, <span style="color:#e6db74">&#39;PATCH&#39;</span>, <span style="color:#e6db74">&#39;DELETE&#39;</span>]);
</span></span></code></pre></div><h2 id="les-workflows-acceptent-les-backedenums">Les Workflows acceptent les BackedEnums</h2>
<p>Les places et transitions de Workflow peuvent maintenant être définies avec des backed enums PHP, à la fois en YAML (via le tag <code>!php/enum</code>) et en config PHP. Le marking store fonctionne avec les valeurs d&rsquo;enum directement, donc le modèle de domaine et la définition du workflow utilisent enfin les mêmes types :</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">workflows</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">blog_publishing</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">initial_marking</span>: !<span style="color:#ae81ff">php/enum App\Status\PostStatus::Draft</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">places</span>: !<span style="color:#ae81ff">php/enum App\Status\PostStatus</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">transitions</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">publish</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">from</span>: !<span style="color:#ae81ff">php/enum App\Status\PostStatus::Review</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">to</span>: !<span style="color:#ae81ff">php/enum App\Status\PostStatus::Published</span>
</span></span></code></pre></div><h2 id="étendre-la-validation-et-la-sérialisation-pour-les-classes-tierces">Étendre la validation et la sérialisation pour les classes tierces</h2>
<p>Besoin d&rsquo;ajouter des métadonnées de validation ou de sérialisation à une classe d&rsquo;un bundle qu&rsquo;on ne possède pas ? 7.4 a <code>#[ExtendsValidationFor]</code> et <code>#[ExtendsSerializationFor]</code> pour ça. On écrit une classe compagnon avec ses annotations supplémentaires, on pointe l&rsquo;attribut vers la classe cible, et Symfony fusionne les métadonnées à la compilation du conteneur :</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">#[ExtendsValidationFor(UserRegistration::class)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UserRegistrationValidation</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[Assert\NotBlank(groups: [&#39;my_app&#39;])]
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[Assert\Length(min: 3, groups: [&#39;my_app&#39;])]
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[Assert\Email(groups: [&#39;my_app&#39;])]
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $email <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Symfony vérifie à la compilation que les propriétés déclarées existent réellement sur la classe cible. Un renommage ne cassera pas silencieusement la validation.</p>
<h2 id="dx--ce-qui-ne-fait-pas-la-une-mais-compte">DX : ce qui ne fait pas la une mais compte</h2>
<p>Le helper Question dans Console accepte un timeout. Demander à l&rsquo;utilisateur de confirmer quelque chose, et s&rsquo;il ne répond pas en N secondes, la réponse par défaut s&rsquo;applique. Très pratique dans les scripts de déploiement qui ne peuvent pas se permettre d&rsquo;attendre éternellement un humain.</p>
<p><code>messenger:consume</code> reçoit <code>--exclude-receivers</code>. Combiné avec <code>--all</code>, il permet de consommer depuis tous les transports sauf des spécifiques :</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>bin/console messenger:consume --all --exclude-receivers<span style="color:#f92672">=</span>low_priority
</span></span></code></pre></div><p>Le mode worker FrankenPHP est maintenant auto-détecté. Si le processus tourne dans FrankenPHP, Symfony bascule en mode worker automatiquement. Pas de package supplémentaire nécessaire.</p>
<p>La commande <code>debug:router</code> cache les colonnes <code>Scheme</code> et <code>Host</code> quand toutes les routes utilisent <code>ANY</code>, ce qui supprime beaucoup de bruit de la sortie par défaut. Les méthodes HTTP sont maintenant aussi colorées.</p>
<p>Les tests fonctionnels reçoivent <code>$client-&gt;getSession()</code> avant la première requête. Auparavant il fallait faire au moins une requête pour accéder à la session, ce qui était agaçant. Maintenant on peut pré-remplir les tokens CSRF ou les flags A/B en amont :</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>$session <span style="color:#f92672">=</span> $client<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getSession</span>();
</span></span><span style="display:flex;"><span>$session<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">set</span>(<span style="color:#e6db74">&#39;_csrf/checkout&#39;</span>, <span style="color:#e6db74">&#39;test-token&#39;</span>);
</span></span><span style="display:flex;"><span>$session<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">save</span>();
</span></span></code></pre></div><h2 id="lock--store-dynamodb">Lock : store DynamoDB</h2>
<p><code>DynamoDbStore</code> arrive comme nouveau backend de Lock. Utile dans les déploiements AWS-natifs où Redis n&rsquo;est pas dans la stack, et ça fonctionne exactement comme n&rsquo;importe quel autre store :</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>$store <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">DynamoDbStore</span>(<span style="color:#e6db74">&#39;dynamodb://default/locks&#39;</span>);
</span></span><span style="display:flex;"><span>$factory <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">LockFactory</span>($store);
</span></span></code></pre></div><h2 id="bridge-doctrine--types-day-point-et-time-point">Bridge Doctrine : types day point et time point</h2>
<p>Deux nouveaux types de colonnes Doctrine : <code>day_point</code> stocke une valeur date uniquement (sans composant heure) et <code>time_point</code> stocke une valeur heure uniquement, tous deux mappant vers <code>DatePoint</code>. Bien quand le domaine sépare genuinement la date de l&rsquo;heure :</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">#[ORM\Column(type: &#39;day_point&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#a6e22e">DatePoint</span> $birthDate;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#[ORM\Column(type: &#39;time_point&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#a6e22e">DatePoint</span> $openingTime;
</span></span></code></pre></div><h2 id="routing--paramètres-de-query-explicites">Routing : paramètres de query explicites</h2>
<p>La clé <code>_query</code> dans la génération d&rsquo;URL permet de définir les paramètres de query explicitement, séparément des paramètres de route. Ça compte quand un paramètre de route et un paramètre de query partagent le même 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-php" data-lang="php"><span style="display:flex;"><span>$url <span style="color:#f92672">=</span> $urlGenerator<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">generate</span>(<span style="color:#e6db74">&#39;report&#39;</span>, [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;site&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;fr&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;_query&#39;</span> <span style="color:#f92672">=&gt;</span> [<span style="color:#e6db74">&#39;site&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;us&#39;</span>],
</span></span><span style="display:flex;"><span>]);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// /report/fr?site=us
</span></span></span></code></pre></div><h2 id="weblink--parsing-des-en-têtes-link-entrants">WebLink : parsing des en-têtes Link entrants</h2>
<p><code>HttpHeaderParser</code> parse les en-têtes de réponse <code>Link</code> en objets structurés. Avant ça, parser des en-têtes Link depuis des réponses d&rsquo;API nécessitait soit d&rsquo;importer une bibliothèque tierce, soit d&rsquo;écrire des regex. Le cas d&rsquo;usage : les APIs HTTP qui annoncent des ressources liées ou la pagination via des en-têtes Link, comme le fait l&rsquo;API GitHub.</p>
<h2 id="le-parsing-html5-est-plus-rapide-sur-php-84">Le parsing HTML5 est plus rapide sur PHP 8.4</h2>
<p>DomCrawler et HtmlSanitizer basculent vers le parser HTML5 natif de PHP 8.4 quand il est disponible. Pas de changements de code nécessaires de votre côté. Le parser natif est plus rapide et plus conforme à la spec que le fallback précédent. Sur PHP 8.2 ou 8.3, rien ne change.</p>
<h2 id="translation--staticmessage">Translation : StaticMessage</h2>
<p><code>StaticMessage</code> implémente <code>TranslatableInterface</code> mais ne traduit intentionnellement pas. Elle passe la string inchangée quelle que soit la locale. Le cas d&rsquo;usage : les réponses d&rsquo;API qui doivent rester dans une langue fixe quelle que soit la locale de l&rsquo;utilisateur, ou les entrées de log d&rsquo;audit où on doit préserver le texte original tel quel.</p>
]]></content:encoded></item><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><item><title>PHP 7.2 : adieu mcrypt, bonjour sodium</title><link>https://guillaumedelre.github.io/fr/2018/01/14/php-7.2-adieu-mcrypt-bonjour-sodium/</link><pubDate>Sun, 14 Jan 2018 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2018/01/14/php-7.2-adieu-mcrypt-bonjour-sodium/</guid><description>Part 3 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 7.2 supprime l&amp;#39;extension mcrypt, abandonnée depuis des années, et intègre libsodium — la cryptographie PHP entre enfin dans le XXIe siècle.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.2 est sorti le 30 novembre. La grande nouvelle n&rsquo;est pas une nouvelle fonctionnalité, c&rsquo;est une suppression. <code>mcrypt</code> disparaît.</p>
<p>C&rsquo;est une bonne chose, même si ça ne le semble pas quand c&rsquo;est vous qui devez faire la migration.</p>
<h2 id="le-problème-mcrypt">Le problème mcrypt</h2>
<p><code>mcrypt</code> n&rsquo;est plus maintenu depuis 2007. Plus d&rsquo;une décennie de stagnation dans une bibliothèque cryptographique. Dépréciée en 7.1, elle est retirée définitivement en 7.2. Son remplaçant : <code>sodium</code>, désormais intégré comme extension core.</p>
<p>Sodium est le binding PHP pour <a href="https://libsodium.org" target="_blank" rel="noopener noreferrer">libsodium</a>, une bibliothèque cryptographique moderne conçue autour de valeurs par défaut sûres. Là où mcrypt vous demandait de choisir le bon algorithme, le bon mode et le bon padding (et vous laissait vous planter silencieusement), l&rsquo;API de sodium rend les choix dangereux structurellement difficiles. <code>sodium_crypto_secretbox()</code> pour le chiffrement symétrique, <code>sodium_crypto_box()</code> pour l&rsquo;asymétrique, <code>sodium_crypto_sign()</code> pour les signatures. Les noms disent ce qu&rsquo;on fait.</p>
<p>Si vous avez du code mcrypt en production, la migration est inévitable. Et ça vaut le coup de le faire soigneusement : du code cryptographique qui &ldquo;fonctionne encore&rdquo; peut être silencieusement cassé d&rsquo;une façon que vous ne remarquerez qu&rsquo;il sera trop tard.</p>
<h2 id="le-type-hint-object">Le type hint <code>object</code></h2>
<p>7.2 ajoute <code>object</code> comme type de paramètre et de retour :</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">serialize</span>(<span style="color:#a6e22e">object</span> $data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// accepte n&#39;importe quel objet
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>C&rsquo;est large — n&rsquo;importe quel objet le satisfait — mais c&rsquo;est mieux qu&rsquo;aucun type du tout quand vous ne vous souciez vraiment pas de la classe spécifique. Complète les types existants <code>array</code>, <code>callable</code> et les hints par classe concrète.</p>
<h2 id="argon2-dans-password_hash">Argon2 dans password_hash</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>$hash <span style="color:#f92672">=</span> <span style="color:#a6e22e">password_hash</span>($password, <span style="color:#a6e22e">PASSWORD_ARGON2I</span>);
</span></span></code></pre></div><p><code>PASSWORD_BCRYPT</code> était la valeur par défaut et reste raisonnable, mais Argon2 a remporté le Password Hashing Competition pour une bonne raison : il est memory-hard, ce qui rend le craquage par GPU significativement plus coûteux. Vaut la peine de basculer pour les nouvelles apps.</p>
<p>7.2 est davantage une version sécurité qu&rsquo;une version langage. Supprimez mcrypt, ajoutez sodium, et vous placez la plateforme dans un état où on peut lui faire confiance avec des données sensibles. Les fonctionnalités du langage sont incrémentales. Le changement d&rsquo;infrastructure, lui, ne l&rsquo;est pas.</p>
<h2 id="des-types-de-paramètres-quon-peut-désormais-omettre-intentionnellement">Des types de paramètres qu&rsquo;on peut désormais omettre intentionnellement</h2>
<p>7.2 formalise quelque chose qui était jusqu&rsquo;ici juste une odeur de code : quand vous implémentez une interface ou surchargez une méthode, vous pouvez maintenant omettre complètement le type du paramètre. C&rsquo;est de la contravariance valide au sens du principe de substitution de Liskov.</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">interface</span> <span style="color:#a6e22e">Serializable</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">serialize</span>(<span style="color:#66d9ef">array</span> $data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</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">JsonSerializer</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">Serializable</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">serialize</span>($data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> { <span style="color:#75715e">// pas de type — accepte plus, toujours valide
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">json_encode</span>($data);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Ça paraît bizarre au premier abord. Mais c&rsquo;est logiquement correct : une méthode qui accepte tout est strictement plus permissive qu&rsquo;une qui n&rsquo;accepte que des tableaux. Le système de types est d&rsquo;accord, même si votre relecteur de code lève un sourcil.</p>
<h2 id="des-méthodes-abstraites-qui-évoluent">Des méthodes abstraites qui évoluent</h2>
<p>Quand une classe abstraite étend une autre classe abstraite, elle peut désormais surcharger la méthode abstraite avec une signature différente. La contrainte est directionnelle : les paramètres peuvent être élargis (contravariants), les types de retour peuvent être resserrés (covariants).</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">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">BaseProcessor</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">string</span> $input);
</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">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TypedProcessor</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">BaseProcessor</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>($input)<span style="color:#f92672">:</span> <span style="color:#a6e22e">int</span>; <span style="color:#75715e">// paramètre élargi, type de retour ajouté
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>C&rsquo;était refusé avant 7.2. Ça débloque des abstractions intermédiaires sans forcer chaque classe feuille à répéter la même signature.</p>
<h2 id="virgule-finale-dans-les-imports-groupés">Virgule finale dans les imports groupés</h2>
<p>Petit, mais je remarque son absence quand elle manque. Les imports de namespaces groupés peuvent désormais avoir une virgule finale après le dernier élément :</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">App\Services\</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">UserService</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">OrderService</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">NotificationService</span>, <span style="color:#75715e">// virgule ici — enfin
</span></span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Ça signifie qu&rsquo;on peut réordonner ou ajouter des lignes sans toucher à la ligne précédente. Les diffs git deviennent plus propres, les conflits de merge plus rares.</p>
<h2 id="count-a-développé-une-conscience"><code>count()</code> a développé une conscience</h2>
<p>Avant 7.2, <code>count(null)</code> retournait 0. Silencieusement. Sans warning. C&rsquo;est exactement le genre de chose qui enterre un bug pendant des mois. Maintenant, ça émet un <code>E_WARNING</code> quand vous passez quelque chose qui n&rsquo;est ni un tableau ni un objet <code>Countable</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:#a6e22e">count</span>(<span style="color:#66d9ef">null</span>);  <span style="color:#75715e">// Warning: count(): Parameter must be an array or an object that implements Countable
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">count</span>(<span style="color:#ae81ff">42</span>);    <span style="color:#75715e">// pareil
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">count</span>(<span style="color:#e6db74">&#34;hi&#34;</span>);  <span style="color:#75715e">// pareil
</span></span></span></code></pre></div><p>Le comportement n&rsquo;a pas changé pour les entrées valides. Seul le silence a été brisé. C&rsquo;est la bonne direction.</p>
<h2 id="spl_object_id--ce-que-vous-émuliez-avec-splobjectstorage"><code>spl_object_id()</code> — ce que vous émuliez avec SplObjectStorage</h2>
<p>Si vous avez déjà construit une map indexée par identité d&rsquo;objet, vous avez écrit quelque chose comme ç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>$storage <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SplObjectStorage</span>();
</span></span><span style="display:flex;"><span>$storage[$obj] <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span></code></pre></div><p>7.2 ajoute <code>spl_object_id()</code>, qui retourne un entier unique pour la durée de vie d&rsquo;un objet. C&rsquo;est le même handle interne qu&rsquo;utilise <code>SplObjectStorage</code>, rendu directement accessible :</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>$id <span style="color:#f92672">=</span> <span style="color:#a6e22e">spl_object_id</span>($obj); <span style="color:#75715e">// ex. 42
</span></span></span><span style="display:flex;"><span>$map[$id] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;something&#39;</span>;
</span></span></code></pre></div><p>L&rsquo;entier est réutilisé après la destruction de l&rsquo;objet, donc ne le conservez pas au-delà de la durée de vie de l&rsquo;objet. Dans un contexte bien délimité cependant, c&rsquo;est une clé d&rsquo;identité peu coûteuse.</p>
<h2 id="pdo--les-chaînes-de-caractères-nationales">PDO : les chaînes de caractères nationales</h2>
<p>Quand on travaille avec des bases de données qui distinguent les types de chaînes régulières et nationales (Oracle, SQL Server), 7.2 ajoute les flags nécessaires :</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>$stmt <span style="color:#f92672">=</span> $pdo<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">prepare</span>(<span style="color:#e6db74">&#34;SELECT * FROM users WHERE name = ?&#34;</span>);
</span></span><span style="display:flex;"><span>$stmt<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">bindValue</span>(<span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#39;Ångström&#39;</span>, <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PARAM_STR</span> <span style="color:#f92672">|</span> <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PARAM_STR_NATL</span>);
</span></span></code></pre></div><p>Ou définir une valeur par défaut au niveau de la connexion :</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>$pdo<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setAttribute</span>(<span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">ATTR_DEFAULT_STR_PARAM</span>, <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PARAM_STR_NATL</span>);
</span></span></code></pre></div><p><code>PDO::PARAM_STR_NATL</code> indique que la chaîne est un type caractère national (NCHAR/NVARCHAR). Obscur, certes. Indispensable si vous avez déjà vu vos données Unicode ressortir déformées parce que le driver ne faisait pas la différence.</p>
<h2 id="gd-supporte-les-bmp-et-les-rectangles-de-clipping">GD supporte les BMP et les rectangles de clipping</h2>
<p>Deux choses à connaître. D&rsquo;abord, les fichiers BMP sont désormais des citoyens de première classe dans l&rsquo;extension GD :</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>$image <span style="color:#f92672">=</span> <span style="color:#a6e22e">imagecreatefrombmp</span>(<span style="color:#e6db74">&#39;photo.bmp&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">imagebmp</span>($image, <span style="color:#e6db74">&#39;output.bmp&#39;</span>);
</span></span></code></pre></div><p>Ensuite, on peut maintenant définir un rectangle de clipping pour que les opérations de dessin n&rsquo;affectent qu&rsquo;une portion de l&rsquo;image :</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">imagesetclip</span>($image, <span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">150</span>); <span style="color:#75715e">// x1, y1, x2, y2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// tout ce qui est dessiné en dehors de ce rectangle est silencieusement ignoré
</span></span></span></code></pre></div><p>Aucune de ces fonctionnalités ne transforme la façon dont la plupart des apps fonctionnent, mais les deux remplacent &ldquo;installer une bibliothèque supplémentaire&rdquo; par &ldquo;c&rsquo;est juste dans le core maintenant.&rdquo;</p>
<h2 id="mb_chr-et-mb_ord--le-chr-et-ord-dunicode"><code>mb_chr()</code> et <code>mb_ord()</code> — le <code>chr()</code> et <code>ord()</code> d&rsquo;Unicode</h2>
<p>PHP a <code>chr()</code> et <code>ord()</code> depuis toujours. Ils travaillent sur des octets. Pour les points de code Unicode, vous étiez livré à vous-même. 7.2 ajoute les équivalents multibyte :</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>$char <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_chr</span>(<span style="color:#ae81ff">0x1F600</span>); <span style="color:#75715e">// retourne l&#39;emoji 😀
</span></span></span><span style="display:flex;"><span>$code <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_ord</span>(<span style="color:#e6db74">&#39;é&#39;</span>);     <span style="color:#75715e">// retourne 233
</span></span></span></code></pre></div><p>Et <code>mb_scrub()</code>, qui supprime les séquences d&rsquo;octets invalides d&rsquo;une chaîne plutôt que d&rsquo;échouer silencieusement ou de lancer une exception :</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>$clean <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_scrub</span>($untrustedInput, <span style="color:#e6db74">&#39;UTF-8&#39;</span>);
</span></span></code></pre></div><p>Pratique à toute frontière externe : réponses API, uploads de fichiers, lectures en base depuis des systèmes legacy.</p>
<h2 id="dépréciations-à-connaître-avant-larrivée-de-74">Dépréciations à connaître avant l&rsquo;arrivée de 7.4</h2>
<p>Plusieurs choses ont été mollement dépréciées en 7.2 et deviendront des erreurs dans les versions ultérieures. Celles qui risquent le plus de piquer :</p>
<p><code>__autoload()</code> est déprécié. Si vous enregistrez encore une fonction d&rsquo;autoload globale au lieu d&rsquo;utiliser <code>spl_autoload_register()</code>, corrigez ça avant que ça devienne fatal.</p>
<p><code>create_function()</code> est déprécié. C&rsquo;est un wrapper autour de <code>eval()</code> et ça a toujours été dangereux. Utilisez une closure :</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>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">create_function</span>(<span style="color:#e6db74">&#39;$x&#39;</span>, <span style="color:#e6db74">&#39;return $x * 2;&#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>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">fn</span>($x) <span style="color:#f92672">=&gt;</span> $x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>;
</span></span></code></pre></div><p><code>each()</code> est déprécié. Le pattern de boucle qu&rsquo;il permettait s&rsquo;écrit mieux avec <code>foreach</code>. Aucune perte ici.</p>
<p><code>parse_str()</code> sans second argument déverse les valeurs parsées dans la table des symboles locale — un problème de sécurité qui n&rsquo;aurait jamais dû être autorisé. Passez toujours la variable de sortie :</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">parse_str</span>($queryString, $params); <span style="color:#75715e">// correct
</span></span></span></code></pre></div><p>Le cast <code>(unset)</code> est déprécié parce qu&rsquo;il retourne toujours <code>null</code>, que vous pouvez simplement écrire <code>null</code>. Une syntaxe complètement inutile qui n&rsquo;aurait jamais dû exister.</p>
]]></content:encoded></item></channel></rss>