<?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>Readonly on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/readonly/</link><description>Recent content in Readonly on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Sun, 22 Jan 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/readonly/index.xml" rel="self" type="application/rss+xml"/><item><title>PHP 8.2 : les classes readonly et la dépréciation qui compte vraiment</title><link>https://guillaumedelre.github.io/fr/2023/01/22/php-8.2-les-classes-readonly-et-la-d%C3%A9pr%C3%A9ciation-qui-compte-vraiment/</link><pubDate>Sun, 22 Jan 2023 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2023/01/22/php-8.2-les-classes-readonly-et-la-d%C3%A9pr%C3%A9ciation-qui-compte-vraiment/</guid><description>Part 8 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 8.2 introduit les classes readonly, déprécie les propriétés dynamiques, et ajoute les types en forme normale disjonctive.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.2 est sorti le 8 décembre. Les classes readonly font les gros titres. La dépréciation des propriétés dynamiques, elle, demande votre attention concrète.</p>
<h2 id="les-propriétés-dynamiques-dépréciées">Les propriétés dynamiques dépréciées</h2>
<p>PHP a toujours permis d&rsquo;ajouter des propriétés à des objets sans les déclarer dans la classe :</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>$user <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">User</span>();
</span></span><span style="display:flex;"><span>$user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Alice&#39;</span>; <span style="color:#75715e">// aucune déclaration, aucune erreur... jusqu&#39;ici
</span></span></span></code></pre></div><p>En 8.2, ça déclenche un avertissement de dépréciation. En PHP 9.0, ce sera une erreur fatale. Le délai de grâce existe, mais le compteur tourne.</p>
<p>La logique est solide : les propriétés dynamiques sont une source classique de typos qui passent silencieusement (écrivez <code>$user-&gt;nmae</code> et PHP crée simplement une nouvelle propriété au lieu de se plaindre). Des déclarations explicites rendent le contrat de la classe lisible et donnent aux outils matière à travailler.</p>
<p>La migration est essentiellement mécanique : déclarez les propriétés, ou posez <code>#[AllowDynamicProperties]</code> sur les classes legacy que vous ne pouvez pas encore toucher.</p>
<h2 id="les-classes-readonly">Les classes readonly</h2>
<p>8.1 avait ajouté <code>readonly</code> sur les propriétés individuelles. 8.2 l&rsquo;ajoute sur la déclaration de classe elle-même :</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">readonly</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Point</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">float</span> $x,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $y,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $z,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Toutes les propriétés promues et déclarées explicitement deviennent readonly automatiquement. Les value objects (coordonnées, montants monétaires, identifiants) sont la cible évidente. La syntaxe est propre et l&rsquo;intention se lit clairement.</p>
<p>Une contrainte : les classes readonly ne peuvent pas avoir de propriétés non typées, ce qui était déjà une mauvaise idée avec readonly de toute façon.</p>
<h2 id="les-types-dnf">Les types DNF</h2>
<p>Les types en Forme Normale Disjonctive permettent de combiner types union et types intersection :</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><span style="color:#f92672">|</span><span style="color:#66d9ef">null</span> $collection)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#f92672">...</span> }
</span></span></code></pre></div><p><code>(Countable&amp;Iterator)|null</code> : un objet qui implémente les deux interfaces, ou null. Cela couvre des expressions de type que les unions de 8.0 et les intersections de 8.1 approchaient chacune sans pouvoir les représenter ensemble.</p>
<h2 id="lextension-random">L&rsquo;extension Random</h2>
<p>Une extension <code>Random</code> dédiée remplace les fonctions éparpillées <code>rand()</code>, <code>mt_rand()</code>, <code>random_int()</code> par une API orientée objet :</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>$rng <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Random\Randomizer</span>();
</span></span><span style="display:flex;"><span>$rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getInt</span>(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">100</span>);
</span></span><span style="display:flex;"><span>$rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">shuffleArray</span>($items);
</span></span></code></pre></div><p>Les moteurs sont interchangeables : <code>Mersenne Twister</code>, <code>PCG64</code>, <code>Xoshiro256StarStar</code>, ou <code>CryptoSafeEngine</code> pour les contextes sensibles à la sécurité. Même code, moteur déterministe avec seed dans les tests, moteur cryptographique en production.</p>
<p>8.2 est une version de consolidation. La dépréciation des propriétés dynamiques est la seule décision à prendre maintenant.</p>
<h2 id="null-false-et-true-comme-types-autonomes"><code>null</code>, <code>false</code>, et <code>true</code> comme types autonomes</h2>
<p>PHP avait les types nullable depuis 7.1 et les types union depuis 8.0, mais <code>null</code> comme déclaration de type autonome n&rsquo;était pas valide. 8.2 corrige ç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">function</span> <span style="color:#a6e22e">alwaysNull</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">null</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</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">disabled</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">false</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">enabled</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>false</code> et <code>true</code> comme types autonomes sont utiles quand on veut être précis sur ce qu&rsquo;une fonction peut réellement retourner. C&rsquo;est étroit mais juste : une fonction qui retourne <code>false</code> en cas d&rsquo;échec et une chaîne en cas de succès devrait déclarer <code>string|false</code>, et maintenant les deux côtés de cette union sont de vrais types.</p>
<h2 id="les-constantes-dans-les-traits">Les constantes dans les traits</h2>
<p>Les traits pouvaient contenir des propriétés et des méthodes. Les constantes étaient le trou dans la raquette. 8.2 le comble :</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">trait</span> <span style="color:#a6e22e">Timestamps</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">DATE_FORMAT</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Y-m-d H:i:s&#39;</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">formatCreatedAt</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</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">createdAt</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">format</span>(<span style="color:#a6e22e">self</span><span style="color:#f92672">::</span><span style="color:#a6e22e">DATE_FORMAT</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">class</span> <span style="color:#a6e22e">Article</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">use</span> <span style="color:#a6e22e">Timestamps</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">echo</span> <span style="color:#a6e22e">Article</span><span style="color:#f92672">::</span><span style="color:#a6e22e">DATE_FORMAT</span>; <span style="color:#75715e">// &#39;Y-m-d H:i:s&#39;
</span></span></span></code></pre></div><p>La constante appartient à la classe qui utilise le trait, pas au trait lui-même, donc on ne peut pas accéder à <code>Timestamps::DATE_FORMAT</code> directement. Comportement de scope attendu, cohérent avec le fonctionnement des méthodes de trait.</p>
<h2 id="sensitiveparameter"><code>#[SensitiveParameter]</code></h2>
<p>Les stack traces ont toujours été un risque : les arguments de fonction sont loggés verbatim, ce qui veut dire que les mots de passe et les tokens se retrouvent dans les logs d&rsquo;erreur et les dashboards de monitoring. 8.2 ajoute un attribut pour stopper ç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">function</span> <span style="color:#a6e22e">authenticate</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">string</span> $user,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[\SensitiveParameter] string $password,
</span></span></span><span style="display:flex;"><span>)<span style="color:#f92672">:</span> <span style="color:#a6e22e">bool</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// si ça lève une exception, la stack trace affiche :
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// authenticate(&#39;alice&#39;, Object(SensitiveParameterValue))
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">hash</span>(<span style="color:#e6db74">&#39;sha256&#39;</span>, $password) <span style="color:#f92672">===</span> <span style="color:#a6e22e">getStoredHash</span>($user);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>La valeur du paramètre dans la trace est remplacée par un objet <code>SensitiveParameterValue</code>. Un attribut, zéro excuse pour ne pas l&rsquo;ajouter sur chaque fonction qui touche à des credentials.</p>
<h2 id="les-syntaxes-dinterpolation-de-chaînes-dépréciées">Les syntaxes d&rsquo;interpolation de chaînes dépréciées</h2>
<p>Deux façons d&rsquo;interpoler des expressions dans des chaînes sont dépréciées en 8.2 :</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>$name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;world&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Celles-ci sont dépréciées :
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;Hello </span><span style="color:#e6db74">${</span>name<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;       <span style="color:#75715e">// utiliser &#34;$name&#34; ou &#34;{$name}&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;Hello </span><span style="color:#e6db74">${</span>getName()<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;  <span style="color:#75715e">// utiliser &#34;{$this-&gt;getName()}&#34;
</span></span></span></code></pre></div><p>Les formes <code>${...}</code> créaient une ambiguïté entre les variables variables et les expressions. La syntaxe plus propre <code>{$...}</code> a toujours été là et fait la même chose. C&rsquo;est essentiellement un travail de recherche-remplacement sur les bases de code qui ont adopté les formes dépréciées par habitude.</p>
<h2 id="utf8_encode-et-utf8_decode-dépréciées"><code>utf8_encode()</code> et <code>utf8_decode()</code> dépréciées</h2>
<p>Ces deux fonctions sont dépréciées en 8.2 et disparaissent en 9.0. Leur comportement a toujours été plus étroit que ce que les noms suggéraient : <code>utf8_encode()</code> convertit ISO-8859-1 en UTF-8, pas &ldquo;n&rsquo;importe quel encodage en UTF-8&rdquo;.</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">// Déprécié en 8.2 :
</span></span></span><span style="display:flex;"><span>$utf8 <span style="color:#f92672">=</span> <span style="color:#a6e22e">utf8_encode</span>($latin1String);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Utiliser à la place :
</span></span></span><span style="display:flex;"><span>$utf8 <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_convert_encoding</span>($latin1String, <span style="color:#e6db74">&#39;UTF-8&#39;</span>, <span style="color:#e6db74">&#39;ISO-8859-1&#39;</span>);
</span></span></code></pre></div><p><code>mb_convert_encoding()</code> ou <code>iconv()</code> gèrent le cas général. Si vous traitez vraiment des entrées en Latin-1, le remplacement est direct.</p>
<h2 id="les-fonctions-de-chaîne-indépendantes-de-la-locale">Les fonctions de chaîne indépendantes de la locale</h2>
<p>Plusieurs fonctions de chaîne variaient silencieusement leur comportement selon la locale système, produisant des résultats différents en production par rapport à un container de dev. En 8.2, elles sont indépendantes de la locale et ne gèrent que l&rsquo;ASCII :</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">// strtolower, strtoupper, stristr, stripos, strripos,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// lcfirst, ucfirst, ucwords, str_ireplace font maintenant une conversion
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// de casse ASCII uniquement.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Pour un comportement sensible à la locale, utiliser les équivalents mb_* :
</span></span></span><span style="display:flex;"><span>$lowered <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_strtolower</span>($text, <span style="color:#e6db74">&#39;UTF-8&#39;</span>);
</span></span></code></pre></div><p>C&rsquo;est un correctif de cohérence. Si votre code reposait sur le comportement sensible à la locale de ces fonctions, il était déjà cassé sur des systèmes avec des configurations de locale différentes. 8.2 rend le comportement déterministe partout, ce qui est ce qu&rsquo;on voulait vraiment.</p>
<h2 id="str_split-sur-une-chaîne-vide"><code>str_split()</code> sur une chaîne vide</h2>
<p>Un changement de comportement discret à noter :</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">// PHP 8.1 : str_split(&#39;&#39;) === [&#39;&#39;]
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// PHP 8.2 : str_split(&#39;&#39;) === []
</span></span></span></code></pre></div><p>Le nouveau comportement a plus de sens : découper rien ne produit rien. Si vous vérifiez <code>count(str_split($input))</code>, une entrée vide ne produit plus un count de 1.</p>
]]></content:encoded></item></channel></rss>