<?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>Typage on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/typage/</link><description>Recent content in Typage on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Sun, 05 Jan 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/typage/index.xml" rel="self" type="application/rss+xml"/><item><title>PHP 8.4 : les property hooks et la fin de la cérémonie getter/setter</title><link>https://guillaumedelre.github.io/fr/2025/01/05/php-8.4-les-property-hooks-et-la-fin-de-la-c%C3%A9r%C3%A9monie-getter/setter/</link><pubDate>Sun, 05 Jan 2025 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2025/01/05/php-8.4-les-property-hooks-et-la-fin-de-la-c%C3%A9r%C3%A9monie-getter/setter/</guid><description>Part 10 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 8.4 apporte les property hooks : logique get/set directement sur les propriétés, remplaçant vingt ans de boilerplate getter/setter.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.4 est sorti le 21 novembre. Les property hooks sont la fonctionnalité. Tout le reste, et il y en a beaucoup, est secondaire.</p>
<h2 id="les-property-hooks">Les property hooks</h2>
<p>Pendant vingt ans, si on voulait du comportement à l&rsquo;accès d&rsquo;une propriété en PHP, il fallait écrire des getters et setters :</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">private</span> <span style="color:#a6e22e">string</span> $_name;
</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">getName</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> { <span style="color:#66d9ef">return</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">_name</span>; }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">setName</span>(<span style="color:#a6e22e">string</span> $name)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">_name</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">strtoupper</span>($name);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>PHP 8.4 ajoute des hooks directement sur la propriété :</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:#a6e22e">string</span> $name {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">set</span>(<span style="color:#a6e22e">string</span> $name) {
</span></span><span style="display:flex;"><span>            $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">strtoupper</span>($name);
</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>On peut définir les hooks <code>get</code> et <code>set</code> indépendamment. Une propriété avec seulement un hook <code>get</code> est calculée à l&rsquo;accè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">class</span> <span style="color:#a6e22e">Circle</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $area {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">get</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">M_PI</span> <span style="color:#f92672">*</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">radius</span> <span style="color:#f92672">**</span> <span style="color:#ae81ff">2</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">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(<span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $radius) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Pas de stockage backing, pas de méthode getter explicite, support IDE complet. Les interfaces peuvent aussi déclarer des propriétés avec des hooks, ce qui signifie que les contrats peuvent maintenant spécifier un comportement à l&rsquo;accès aux propriétés, quelque chose qui était tout simplement impossible avant.</p>
<h2 id="la-visibilité-asymétrique">La visibilité asymétrique</h2>
<p>Une option plus légère pour quand on veut juste une lecture publique et une écriture privée :</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">Version</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">private</span>(<span style="color:#a6e22e">set</span>) <span style="color:#a6e22e">string</span> $value <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;1.0.0&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$v <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Version</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $v<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">value</span>;      <span style="color:#75715e">// fonctionne
</span></span></span><span style="display:flex;"><span>$v<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;2.0&#39;</span>;  <span style="color:#75715e">// Error
</span></span></span></code></pre></div><p>Élimine le pattern <code>private $x</code> + <code>public getX()</code> pour les propriétés publiques en lecture seule sans avoir besoin de la sémantique readonly complète.</p>
<h2 id="array_find-et-amis">array_find() et amis</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>$first <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_find</span>($users, <span style="color:#a6e22e">fn</span>($u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isActive</span>());
</span></span><span style="display:flex;"><span>$any   <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_any</span>($users, <span style="color:#a6e22e">fn</span>($u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isPremium</span>());
</span></span><span style="display:flex;"><span>$all   <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_all</span>($users, <span style="color:#a6e22e">fn</span>($u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isVerified</span>());
</span></span></code></pre></div><p>Ces fonctions existent dans la bibliothèque standard de chaque autre langage depuis des décennies. En PHP, il fallait utiliser <code>array_filter()</code> + accès par index ou écrire une boucle manuelle. Elles existent maintenant : <code>array_find()</code>, <code>array_find_key()</code>, <code>array_any()</code>, <code>array_all()</code>.</p>
<h2 id="instanciation-sans-parenthèses-supplémentaires">Instanciation sans parenthèses supplémentaires</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><span style="color:#75715e">// avant
</span></span></span><span style="display:flex;"><span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyClass</span>())<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">method</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:#66d9ef">new</span> <span style="color:#a6e22e">MyClass</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">method</span>();
</span></span></code></pre></div><p>Une restriction syntaxique qui était toujours agaçante et jamais justifiée est supprimée.</p>
<h2 id="les-objets-paresseux">Les objets paresseux</h2>
<p>Des objets dont l&rsquo;initialisation est différée jusqu&rsquo;au premier accès à une propriété :</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>$user <span style="color:#f92672">=</span> $reflector<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">newLazyProxy</span>(<span style="color:#a6e22e">fn</span>() <span style="color:#f92672">=&gt;</span> $repository<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">find</span>($id));
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Pas d&#39;appel en base encore
</span></span></span><span style="display:flex;"><span>$user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">name</span>; <span style="color:#75715e">// Maintenant le proxy s&#39;initialise
</span></span></span></code></pre></div><p>Le public direct est les auteurs de frameworks ORM et de conteneurs DI, pas les développeurs d&rsquo;applications. Mais l&rsquo;effet se fait sentir dans chaque application qui utilise Doctrine ou Symfony : le lazy loading implémenté au niveau du langage plutôt qu&rsquo;à travers la génération de code.</p>
<p>PHP 8.4 est un langage qui ressemble à peine au PHP 5 avec lequel la plupart d&rsquo;entre nous avons commencé. Les property hooks en particulier : ce ne sont pas des contournements, ce sont une fonctionnalité de conception.</p>
<h2 id="deprecated-pour-son-propre-code">#[\Deprecated] pour son propre code</h2>
<p>PHP émet des notices de dépréciation pour les fonctions intégrées depuis des années. 8.4 permet de câbler le même mécanisme dans son propre 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">ApiClient</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[\Deprecated(
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">message</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Use fetchJson() instead&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">since</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;2.0&#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">get</span>(<span style="color:#a6e22e">string</span> $url)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> { <span style="color:#f92672">...</span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Appeler une méthode dépréciée émet maintenant <code>E_USER_DEPRECATED</code>, exactement comme appeler <code>mysql_connect()</code>. Les IDEs le détectent, les analyseurs statiques le signalent, le log d&rsquo;erreurs le capture. Avant ça, la seule option était un commentaire PHPDoc <code>@deprecated</code> : bien pour les IDEs, complètement invisible pour le moteur.</p>
<h2 id="bcmathnumber-rend-la-précision-arbitraire-utilisable">BcMath\Number rend la précision arbitraire utilisable</h2>
<p>Les fonctions <code>bcmath</code> existent en PHP depuis toujours, mais leur API procédurale rend tout chaînage pénible. 8.4 ajoute <code>BcMath\Number</code>, un wrapper objet avec surcharge d&rsquo;opérateurs :</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>$a <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">BcMath\Number</span>(<span style="color:#e6db74">&#39;10.5&#39;</span>);
</span></span><span style="display:flex;"><span>$b <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">BcMath\Number</span>(<span style="color:#e6db74">&#39;3.2&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $a <span style="color:#f92672">+</span> $b;             <span style="color:#75715e">// BcMath\Number(&#39;13.7&#39;)
</span></span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $a <span style="color:#f92672">*</span> $b <span style="color:#f92672">-</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">BcMath\Number</span>(<span style="color:#e6db74">&#39;1&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $result;                  <span style="color:#75715e">// 32.6
</span></span></span></code></pre></div><p>Les opérateurs <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>**</code>, <code>%</code> fonctionnent tous. L&rsquo;objet est immutable. L&rsquo;échelle se propage automatiquement à travers les opérations. Les calculs financiers, qui nécessitaient des chaînes de <code>bcadd(bcmul(...), ...)</code>, se lisent maintenant comme de l&rsquo;arithmétique.</p>
<p>De nouvelles fonctions procédurales complètent le tableau : <code>bcceil()</code>, <code>bcfloor()</code>, <code>bcround()</code>, <code>bcdivmod()</code>.</p>
<h2 id="lenum-roundingmode-remplace-les-constantes-php_round_">L&rsquo;enum RoundingMode remplace les constantes PHP_ROUND_*</h2>
<p><code>round()</code> a toujours pris un <code>$mode</code> entier depuis un ensemble de constantes <code>PHP_ROUND_*</code>. 8.4 les remplace par un enum <code>RoundingMode</code> avec des noms plus propres et quatre modes supplémentaires qui n&rsquo;étaient pas disponibles avant :</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">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfAwayFromZero</span>);  <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfTowardsZero</span>);   <span style="color:#75715e">// 2
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfEven</span>);          <span style="color:#75715e">// 2 (arrondi du banquier)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfOdd</span>);           <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Les quatre nouveaux modes (disponibles uniquement via l&#39;enum)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.3</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">TowardsZero</span>);       <span style="color:#75715e">// 2
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.7</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">AwayFromZero</span>);      <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.3</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PositiveInfinity</span>);  <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.3</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">NegativeInfinity</span>);  <span style="color:#75715e">// 2
</span></span></span></code></pre></div><p>Les anciennes constantes <code>PHP_ROUND_*</code> fonctionnent encore. L&rsquo;enum est la voie à suivre.</p>
<h2 id="les-fonctions-de-string-multibyte-qui-auraient-dû-exister">Les fonctions de string multibyte qui auraient dû exister</h2>
<p><code>mb_trim()</code>, <code>mb_ltrim()</code>, <code>mb_rtrim()</code> : des fonctions de trim qui respectent les frontières de caractères multibyte, pas juste les espaces ASCII. Aussi nouvelles : <code>mb_ucfirst()</code> et <code>mb_lcfirst()</code> pour la mise en majuscule correcte des strings 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>$s <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;\u{200B}hello\u{200B}&#34;</span>; <span style="color:#75715e">// Espaces de largeur nulle
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">mb_trim</span>($s);              <span style="color:#75715e">// &#34;hello&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">mb_ucfirst</span>(<span style="color:#e6db74">&#39;über&#39;</span>);       <span style="color:#75715e">// &#34;Über&#34;
</span></span></span></code></pre></div><p>Ces fonctions comblent des lacunes présentes depuis que <code>mbstring</code> a été introduit.</p>
<h2 id="request_parse_body-pour-les-requêtes-non-post">request_parse_body() pour les requêtes non-POST</h2>
<p>PHP parse automatiquement <code>application/x-www-form-urlencoded</code> et <code>multipart/form-data</code> dans <code>$_POST</code> et <code>$_FILES</code>, mais seulement pour les requêtes POST. Les requêtes PATCH et PUT avec les mêmes types de contenu nécessitaient un parsing manuel avec <code>file_get_contents('php://input')</code> et du code personnalisé.</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">// Dans un handler PATCH
</span></span></span><span style="display:flex;"><span>[$_POST, $_FILES] <span style="color:#f92672">=</span> <span style="color:#a6e22e">request_parse_body</span>();
</span></span></code></pre></div><p>La fonction retourne un tuple. Même logique de parsing que PHP utilise pour POST, maintenant disponible pour n&rsquo;importe quelle méthode HTTP.</p>
<h2 id="une-nouvelle-api-dom-qui-suit-la-spec">Une nouvelle API DOM qui suit la spec</h2>
<p>L&rsquo;API <code>DOMDocument</code> existante était construite sur une spec DOM level 3 plus ancienne avec des spécificités PHP superposées. 8.4 ajoute un namespace <code>Dom\</code> parallèle qui implémente le WHATWG Living Standard :</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>$doc <span style="color:#f92672">=</span> <span style="color:#a6e22e">Dom\HTMLDocument</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromString</span>(<span style="color:#e6db74">&#39;&lt;p class=&#34;lead&#34;&gt;Hello&lt;/p&gt;&#39;</span>);
</span></span><span style="display:flex;"><span>$p <span style="color:#f92672">=</span> $doc<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">&#39;p&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $p<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">classList</span>;  <span style="color:#75715e">// &#34;lead&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $p<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">id</span>;         <span style="color:#75715e">// &#34;&#34;
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$doc2 <span style="color:#f92672">=</span> <span style="color:#a6e22e">Dom\HTMLDocument</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromFile</span>(<span style="color:#e6db74">&#39;page.html&#39;</span>);
</span></span></code></pre></div><p><code>Dom\HTMLDocument</code> parse correctement HTML5, tag soup inclus. <code>Dom\XMLDocument</code> gère le XML strict. Les nouvelles classes sont strictes sur les types, retournent les bons types de nœuds, et exposent des propriétés modernes comme <code>classList</code>, <code>id</code>, <code>className</code>. L&rsquo;ancien <code>DOMDocument</code> reste, inchangé, pour la compatibilité ascendante.</p>
<h2 id="pdo-reçoit-des-sous-classes-spécifiques-au-driver">PDO reçoit des sous-classes spécifiques au driver</h2>
<p><code>PDO::connect()</code> et l&rsquo;instanciation directe retournent maintenant des sous-classes spécifiques au driver quand elles sont disponibles :</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">=</span> <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">connect</span>(<span style="color:#e6db74">&#39;mysql:host=localhost;dbname=test&#39;</span>, <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#e6db74">&#39;pass&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// $pdo est maintenant une instance Pdo\Mysql
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$pdo <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Pdo\Pgsql</span>(<span style="color:#e6db74">&#39;pgsql:host=localhost;dbname=test&#39;</span>, <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#e6db74">&#39;pass&#39;</span>);
</span></span></code></pre></div><p>Chaque sous-classe driver (<code>Pdo\Mysql</code>, <code>Pdo\Pgsql</code>, <code>Pdo\Sqlite</code>, <code>Pdo\Firebird</code>, <code>Pdo\Odbc</code>, <code>Pdo\DbLib</code>) peut exposer des méthodes spécifiques au driver sans polluer l&rsquo;interface <code>PDO</code> de base. Doctrine, Laravel et autres ORMs similaires peuvent maintenant type-hinter contre la classe de driver spécifique quand ils ont besoin d&rsquo;un comportement spécifique au driver.</p>
<h2 id="openssl-reçoit-le-support-des-clés-modernes">OpenSSL reçoit le support des clés modernes</h2>
<p><code>openssl_pkey_new()</code> et les fonctions associées supportent maintenant Curve25519 et Curve448, les courbes elliptiques modernes qui ont remplacé les anciennes courbes NIST dans la plupart des recommandations de sécurité :</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>$key <span style="color:#f92672">=</span> <span style="color:#a6e22e">openssl_pkey_new</span>([<span style="color:#e6db74">&#39;curve_name&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;ed25519&#39;</span>, <span style="color:#e6db74">&#39;private_key_type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">OPENSSL_KEYTYPE_EC</span>]);
</span></span><span style="display:flex;"><span>$details <span style="color:#f92672">=</span> <span style="color:#a6e22e">openssl_pkey_get_details</span>($key);
</span></span></code></pre></div><p><code>x25519</code> et <code>x448</code> pour l&rsquo;échange de clés, <code>ed25519</code> et <code>ed448</code> pour les signatures. Les quatre fonctionnent maintenant avec <code>openssl_sign()</code> et <code>openssl_verify()</code>.</p>
<h2 id="pcre--lookbehind-de-longueur-variable">PCRE : lookbehind de longueur variable</h2>
<p>La mise à jour de la bibliothèque PCRE2 embarquée (10.44) apporte les assertions lookbehind de longueur variable, quelque chose que les moteurs regex de Perl et Python avaient et que PHP ne pouvait pas faire :</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">// Correspondre &#34;bar&#34; seulement quand précédé de &#34;foo&#34; ou &#34;foooo&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">preg_match</span>(<span style="color:#e6db74">&#39;/(?&lt;=foo+)bar/&#39;</span>, <span style="color:#e6db74">&#39;foooobar&#39;</span>, $matches);
</span></span></code></pre></div><p>Les assertions lookbehind nécessitaient auparavant un pattern de largeur fixe. Elles peuvent maintenant correspondre à des patterns de longueur variable. Le modificateur <code>r</code> (<code>PCRE2_EXTRA_CASELESS_RESTRICT</code>) est aussi nouveau : il empêche le mélange de caractères ASCII et non-ASCII dans les correspondances insensibles à la casse, fermant une classe d&rsquo;attaques de confusion Unicode.</p>
<h2 id="datetime-reçoit-les-microsecondes-et-une-factory-de-timestamp">DateTime reçoit les microsecondes et une factory de timestamp</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>$dt <span style="color:#f92672">=</span> <span style="color:#a6e22e">DateTimeImmutable</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromTimestamp</span>(<span style="color:#ae81ff">1700000000.5</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $dt<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getMicrosecond</span>(); <span style="color:#75715e">// 500000
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$with_micros <span style="color:#f92672">=</span> $dt<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setMicrosecond</span>(<span style="color:#ae81ff">123456</span>);
</span></span></code></pre></div><p><code>createFromTimestamp()</code> accepte un float pour une précision sous-seconde. <code>getMicrosecond()</code> et <code>setMicrosecond()</code> complètent l&rsquo;API pour le composant microseconde qui était à l&rsquo;intérieur de <code>DateTime</code> mais inaccessible directement.</p>
<h2 id="fpow-pour-la-conformité-ieee-754">fpow() pour la conformité IEEE 754</h2>
<p><code>pow(0, -2)</code> en PHP retournait historiquement une valeur définie par l&rsquo;implémentation. 8.4 déprécie <code>pow()</code> avec une base zéro et un exposant négatif et introduit <code>fpow()</code>, qui suit strictement IEEE 754 : <code>fpow(0, -2)</code> retourne <code>INF</code>, comme le standard le définit :</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">echo</span> <span style="color:#a6e22e">fpow</span>(<span style="color:#ae81ff">2.0</span>, <span style="color:#ae81ff">3.0</span>);   <span style="color:#75715e">// 8.0
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">fpow</span>(<span style="color:#ae81ff">0.0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1.0</span>);  <span style="color:#75715e">// INF
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">fpow</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">1.0</span>, <span style="color:#a6e22e">INF</span>);  <span style="color:#75715e">// 1.0
</span></span></span></code></pre></div><p>À retenir dans tout code faisant des calculs mathématiques où la conformité IEEE compte.</p>
<h2 id="le-coût-de-bcrypt-augmente">Le coût de bcrypt augmente</h2>
<p>Le coût par défaut pour <code>password_hash()</code> avec <code>PASSWORD_BCRYPT</code> est passé de <code>10</code> à <code>12</code>. Ça impacte tout code appelant <code>password_hash($pass, PASSWORD_BCRYPT)</code> sans coût explicite. L&rsquo;objectif est de maintenir le défaut grossièrement à &ldquo;quelques centaines de millisecondes sur du matériel moderne&rdquo; à mesure que le matériel devient plus rapide.</p>
<p>Si on stocke des hash bcrypt et qu&rsquo;on monte sur 8.4, les hash existants restent valides : <code>password_verify()</code> lit le coût depuis le hash lui-même. Les nouveaux hash utilisent le coût 12. <code>password_needs_rehash()</code> retourne true pour les anciens hash si on passe <code>['cost' =&gt; 12]</code>, donc on peut les mettre à jour à la prochaine connexion.</p>
<h2 id="les-dépréciations-qui-comptent">Les dépréciations qui comptent</h2>
<p>Les paramètres implicitement nullable sont dépréciés. Si un paramètre a un défaut de <code>null</code>, le type doit le dire explicitement :</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é dans 8.4
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">foo</span>(<span style="color:#a6e22e">string</span> $s <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>) {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Correct
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">foo</span>(<span style="color:#f92672">?</span><span style="color:#a6e22e">string</span> $s <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>) {}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">foo</span>(<span style="color:#a6e22e">string</span><span style="color:#f92672">|</span><span style="color:#66d9ef">null</span> $s <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>) {}
</span></span></code></pre></div><p><code>trigger_error()</code> avec <code>E_USER_ERROR</code> est déprécié : le remplacer par une exception ou <code>exit()</code>. Le niveau <code>E_USER_ERROR</code> a toujours été un hybride maladroit entre une erreur récupérable et une erreur fatale, et personne n&rsquo;était sûr lequel.</p>
<p><code>lcg_value()</code> est aussi déprécié. Utiliser <code>Random\Randomizer::getFloat()</code> à la place. Le générateur LCG avait de mauvaises propriétés d&rsquo;aléatoire et aucun contrôle de graine.</p>
]]></content:encoded></item><item><title>PHP 8.3 : les constantes typées et les petites victoires qui restent</title><link>https://guillaumedelre.github.io/fr/2024/01/07/php-8.3-les-constantes-typ%C3%A9es-et-les-petites-victoires-qui-restent/</link><pubDate>Sun, 07 Jan 2024 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2024/01/07/php-8.3-les-constantes-typ%C3%A9es-et-les-petites-victoires-qui-restent/</guid><description>Part 9 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 8.3 ajoute les constantes de classe typées, une fonction json_validate, et une façon plus propre d&amp;#39;accéder dynamiquement aux constantes de classe.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.3 est sorti le 23 novembre. Une version discrète par les standards PHP : pas de bouleversement à la taille des enums, pas de JIT. Ce qu&rsquo;elle apporte, c&rsquo;est un ensemble ciblé d&rsquo;améliorations qui comblent des lacunes de longue date dans le système de types et ajoutent des fonctions qui auraient dû exister depuis des années.</p>
<h2 id="les-constantes-de-classe-typées">Les constantes de classe typées</h2>
<p>Les constantes de classe n&rsquo;ont jamais été typées depuis leur introduction. PHP 8.3 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">interface</span> <span style="color:#a6e22e">HasVersion</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">string</span> <span style="color:#a6e22e">VERSION</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">App</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">HasVersion</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">string</span> <span style="color:#a6e22e">VERSION</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;1.0.0&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Sans constantes typées, une constante d&rsquo;interface pouvait être redéfinie avec un type complètement différent dans une classe implémentante sans que rien ne se plaigne. Les constantes typées comblent ce trou, et sur les bases de code pilotées par les interfaces, l&rsquo;impact est immédiat.</p>
<h2 id="laccès-dynamique-aux-constantes-de-classe">L&rsquo;accès dynamique aux constantes de classe</h2>
<p>Une lacune qui nécessitait un contournement depuis que les constantes existent :</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;STATUS&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">MyClass</span><span style="color:#f92672">::</span>{$name}; <span style="color:#75715e">// ça marche maintenant
</span></span></span></code></pre></div><p>Avant, accéder à une constante avec un nom dynamique signifiait appeler <code>constant('MyClass::STATUS')</code>. La nouvelle syntaxe est cohérente avec la façon dont PHP gère déjà les variables variables et les appels de méthodes dynamiques.</p>
<h2 id="readonly-peut-maintenant-être-modifié-dans-clone">readonly peut maintenant être modifié dans clone</h2>
<p>Une limitation spécifique mais vraiment agaçante de 8.1 avec readonly : on ne pouvait pas cloner un objet et changer une propriété readonly. 8.3 ajoute la possibilité de réinitialiser les propriétés readonly pendant le clonage, ce qui rend les value objects immuables utilisables dans beaucoup plus de patterns.</p>
<h2 id="json_validate">json_validate()</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><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">json_validate</span>($string)) {
</span></span><span style="display:flex;"><span>    $data <span style="color:#f92672">=</span> <span style="color:#a6e22e">json_decode</span>($string);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Avant 8.3, la seule façon de valider une chaîne JSON était de la décoder et de vérifier les erreurs. <code>json_validate()</code> vérifie sans allouer la structure décodée, ce qui compte quand on a juste besoin de savoir si la chaîne est du JSON valide, pas ce qu&rsquo;elle contient.</p>
<h2 id="améliorations-du-randomizer">Améliorations du Randomizer</h2>
<p><code>getBytesFromString()</code> génère une chaîne aléatoire composée uniquement de caractères d&rsquo;un ensemble donné :</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>$token <span style="color:#f92672">=</span> $rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getBytesFromString</span>(<span style="color:#e6db74">&#39;abcdefghijklmnopqrstuvwxyz0123456789&#39;</span>, <span style="color:#ae81ff">32</span>);
</span></span></code></pre></div><p>L&rsquo;approche précédente : <code>str_split</code>, <code>array_map</code>, sélection aléatoire, <code>implode</code>. Ça marchait, mais c&rsquo;était plus long que ça n&rsquo;avait le droit d&rsquo;être.</p>
<p>8.3 est pour les équipes qui adoptent les versions PHP rapidement et veulent les améliorations incrémentales. Les constantes typées seules valent le coup sur toute base de code avec des constantes d&rsquo;interface.</p>
<h2 id="override-rend-lhéritage-explicite">#[\Override] rend l&rsquo;héritage explicite</h2>
<p>Avant 8.3, rien n&rsquo;empêchait d&rsquo;écrire une méthode qu&rsquo;on croyait surcharger celle d&rsquo;un parent, alors qu&rsquo;on avait un typo dans le nom ou que le parent l&rsquo;avait silencieusement supprimée. Des bugs silencieux, zéro retour du moteur.</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">Cache</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[\Override]
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">string</span> $key)<span style="color:#f92672">:</span> <span style="color:#a6e22e">mixed</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Le moteur vérifie que cette méthode existe dans un parent ou une interface
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Si la méthode n&rsquo;existe dans aucune classe parente ou interface implémentée, PHP lève une erreur. Même concept que <code>@Override</code> en Java ou <code>override</code> en C#, enfin en PHP.</p>
<h2 id="final-sur-les-méthodes-de-trait">final sur les méthodes de trait</h2>
<p>Les traits ont toujours eu des aspérités dans le modèle OOP de PHP. Un problème spécifique : une classe utilisant un trait pouvait surcharger n&rsquo;importe laquelle de ses méthodes, sapant les garanties que le trait essayait de fournir. 8.3 laisse le trait lui-même marquer une méthode comme final :</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">Singleton</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getInstance</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Maintenant, une classe utilisant le trait ne peut pas surcharger <code>getInstance()</code>. La garantie tient.</p>
<h2 id="les-classes-anonymes-peuvent-être-readonly">Les classes anonymes peuvent être readonly</h2>
<p>PHP 8.1 avait apporté les classes readonly. Les classes anonymes avaient été laissées de côté pour une raison obscure. 8.3 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>$point <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">class</span>(<span style="color:#ae81ff">3</span>, <span style="color:#ae81ff">4</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></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Pratique quand on a besoin d&rsquo;un value object immuable jetable sans la cérémonie de lui donner un nom.</p>
<h2 id="les-initialiseurs-de-variables-statiques-acceptent-des-expressions">Les initialiseurs de variables statiques acceptent des expressions</h2>
<p>Une restriction petite mais ancienne : les initialiseurs de variables statiques n&rsquo;acceptaient que des expressions constantes, pas d&rsquo;appels de fonctions. 8.3 lève cette contrainte :</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">connection</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">PDO</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> $pdo <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">PDO</span>(<span style="color:#a6e22e">getenv</span>(<span style="color:#e6db74">&#39;DATABASE_URL&#39;</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $pdo;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>L&rsquo;initialiseur s&rsquo;exécute une seule fois au premier appel, la variable statique persiste. Faisable avec un test null avant, c&rsquo;est juste plus propre.</p>
<h2 id="mb_str_pad-existe-enfin">mb_str_pad() existe enfin</h2>
<p><code>str_pad()</code> a toujours été conscient des octets, pas des caractères. Pour les chaînes multibyte (arabe, japonais, caractères accentués) il produisait une sortie incorrecte. 8.3 ajoute enfin la variante 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>$padded <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_str_pad</span>(<span style="color:#e6db74">&#39;日本&#39;</span>, <span style="color:#ae81ff">10</span>, <span style="color:#e6db74">&#39;*&#39;</span>, <span style="color:#a6e22e">STR_PAD_BOTH</span>);
</span></span></code></pre></div><p>La fonction respecte les limites de caractères, pas les comptages d&rsquo;octets.</p>
<h2 id="str_increment-et-str_decrement">str_increment() et str_decrement()</h2>
<p>L&rsquo;opérateur <code>++</code> de PHP sur les chaînes a une histoire de bizarreries : il incrémente les séquences de lettres (<code>'a'</code> → <code>'b'</code>, <code>'z'</code> → <code>'aa'</code>), mais <code>--</code> n&rsquo;a jamais fonctionné symétriquement. Le comportement était suffisamment surprenant que 8.3 déprécie <code>++</code>/<code>--</code> sur les chaînes non alphanumériques et introduit des fonctions explicites :</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">echo</span> <span style="color:#a6e22e">str_increment</span>(<span style="color:#e6db74">&#39;a&#39;</span>);  <span style="color:#75715e">// b
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">str_increment</span>(<span style="color:#e6db74">&#39;Az&#39;</span>); <span style="color:#75715e">// Ba
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">str_decrement</span>(<span style="color:#e6db74">&#39;b&#39;</span>);  <span style="color:#75715e">// a
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">str_decrement</span>(<span style="color:#e6db74">&#39;Ba&#39;</span>); <span style="color:#75715e">// Az
</span></span></span></code></pre></div><p>Les fonctions rendent l&rsquo;intention évidente et le comportement prévisible.</p>
<h2 id="randomrandomizer-gagne-le-support-des-flottants">Random\Randomizer gagne le support des flottants</h2>
<p>8.3 comble le côté flottant de l&rsquo;API Randomizer :</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>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Un flottant dans [0.0, 1.0)
</span></span></span><span style="display:flex;"><span>$f <span style="color:#f92672">=</span> $rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">nextFloat</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Un flottant dans une plage spécifique avec contrôle de l&#39;inclusion des bornes
</span></span></span><span style="display:flex;"><span>$f <span style="color:#f92672">=</span> $rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getFloat</span>(<span style="color:#ae81ff">1.5</span>, <span style="color:#ae81ff">3.5</span>, <span style="color:#a6e22e">Random\IntervalBoundary</span><span style="color:#f92672">::</span><span style="color:#a6e22e">ClosedOpen</span>);
</span></span></code></pre></div><p><code>IntervalBoundary</code> est un nouvel enum avec quatre valeurs : <code>ClosedOpen</code>, <code>ClosedClosed</code>, <code>OpenClosed</code>, <code>OpenOpen</code>. C&rsquo;est important pour la justesse statistique : l&rsquo;approche naïve avec <code>rand() / getrandmax()</code> ne produit pas une distribution uniforme sur les flottants.</p>
<h2 id="la-hiérarchie-dexceptions-de-date">La hiérarchie d&rsquo;exceptions de Date</h2>
<p>Les erreurs de date/heure en PHP levaient des exceptions génériques sans moyen de distinguer &ldquo;chaîne malformée&rdquo; de &ldquo;timezone invalide&rdquo; sans parser le message. 8.3 ajoute une hiérarchie propre :</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>    <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">DateTimeImmutable</span>(<span style="color:#e6db74">&#39;not a date&#39;</span>);
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">DateMalformedStringException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// spécifiquement un échec de parsing
</span></span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">DateException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// autres erreurs liées aux dates
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>L&rsquo;arbre complet : <code>DateError</code> (niveau moteur), <code>DateException</code> (base), avec des sous-classes spécifiques pour timezone invalide, chaîne d&rsquo;intervalle malformée, chaîne de période malformée, et chaîne de date malformée.</p>
<h2 id="gc_status-en-dit-plus">gc_status() en dit plus</h2>
<p><code>gc_status()</code> retourne maintenant huit champs supplémentaires : <code>running</code>, <code>protected</code>, <code>full</code>, <code>buffer_size</code>, et des décompositions temporelles (<code>application_time</code>, <code>collector_time</code>, <code>destructor_time</code>, <code>free_time</code>). Si vous profilez la pression mémoire ou les pauses GC, ces données étaient auparavant inaccessibles sans passer par une extension.</p>
<h2 id="strrchr-gagne-un-argument-de-direction">strrchr() gagne un argument de direction</h2>
<p><code>strrchr()</code> (trouver la dernière occurrence d&rsquo;un caractère, retourner de là jusqu&rsquo;à la fin) accepte maintenant un booléen <code>$before_needle</code>, alignant son API sur celle de <code>strstr()</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>$path <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;/var/www/html/index.php&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">strrchr</span>($path, <span style="color:#e6db74">&#39;/&#39;</span>, <span style="color:#a6e22e">before_needle</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>);  <span style="color:#75715e">// /var/www/html
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">strrchr</span>($path, <span style="color:#e6db74">&#39;/&#39;</span>);                        <span style="color:#75715e">// /index.php
</span></span></span></code></pre></div><p>Une fonction présente dans PHP depuis 1994, enfin cohérente avec sa voisine.</p>
<h2 id="dépréciations-à-noter">Dépréciations à noter</h2>
<p><code>get_class()</code> et <code>get_parent_class()</code> sans arguments émettent maintenant des avertissements de dépréciation. Les formes sans argument reposaient sur un contexte <code>$this</code> implicite, facile à mal lire. Passez l&rsquo;objet explicitement.</p>
<p><code>assert_options()</code> et les constantes <code>ASSERT_*</code> sont dépréciées au profit de la directive INI <code>zend.assertions</code>, qui est le bon outil pour contrôler le comportement des assertions selon les environnements.</p>
<p>Les opérateurs <code>++</code>/<code>--</code> sur les chaînes vides et les chaînes non numériques non alphanumériques émettent maintenant des avertissements de dépréciation. Le comportement était un territoire indéfini. 8.3 amorce la migration vers un comportement défini en 9.0.</p>
<h2 id="protection-contre-les-débordements-de-pile">Protection contre les débordements de pile</h2>
<p>Deux nouvelles directives INI : <code>zend.max_allowed_stack_size</code> fixe une limite dure sur la profondeur de pile de PHP, et <code>zend.reserved_stack_size</code> réserve un buffer pour le nettoyage après qu&rsquo;une limite soit atteinte. Avant 8.3, un code récursif profond pouvait tout simplement crasher au niveau OS. Maintenant PHP l&rsquo;intercepte et lève une <code>Error</code> avec un message utile.</p>
]]></content:encoded></item><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><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><item><title>PHP 8.0 : match, arguments nommés, attributs et JIT</title><link>https://guillaumedelre.github.io/fr/2021/01/10/php-8.0-match-arguments-nomm%C3%A9s-attributs-et-jit/</link><pubDate>Sun, 10 Jan 2021 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2021/01/10/php-8.0-match-arguments-nomm%C3%A9s-attributs-et-jit/</guid><description>Part 6 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 8.0 remodèle le langage : compilateur JIT, arguments nommés, expressions match, types union et opérateur nullsafe.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.0 est sorti le 26 novembre. Je le fais tourner depuis six semaines sur un projet perso et un nouveau service au boulot. C&rsquo;est la version PHP la plus significative depuis 7.0, et à certains égards plus impactante, parce que les changements se renforcent mutuellement de façon utile.</p>
<h2 id="jit">JIT</h2>
<p>Le compilateur Just-In-Time était l&rsquo;annonce principale. La réalité en production est plus nuancée : pour les applications web typiques (requêtes en base, appels HTTP, rendu de templates) les gains sont modestes, parce que ces workloads sont limités par les I/O, pas par le calcul. Là où le JIT brille vraiment, c&rsquo;est le code intensif en CPU : manipulation d&rsquo;images, transformation de données, calcul mathématique.</p>
<p>Pour la plupart des applications web, l&rsquo;amélioration de performance vient du travail sur le moteur en général dans 8.0, pas du JIT spécifiquement. Vaut quand même la peine de l&rsquo;activer : ça ne coûte rien sur les workloads I/O-bound.</p>
<h2 id="les-expressions-match">Les expressions match</h2>
<p><code>switch</code> a trois problèmes : il utilise la comparaison souple, il tombe en cascade par défaut, et il ne peut pas être utilisé comme expression. <code>match</code> règle les trois :</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>$result <span style="color:#f92672">=</span> <span style="color:#a6e22e">match</span>($status) {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;active&#39;</span>, <span style="color:#e6db74">&#39;pending&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;processing&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;done&#39;</span>             <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;finished&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">default</span>            <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\UnexpectedValueException</span>($status),
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Comparaison stricte. Pas de cascade. Expression qui retourne une valeur. Un match non exhaustif lève une exception. Après une semaine avec <code>match</code>, j&rsquo;ai arrêté d&rsquo;écrire des <code>switch</code>.</p>
<h2 id="les-arguments-nommés">Les arguments nommés</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><span style="color:#a6e22e">array_slice</span>(<span style="color:#66d9ef">array</span><span style="color:#f92672">:</span> $users, <span style="color:#a6e22e">offset</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0</span>, <span style="color:#a6e22e">length</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">10</span>, <span style="color:#a6e22e">preserve_keys</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>);
</span></span></code></pre></div><p>Les arguments nommés permettent de passer les arguments dans n&rsquo;importe quel ordre et d&rsquo;en sauter des optionnels. Le gain évident : la lisibilité sur les fonctions avec plusieurs flags booléens. Le gain moins évident : les arguments nommés survivent aux mises à jour de PHP même quand l&rsquo;ordre des paramètres change, parce qu&rsquo;on nomme ce qu&rsquo;on veut dire.</p>
<h2 id="les-attributs">Les attributs</h2>
<p>Place aux docblock annotations (le style <code>@Route</code>, <code>@ORM\Column</code> sur lequel les frameworks se sont appuyés pendant des années), bienvenue à la syntaxe PHP de première 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:#75715e">#[Route(&#39;/users&#39;, methods: [&#39;GET&#39;])]
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#[IsGranted(&#39;ROLE_ADMIN&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">list</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Response</span> { <span style="color:#f92672">...</span> }
</span></span></code></pre></div><p>Les attributs sont validés par le moteur, pas parsés depuis des strings. Le support IDE fonctionne directement, sans magie de plugin. Pour les utilisateurs de Symfony et Doctrine, c&rsquo;est le vrai gain quotidien de PHP 8.0.</p>
<h2 id="la-promotion-de-constructeur">La promotion de constructeur</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><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">string</span> $name,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">private</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">string</span> $email <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Propriétés déclarées et assignées en une ligne dans la signature du constructeur. Le gain de refactoring le plus immédiat dans 8.0 : chaque classe de données que j&rsquo;ai touchée depuis la mise à jour fait moitié moins de lignes qu&rsquo;avant.</p>
<h2 id="lopérateur-nullsafe">L&rsquo;opérateur nullsafe</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>$city <span style="color:#f92672">=</span> $user<span style="color:#f92672">?-&gt;</span><span style="color:#a6e22e">getAddress</span>()<span style="color:#f92672">?-&gt;</span><span style="color:#a6e22e">getCity</span>()<span style="color:#f92672">?-&gt;</span><span style="color:#a6e22e">getName</span>();
</span></span></code></pre></div><p><code>null</code> à n&rsquo;importe quel point de la chaîne court-circuite le reste et retourne <code>null</code>. L&rsquo;alternative était des null checks imbriqués ou une chaîne de retours anticipés. Ça se compose naturellement.</p>
<h2 id="les-types-union">Les types union</h2>
<p>Les arguments nommés rendent les signatures de fonctions plus explicites au site d&rsquo;appel. Les types union les rendent plus honnêtes au site de déclaration :</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">processInput</span>(<span style="color:#a6e22e">int</span><span style="color:#f92672">|</span><span style="color:#a6e22e">float</span><span style="color:#f92672">|</span><span style="color:#a6e22e">string</span> $value)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span><span style="color:#f92672">|</span><span style="color:#a6e22e">int</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">is_string</span>($value)) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">strlen</span>($value);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> (<span style="color:#a6e22e">int</span>) <span style="color:#a6e22e">round</span>($value);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>L&rsquo;union <code>int|float|string</code> est un OU littéral. Le moteur l&rsquo;impose à l&rsquo;entrée et à la sortie. Avant 8.0, &ldquo;ce paramètre accepte int ou float&rdquo; vivait dans un docblock que rien n&rsquo;imposait. Il y a aussi <code>null</code> comme composant de type : <code>?string</code> est juste du sucre syntaxique pour <code>string|null</code>, les deux sont valides.</p>
<p>Un cas spécial : <code>false</code>. PHP a un tas de fonctions natives qui retournent une valeur typée en cas de succès et <code>false</code> en cas d&rsquo;échec. Le système de types de 8.0 accommode ça : <code>array|false</code>, <code>string|false</code>. C&rsquo;est une reconnaissance honnête que la codebase ne peut pas être réécrite du jour au lendemain.</p>
<h2 id="le-type-de-retour-static">Le type de retour static</h2>
<p><code>static</code> comme type de retour était possible de manière informelle via les docblocks, mais 8.0 le rend officiel. La distinction entre <code>self</code> et <code>static</code> compte dans l&rsquo;héritage :</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">Builder</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">protected</span> <span style="color:#66d9ef">array</span> $config <span style="color:#f92672">=</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">set</span>(<span style="color:#a6e22e">string</span> $key, <span style="color:#a6e22e">mixed</span> $value)<span style="color:#f92672">:</span> <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">config</span>[$key] <span style="color:#f92672">=</span> $value;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> $this;
</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">SpecialBuilder</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Builder</span> {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> (<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SpecialBuilder</span>())<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">set</span>(<span style="color:#e6db74">&#39;foo&#39;</span>, <span style="color:#e6db74">&#39;bar&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// $result est SpecialBuilder, pas Builder
</span></span></span></code></pre></div><p>Avec <code>self</code> comme type de retour, cette chaîne retournerait <code>Builder</code>, cassant les interfaces fluides dans les sous-classes. <code>static</code> fait fonctionner correctement les APIs fluides à travers les hiérarchies d&rsquo;héritage sans surcharges manuelles.</p>
<h2 id="le-type-mixed">Le type mixed</h2>
<p><code>mixed</code> était une convention de docblock pendant des années. 8.0 en fait un vrai type qui apparaît dans les signatures :</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">debug</span>(<span style="color:#a6e22e">mixed</span> $value)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">var_dump</span>($value);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Il accepte tout : <code>null</code>, objets, ressources, scalaires, tableaux. Sémantiquement c&rsquo;est la même chose que n&rsquo;avoir aucune déclaration de type, mais c&rsquo;est explicite plutôt qu&rsquo;absent. La différence entre &ldquo;ce paramètre est non typé&rdquo; et &ldquo;ce paramètre accepte intentionnellement n&rsquo;importe quoi.&rdquo; Vaut la peine de l&rsquo;utiliser quand on écrit un utilitaire généraliste qui serait malhonnête avec un type plus étroit.</p>
<h2 id="throw-comme-expression">throw comme expression</h2>
<p>Avant 8.0, <code>throw</code> était une instruction. Ça semble une distinction pédante jusqu&rsquo;à ce qu&rsquo;on tombe sur les endroits où on veut vraiment une expression :</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">// Dans un ternaire :
</span></span></span><span style="display:flex;"><span>$value <span style="color:#f92672">=</span> $input <span style="color:#f92672">??</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\InvalidArgumentException</span>(<span style="color:#e6db74">&#39;input required&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Dans une arrow function :
</span></span></span><span style="display:flex;"><span>$getId <span style="color:#f92672">=</span> <span style="color:#a6e22e">fn</span>(<span style="color:#a6e22e">User</span> $u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">id</span> <span style="color:#f92672">??</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\RuntimeException</span>(<span style="color:#e6db74">&#39;no id&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Dans un bras match (qui est déjà une expression) :
</span></span></span><span style="display:flex;"><span>$status <span style="color:#f92672">=</span> <span style="color:#a6e22e">match</span>($code) {
</span></span><span style="display:flex;"><span>    <span style="color:#ae81ff">200</span>     <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;ok&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ae81ff">404</span>     <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;not found&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">default</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\UnexpectedValueException</span>(<span style="color:#e6db74">&#34;unknown code: </span><span style="color:#e6db74">$code</span><span style="color:#e6db74">&#34;</span>),
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Le dernier est particulièrement utile : un match sans default lancera <code>UnhandledMatchError</code> automatiquement, mais parfois on veut contrôler le type d&rsquo;exception et le message.</p>
<h2 id="catch-sans-variable">catch sans variable</h2>
<p>Petite amélioration de qualité de vie. Quand on attrape une exception mais qu&rsquo;on n&rsquo;utilise pas réellement l&rsquo;objet, 8.0 permet d&rsquo;omettre la variable :</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>    $result <span style="color:#f92672">=</span> $cache<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>($key);
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">CacheMissException</span>) {
</span></span><span style="display:flex;"><span>    $result <span style="color:#f92672">=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">compute</span>($key);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Avant 8.0, il fallait écrire <code>catch (CacheMissException $e)</code> et ensuite soit utiliser <code>$e</code> soit vivre avec l&rsquo;avertissement IDE sur la variable inutilisée. Aucune des deux options n&rsquo;était satisfaisante.</p>
<h2 id="les-fonctions-string-qui-auraient-dû-exister-depuis-des-années">Les fonctions string qui auraient dû exister depuis des années</h2>
<p>Trois fonctions que chaque développeur PHP a écrites manuellement au moins une 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:#a6e22e">str_contains</span>(<span style="color:#e6db74">&#39;hello world&#39;</span>, <span style="color:#e6db74">&#39;world&#39;</span>);  <span style="color:#75715e">// true
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">str_starts_with</span>(<span style="color:#e6db74">&#39;hello world&#39;</span>, <span style="color:#e6db74">&#39;hell&#39;</span>); <span style="color:#75715e">// true
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">str_ends_with</span>(<span style="color:#e6db74">&#39;hello world&#39;</span>, <span style="color:#e6db74">&#39;world&#39;</span>); <span style="color:#75715e">// true
</span></span></span></code></pre></div><p>Avant 8.0, les approches habituelles étaient <code>strpos() !== false</code>, <code>strncmp()</code>, ou <code>substr() ===</code>, qui nécessitent toutes de s&rsquo;arrêter pour se souvenir de la sémantique. Ces nouvelles fonctions sont juste directes et lisibles. Pas de regex, pas d&rsquo;arithmétique d&rsquo;offset.</p>
<h2 id="un-tri-stable">Un tri stable</h2>
<p>Les fonctions de tri de PHP n&rsquo;étaient pas stables avant 8.0. &ldquo;Pas stable&rdquo; signifie que les éléments qui se comparent comme égaux pouvaient se retrouver dans n&rsquo;importe quel ordre les uns par rapport aux autres. En pratique, ça causait des bugs subtils dans le code UI qui avait besoin d&rsquo;un ordre cohérent, une pagination qui changeait entre les chargements, et des tests qui ne passaient que par chance.</p>
<p>8.0 garantit la stabilité à travers toutes les fonctions de tri : <code>sort()</code>, <code>usort()</code>, <code>array_multisort()</code>, et le reste. Les éléments égaux conservent leur position relative originale. C&rsquo;est le comportement que la plupart des gens supposaient déjà être là.</p>
<h2 id="weakmap">WeakMap</h2>
<p>7.4 apportait <code>WeakReference</code> pour les objets simples. 8.0 apporte <code>WeakMap</code> : une map où les clés (des objets) et leurs données associées peuvent être ramassées par le GC quand aucune autre référence à l&rsquo;objet-clé n&rsquo;existe :</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">RequestCache</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">WeakMap</span> $cache;
</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">__construct</span>() {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">cache</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">WeakMap</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">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">Request</span> $request)<span style="color:#f92672">:</span> <span style="color:#a6e22e">Response</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">cache</span>[$request] <span style="color:#f92672">??=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">compute</span>($request);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Dès que <code>$request</code> n&rsquo;est plus référencé ailleurs, l&rsquo;entrée disparaît de la map. Pas de nettoyage manuel nécessaire. C&rsquo;est le bon pattern pour la mémoïsation et les caches de propriétés calculées où on ne veut pas être la seule raison qu&rsquo;un objet reste vivant.</p>
<h2 id="les-nouveaux-types-dexception">Les nouveaux types d&rsquo;exception</h2>
<p><code>ValueError</code> est levée quand une fonction reçoit le bon type mais une valeur invalide, par opposition à <code>TypeError</code> qui se déclenche sur les mauvais 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-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">array_chunk</span>([], <span style="color:#f92672">-</span><span style="color:#ae81ff">5</span>); <span style="color:#75715e">// ValueError: array_chunk(): Argument #2 ($length) must be greater than 0
</span></span></span></code></pre></div><p>Avant 8.0, beaucoup de ces cas étaient des warnings qui retournaient <code>false</code> ou <code>null</code>. Maintenant ils lèvent des exceptions. Le moteur est plus strict, ce qui signifie qu&rsquo;on attrape les problèmes plus tôt plutôt que d&rsquo;obtenir des résultats bizarres quelque part en aval.</p>
<h2 id="get_debug_type-et-fdiv">get_debug_type() et fdiv()</h2>
<p>Deux fonctions utilitaires à connaître.</p>
<p><code>get_debug_type()</code> retourne une représentation string normalisée de n&rsquo;importe quelle valeur, pratique pour les messages d&rsquo;erreur :</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">get_debug_type</span>(<span style="color:#ae81ff">1</span>);          <span style="color:#75715e">// &#34;int&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>(<span style="color:#ae81ff">1.0</span>);        <span style="color:#75715e">// &#34;float&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>(<span style="color:#66d9ef">null</span>);       <span style="color:#75715e">// &#34;null&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Foo</span>());  <span style="color:#75715e">// &#34;Foo&#34; (pas &#34;object&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>([]);         <span style="color:#75715e">// &#34;array&#34;
</span></span></span></code></pre></div><p>La différence avec <code>gettype()</code> : elle retourne les noms de classes pour les objets et utilise des noms normalisés (<code>&quot;int&quot;</code> pas <code>&quot;integer&quot;</code>). Exactement ce qu&rsquo;on veut pour construire un message d&rsquo;exception qui dit ce qu&rsquo;on a reçu versus ce qu&rsquo;on attendait.</p>
<p><code>fdiv()</code> effectue une division en virgule flottante suivant IEEE 754, ce qui signifie que la division par zéro retourne <code>INF</code>, <code>-INF</code>, ou <code>NAN</code> au lieu d&rsquo;un warning :</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">fdiv</span>(<span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">0</span>);   <span style="color:#75715e">// INF
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">fdiv</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">0</span>);  <span style="color:#75715e">// -INF
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">fdiv</span>(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>);    <span style="color:#75715e">// NAN
</span></span></span></code></pre></div><h2 id="les-changements-qui-cassent-des-choses">Les changements qui cassent des choses</h2>
<p>8.0 inclut aussi quelques changements qui ne sont pas des fonctionnalités, ce sont des corrections.</p>
<p>Le plus important : <code>0 == &quot;foo&quot;</code> est maintenant <code>false</code>. En PHP 7, comparer un entier à une string non numérique castait la string en 0, donc <code>0 == &quot;n'importe-quoi-non-numérique&quot;</code> s&rsquo;évaluait à <code>true</code>. C&rsquo;était une source persistante de bugs et de maux de tête de sécurité. PHP 8 l&rsquo;inverse : l&rsquo;entier est casté en string à la place :</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">var_dump</span>(<span style="color:#ae81ff">0</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;foo&#34;</span>);  <span style="color:#75715e">// bool(false) en 8.0, bool(true) en 7.x
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>(<span style="color:#ae81ff">0</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span>);     <span style="color:#75715e">// bool(false) en 8.0, bool(true) en 7.x
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>(<span style="color:#ae81ff">0</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;0&#34;</span>);    <span style="color:#75715e">// bool(true) dans les deux (&#34;0&#34; est numérique)
</span></span></span></code></pre></div><p>Si on s&rsquo;appuyait sur ça intentionnellement, on savait déjà que c&rsquo;était douteux. Si on ne savait pas qu&rsquo;on s&rsquo;en appuyait, 8.0 va trouver ces chemins de code.</p>
<p>Plusieurs fonctions qui retournaient des ressources retournent maintenant des objets propres : <code>curl_init()</code> retourne un <code>CurlHandle</code>, <code>imagecreate()</code> retourne un <code>GdImage</code>, <code>xml_parser_create()</code> retourne un <code>XMLParser</code>. Le code qui vérifie <code>is_resource($curl)</code> va casser, parce que <code>is_resource()</code> retourne <code>false</code> pour ces objets. La correction consiste à vérifier contre <code>false</code> (la valeur de retour en cas d&rsquo;échec) plutôt que de vérifier le type du cas de succès.</p>
<p>PHP 8.0 est le genre de version où les fonctionnalités se renforcent mutuellement. Les attributs se marient bien avec la promotion de constructeur. Match s&rsquo;associe naturellement avec les types union. Les fonctions string réduisent le bruit qui cachait l&rsquo;intention. Les corrections sont parfois cassantes, mais elles poussent le langage vers une cohérence qu&rsquo;il aurait dû avoir depuis des années.</p>
]]></content:encoded></item><item><title>PHP 7.4 : les propriétés typées et les arrow functions qu'on attendait</title><link>https://guillaumedelre.github.io/fr/2020/01/12/php-7.4-les-propri%C3%A9t%C3%A9s-typ%C3%A9es-et-les-arrow-functions-quon-attendait/</link><pubDate>Sun, 12 Jan 2020 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2020/01/12/php-7.4-les-propri%C3%A9t%C3%A9s-typ%C3%A9es-et-les-arrow-functions-quon-attendait/</guid><description>Part 5 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 7.4 apporte les propriétés typées et les arrow functions concises — dernière version 7.x et prévisualisation la plus claire de PHP 8.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.4 est sorti le 28 novembre. C&rsquo;est la dernière version 7.x avant PHP 8.0, et ça se sent. Les fonctionnalités sont suffisamment substantielles pour tenir debout seules, mais elles ressemblent aussi à des fondations pour ce qui arrive.</p>
<h2 id="les-propriétés-typées">Les propriétés typées</h2>
<p>C&rsquo;est la grande nouveauté. Depuis PHP 7.0, on pouvait typer les paramètres de fonctions et les valeurs de retour. Mais les propriétés de classe ? Toujours non typées :</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:#a6e22e">int</span> $id;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $name;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">DateTimeInterface</span> $deletedAt;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>7.4 change ça. Les propriétés typées font respecter les types à l&rsquo;affectation, pas seulement au niveau des sites d&rsquo;appel. Les classes deviennent auto-documentées d&rsquo;une façon que les docblocks n&rsquo;ont jamais vraiment réussi à faire, et le moteur attrape les erreurs de type avant qu&rsquo;elles ne se propagent dans la moitié de la stack.</p>
<p>Une subtilité : les propriétés typées sont <code>uninitialized</code> par défaut (pas <code>null</code>). Accéder à une propriété non initialisée lève une <code>Error</code>. C&rsquo;est un piège classique : <code>?string</code> n&rsquo;implique pas un défaut de <code>null</code>. Il faut encore un <code>= null</code> explicite pour ça.</p>
<h2 id="les-arrow-functions">Les arrow functions</h2>
<p>Les closures en PHP ont toujours nécessité d&rsquo;importer explicitement les variables de la portée extérieure avec <code>use</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>$multiplier <span style="color:#f92672">=</span> <span style="color:#ae81ff">3</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> $multiplier; <span style="color:#75715e">// pas besoin de use()
</span></span></span></code></pre></div><p>Les arrow functions capturent automatiquement la portée englobante. Expression unique, retour implicite, pas de boilerplate. Elles ne remplacent pas les closures complètes pour la logique complexe, mais pour les callbacks courts, elles éliminent une catégorie de bruit qui s&rsquo;accumulait depuis des années.</p>
<h2 id="le-préchargement-opcache">Le préchargement opcache</h2>
<p>Pour les setups PHP-FPM à longue durée de vie, le préchargement permet à un script de charger et compiler des fichiers PHP en mémoire opcache au démarrage du serveur. Ces fichiers sont disponibles pour toutes les requêtes sans overhead de compilation.</p>
<p>Le gain varie selon l&rsquo;application. Sur les grands frameworks où les mêmes fichiers sont chargés à chaque requête, c&rsquo;est réel. Sur les petites applications, négligeable. Vaut la peine de benchmarker avant d&rsquo;ajouter la complexité de configuration.</p>
<h2 id="les-petites-choses-qui-saccumulent">Les petites choses qui s&rsquo;accumulent</h2>
<p>Les fonctionnalités mentionnées en passant méritent plus qu&rsquo;une ligne. L&rsquo;opérateur d&rsquo;affectation null-coalescente <code>??=</code> résout un pattern suffisamment agaçant à écrire à chaque fois, mais jamais assez pour se donner la peine de l&rsquo;abstraire :</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>$config[<span style="color:#e6db74">&#39;timeout&#39;</span>] <span style="color:#f92672">??=</span> <span style="color:#ae81ff">30</span>;
</span></span><span style="display:flex;"><span><span style="color:#75715e">// équivalent à : $config[&#39;timeout&#39;] = $config[&#39;timeout&#39;] ?? 30;
</span></span></span></code></pre></div><p>L&rsquo;opérateur spread dans les littéraux de tableau fait ce qu&rsquo;on attend de la version pour les appels de fonctions — dépacker un itérable dans un littéral de tableau :</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;blue&#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>$options <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:#f92672">...</span>$defaults, <span style="color:#e6db74">&#39;weight&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">1.2</span>];
</span></span><span style="display:flex;"><span><span style="color:#75715e">// [&#39;size&#39; =&gt; &#39;M&#39;, &#39;color&#39; =&gt; &#39;blue&#39;, &#39;weight&#39; =&gt; 1.2]
</span></span></span></code></pre></div><p>Note : les clés string n&rsquo;étaient pas supportées dans 7.4 pour le dépaquet de tableau. Ça viendra plus tard.</p>
<p>Les types de retour covariants et les types de paramètres contravariants comblent un vide qui rendait certains patterns d&rsquo;héritage inutilement maladroits. Une classe enfant peut maintenant affiner son type de retour vers un sous-type de celui du parent, sans erreur fatale :</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">Producer</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Iterator</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">ChildProducer</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Producer</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">ArrayIterator</span> {} <span style="color:#75715e">// ArrayIterator implémente Iterator
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="lire-des-nombres-à-3h-du-matin">Lire des nombres à 3h du matin</h2>
<p>Le séparateur de littéraux numériques est une de ces fonctionnalités dont on ne sait pas qu&rsquo;on la voulait jusqu&rsquo;à la première fois qu&rsquo;on écrit une grande constante et qu&rsquo;on perd immédiatement le sens de l&rsquo;ordre de grandeur :</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>$earthMass    <span style="color:#f92672">=</span> <span style="color:#ae81ff">5_972_168_000_000_000_000_000_000</span>; <span style="color:#75715e">// kg
</span></span></span><span style="display:flex;"><span>$lightSpeed   <span style="color:#f92672">=</span> <span style="color:#ae81ff">299_792_458</span>;                        <span style="color:#75715e">// m/s
</span></span></span><span style="display:flex;"><span>$planck       <span style="color:#f92672">=</span> <span style="color:#ae81ff">6.626</span><span style="color:#a6e22e">_070_15e</span><span style="color:#f92672">-</span><span style="color:#ae81ff">34</span>;                  <span style="color:#75715e">// J·s
</span></span></span><span style="display:flex;"><span>$hexMask      <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xFF_EC_D5_08</span>;
</span></span><span style="display:flex;"><span>$binaryFlags  <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span><span style="color:#a6e22e">b0001_1111_0010_0000</span>;
</span></span></code></pre></div><p>L&rsquo;underscore est purement syntaxique. Le moteur le supprime avant de parser la valeur. On peut le mettre n&rsquo;importe où entre les chiffres, bien que la convention suive le groupement naturel du système numérique utilisé.</p>
<h2 id="référencer-sans-posséder">Référencer sans posséder</h2>
<p><code>WeakReference</code> permet de tenir une référence à un objet sans empêcher le ramasse-miettes de le détruire. Le cas d&rsquo;usage : les caches et registres — on veut savoir qu&rsquo;un objet est vivant, mais on ne veut pas être la raison qu&rsquo;il reste vivant :</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>$object <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HeavyObject</span>();
</span></span><span style="display:flex;"><span>$ref <span style="color:#f92672">=</span> <span style="color:#a6e22e">WeakReference</span><span style="color:#f92672">::</span><span style="color:#a6e22e">create</span>($object);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>($ref<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>()); <span style="color:#75715e">// object(HeavyObject)
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">unset</span>($object);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>($ref<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>()); <span style="color:#75715e">// NULL — le GC l&#39;a collecté
</span></span></span></code></pre></div><p>Avant 7.4, il y avait <code>WeakRef</code> via une extension, et certains frameworks faisaient des tours de passe-passe avec <code>SplObjectStorage</code> qui ne se comportaient pas tout à fait pareil. La classe native est juste directe.</p>
<h2 id="la-sérialisation-sans-surprise">La sérialisation sans surprise</h2>
<p>La sérialisation personnalisée d&rsquo;objets avant 7.4 passait par l&rsquo;interface <code>Serializable</code> : implémenter <code>serialize()</code> et <code>unserialize()</code>, retourner une string, reconstruire depuis elle. Le problème est que <code>serialize()</code> déclenchait <code>__sleep()</code>, <code>unserialize()</code> déclenchait <code>__wakeup()</code>, et l&rsquo;interaction entre ces hooks était fragile, surtout dans les hiérarchies d&rsquo;héritage.</p>
<p>7.4 introduit <code>__serialize()</code> et <code>__unserialize()</code>, qui travaillent avec des tableaux plutôt que des strings et n&rsquo;interagissent pas avec les anciens hooks :</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">Session</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $token;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">\DateTime</span> $createdAt;
</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">__serialize</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">array</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> [<span style="color:#e6db74">&#39;token&#39;</span> <span style="color:#f92672">=&gt;</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">token</span>, <span style="color:#e6db74">&#39;created&#39;</span> <span style="color:#f92672">=&gt;</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">createdAt</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getTimestamp</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">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__unserialize</span>(<span style="color:#66d9ef">array</span> $data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">token</span> <span style="color:#f92672">=</span> $data[<span style="color:#e6db74">&#39;token&#39;</span>];
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">createdAt</span> <span style="color:#f92672">=</span> (<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\DateTime</span>())<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setTimestamp</span>($data[<span style="color:#e6db74">&#39;created&#39;</span>]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Quand les nouvelles et anciennes méthodes coexistent sur la même classe, <code>__serialize()</code> gagne. L&rsquo;ancienne interface <code>Serializable</code> est dépréciée dans 8.1.</p>
<h2 id="ce-que-la-bibliothèque-standard-a-discrètement-reçu">Ce que la bibliothèque standard a discrètement reçu</h2>
<p><code>mb_str_split()</code> fait ce que <code>str_split()</code> fait mais correctement pour les strings multibyte. Le manque était franchement embarrassant pour un langage utilisé dans autant de locales que PHP :</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">mb_str_split</span>(<span style="color:#e6db74">&#39;héllo&#39;</span>, <span style="color:#ae81ff">1</span>); <span style="color:#75715e">// [&#39;h&#39;, &#39;é&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">str_split</span>(<span style="color:#e6db74">&#39;héllo&#39;</span>, <span style="color:#ae81ff">1</span>);    <span style="color:#75715e">// [&#39;h&#39;, &#39;Ã&#39;, &#39;©&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;] — cassé
</span></span></span></code></pre></div><p><code>strip_tags()</code> accepte maintenant un tableau de tags autorisés, ce qui est plus propre que le format string qu&rsquo;il fallait passer auparavant :</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">strip_tags</span>($html, [<span style="color:#e6db74">&#39;p&#39;</span>, <span style="color:#e6db74">&#39;br&#39;</span>, <span style="color:#e6db74">&#39;strong&#39;</span>]); <span style="color:#75715e">// était : &#39;&lt;p&gt;&lt;br&gt;&lt;strong&gt;&#39;
</span></span></span></code></pre></div><p><code>proc_open()</code> accepte maintenant un tableau de commandes, contournant complètement l&rsquo;interprétation par le shell. Même idée que <code>subprocess</code> de Python avec <code>shell=False</code>. À retenir quand on passe des arguments fournis par l&rsquo;utilisateur à un processus externe.</p>
<h2 id="le-chapitre-ffi">Le chapitre FFI</h2>
<p>L&rsquo;extension Foreign Function Interface a atterri dans 7.4 après avoir passé du temps dans une branche feature. Elle permet à PHP d&rsquo;appeler des fonctions C natives en chargeant une bibliothèque partagée et en déclarant les signatures :</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>$ffi <span style="color:#f92672">=</span> <span style="color:#a6e22e">FFI</span><span style="color:#f92672">::</span><span style="color:#a6e22e">cdef</span>(<span style="color:#e6db74">&#34;int strlen(const char *s);&#34;</span>, <span style="color:#e6db74">&#34;libc.so.6&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>($ffi<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">strlen</span>(<span style="color:#e6db74">&#34;hello&#34;</span>)); <span style="color:#75715e">// int(5)
</span></span></span></code></pre></div><p>Les applications pratiques sont étroites mais réelles : appeler des API de plateforme sans binding PHP, wrapper du code C critique pour les performances sans écrire une extension complète, ou juste jouer avec des bibliothèques natives directement. Ce n&rsquo;est pas un remplacement des extensions propres en production, mais ça supprime la barrière &ldquo;écrire une extension C&rdquo; pour l&rsquo;exploration.</p>
<h2 id="ce-qui-a-été-déprécié">Ce qui a été déprécié</h2>
<p>Quelques choses qui auraient dû être nettoyées depuis longtemps ont finalement reçu le traitement dépréciation dans 7.4.</p>
<p>Les ternaires imbriqués sans parenthèses ont toujours été ambigus. PHP les évaluait de gauche à droite alors que pratiquement tous les autres langages avec un ternaire évaluent de droite à gauche :</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">// Était ambigu, maintenant déprécié :
</span></span></span><span style="display:flex;"><span>$a <span style="color:#f92672">?</span> $b <span style="color:#f92672">:</span> $c <span style="color:#f92672">?</span> $d <span style="color:#f92672">:</span> $e;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Rendre explicite :
</span></span></span><span style="display:flex;"><span>($a <span style="color:#f92672">?</span> $b <span style="color:#f92672">:</span> $c) <span style="color:#f92672">?</span> $d <span style="color:#f92672">:</span> $e;
</span></span></code></pre></div><p>L&rsquo;accès par offset avec accolades pour les strings et tableaux — <code>$str{0}</code> au lieu de <code>$str[0]</code> — est déprécié et supprimé dans 8.0. C&rsquo;était toujours un alias, jamais une fonctionnalité distincte.</p>
<p><code>implode()</code> avec l&rsquo;ordre d&rsquo;arguments inversé (tableau en premier, colle en second) est déprécié. La fonction a accepté les deux ordres depuis le début, ce qui était une erreur. L&rsquo;ordre correct est <code>implode(string $separator, array $array)</code>.</p>
<h2 id="ce-qui-arrive-ensuite">Ce qui arrive ensuite</h2>
<p>7.4 est la dernière version 7.x. Les dépréciations sont principalement du déblayage pour les suppressions dans 8.0. Le backlog de RFCs pour 8.0 est substantiel : JIT, attributs, arguments nommés, expressions match. 7.4 est un bon endroit où atterrir en attendant que tout ça arrive.</p>
]]></content:encoded></item><item><title>PHP 7.1 : un système de types plus rigoureux et les petits gains autour</title><link>https://guillaumedelre.github.io/fr/2017/01/15/php-7.1-un-syst%C3%A8me-de-types-plus-rigoureux-et-les-petits-gains-autour/</link><pubDate>Sun, 15 Jan 2017 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2017/01/15/php-7.1-un-syst%C3%A8me-de-types-plus-rigoureux-et-les-petits-gains-autour/</guid><description>Part 2 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 7.1 a comblé les lacunes laissées par la 7.0 : types nullables, retour void, et déstructuration — de petits ajouts qui ont rendu le système de types utilisable.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.1 est sorti le 1er décembre. Pas de titre &ldquo;2x plus rapide&rdquo;, pas de réécriture du moteur. Il comble les lacunes que la 7.0 avait laissées dans le système de types, et ces lacunes étaient vraiment agaçantes.</p>
<h2 id="les-types-nullables">Les types nullables</h2>
<p>La 7.0 permettait de déclarer <code>string $name</code> comme type de paramètre. Ce qu&rsquo;elle ne permettait pas, c&rsquo;était de dire &ldquo;ça peut aussi être null&rdquo;. On devait soit abandonner le type hint complètement, soit bricoler autour. La 7.1 ajoute le préfixe <code>?</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">function</span> <span style="color:#a6e22e">findUser</span>(<span style="color:#f92672">?</span><span style="color:#a6e22e">int</span> $id)<span style="color:#f92672">:</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> ($id <span style="color:#f92672">===</span> <span style="color:#66d9ef">null</span>) <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</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">repository</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">find</span>($id);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Ça semble mineur. Ce n&rsquo;est pas le cas. Les types nullables font la différence entre une signature qui dit ce que fait une fonction et une qui ment par omission. Chaque codebase sur lequel j&rsquo;ai travaillé a des fonctions qui peuvent retourner null. Maintenant on peut vraiment le dire plutôt que de le cacher dans un docblock.</p>
<h2 id="le-type-de-retour-void">Le type de retour void</h2>
<p>Le complément du nullable : une fonction qui ne retourne intentionnellement rien :</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">process</span>(<span style="color:#a6e22e">Order</span> $order)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatcher</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatch</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">OrderProcessed</span>($order));
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>void</code> rend l&rsquo;intention explicite et empêche de retourner accidentellement une valeur depuis une fonction qui ne devrait pas. Combiné aux types nullables, le système de types de PHP en 7.1 est bien plus expressif qu&rsquo;en 7.0.</p>
<h2 id="la-visibilité-des-constantes-de-classe">La visibilité des constantes de classe</h2>
<p>Un petit correctif mais bienvenu. Les constantes dans les classes étaient toujours publiques avant la 7.1. 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><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Config</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">DB_PASSWORD</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;secret&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">protected</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 style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">MAX_RETRIES</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">3</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Garder les détails d&rsquo;implémentation privés, ça compte. Ça aurait dû exister depuis le début.</p>
<h2 id="attraper-plusieurs-exceptions">Attraper plusieurs exceptions</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><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">InvalidArgumentException</span> <span style="color:#f92672">|</span> <span style="color:#a6e22e">RuntimeException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// gérer les deux
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Évite un bloc catch dupliqué quand deux exceptions nécessitent un traitement identique. Simple, utile.</p>
<h2 id="déstructurer-des-tableaux-sans-list">Déstructurer des tableaux sans list()</h2>
<p><code>list()</code> est dans PHP depuis la 4.0 et a toujours semblé un peu à côté syntaxiquement. La 7.1 ajoute un raccourci avec <code>[]</code> qui se lit bien plus naturellement :</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>[$first, $second] <span style="color:#f92672">=</span> $coordinates;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> ($rows <span style="color:#66d9ef">as</span> [$id, $name, $email]) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Elle gagne aussi le support des clés, ce qui rend la déstructuration de tableaux associatifs enfin utilisable :</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:#e6db74">&#39;id&#39;</span> <span style="color:#f92672">=&gt;</span> $id, <span style="color:#e6db74">&#39;name&#39;</span> <span style="color:#f92672">=&gt;</span> $name] <span style="color:#f92672">=</span> $user;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> ($records <span style="color:#66d9ef">as</span> [<span style="color:#e6db74">&#39;id&#39;</span> <span style="color:#f92672">=&gt;</span> $id, <span style="color:#e6db74">&#39;status&#39;</span> <span style="color:#f92672">=&gt;</span> $status]) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Avant ça, extraire des clés nommées d&rsquo;un tableau signifiait soit <code>extract()</code> (qui déverse tout dans le scope et invite les collisions) soit un tas d&rsquo;assignations individuelles. C&rsquo;est juste plus propre.</p>
<h2 id="le-type-iterable">Le type iterable</h2>
<p>Si on écrit une fonction qui accepte soit un tableau soit un générateur, il n&rsquo;y avait pas de type hint propre pour ça en 7.0. On typait soit en <code>array</code> et on excluait silencieusement les générateurs, soit on abandonnait le hint complètement :</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">processItems</span>(<span style="color:#a6e22e">iterable</span> $items)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">foreach</span> ($items <span style="color:#66d9ef">as</span> $item) {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>($item);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>iterable</code> accepte tout ce sur quoi on peut faire un <code>foreach</code> : les tableaux et les implémentations de <code>Traversable</code>. Ça fonctionne aussi comme type de retour. Pas dramatique, mais ça comble un vrai manque.</p>
<h2 id="les-offsets-négatifs-sur-les-chaînes">Les offsets négatifs sur les chaînes</h2>
<p>L&rsquo;indexation de chaînes avec <code>[]</code> ou <code>{}</code> accepte maintenant les valeurs négatives, en comptant depuis la fin :</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>$str <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;hello&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $str[<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>]; <span style="color:#75715e">// &#34;o&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $str[<span style="color:#f92672">-</span><span style="color:#ae81ff">2</span>]; <span style="color:#75715e">// &#34;l&#34;
</span></span></span></code></pre></div><p>Plusieurs fonctions de chaînes ont reçu le même traitement : <code>strpos()</code>, <code>substr()</code>, <code>substr_count()</code> et d&rsquo;autres acceptent maintenant un offset négatif. Cohérent avec ce que Python fait depuis toujours. Mieux vaut tard que jamais.</p>
<h2 id="closurefromcallable">Closure::fromCallable()</h2>
<p>Avant ça, convertir un callable (comme <code>[$object, 'method']</code> ou une chaîne nom de fonction) en une vraie <code>Closure</code> nécessitait <code>Closure::bind()</code> ou <code>bindTo()</code> avec une gestion de portée délicate. La 7.1 ajoute une méthode de fabrique statique :</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">Processor</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">transform</span>(<span style="color:#a6e22e">string</span> $value)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">strtoupper</span>($value);
</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">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getTransformer</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Closure</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Closure</span><span style="color:#f92672">::</span><span style="color:#a6e22e">fromCallable</span>([$this, <span style="color:#e6db74">&#39;transform&#39;</span>]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>La closure résultante capture le bon <code>$this</code> et la bonne portée. C&rsquo;est particulièrement utile quand on passe des méthodes comme callbacks à des fonctions qui attendent un <code>callable</code>, ou quand on construit des pipelines.</p>
<h2 id="argumentcounterror">ArgumentCountError</h2>
<p>En PHP 7.0, appeler une fonction définie par l&rsquo;utilisateur avec trop peu d&rsquo;arguments générait un warning et l&rsquo;exécution continuait avec des paramètres remplis à <code>null</code>. En 7.1, ça lève une <code>ArgumentCountError</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">function</span> <span style="color:#a6e22e">connect</span>(<span style="color:#a6e22e">string</span> $host, <span style="color:#a6e22e">int</span> $port)<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><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">connect</span>(<span style="color:#e6db74">&#39;localhost&#39;</span>); <span style="color:#75715e">// Lève ArgumentCountError
</span></span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">\ArgumentCountError</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>ArgumentCountError</code> étend <code>TypeError</code>, qui étend <code>Error</code>. Les call sites qui se dégradaient silencieusement auparavant échouent maintenant bruyamment. C&rsquo;est un risque de migration si on a du code qui comptait sur le comportement permissif, mais honnêtement, c&rsquo;est la bonne décision.</p>
<p>La 7.1 est le genre de version qui fait davantage faire confiance à une plateforme. La core team regardait clairement les frictions, pas seulement les titres à faire valoir.</p>
]]></content:encoded></item><item><title>PHP 7.0 : performances, types, et les fonctionnalités qui ont marqué</title><link>https://guillaumedelre.github.io/fr/2016/01/17/php-7.0-performances-types-et-les-fonctionnalit%C3%A9s-qui-ont-marqu%C3%A9/</link><pubDate>Sun, 17 Jan 2016 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2016/01/17/php-7.0-performances-types-et-les-fonctionnalit%C3%A9s-qui-ont-marqu%C3%A9/</guid><description>Part 1 of 11 in &amp;quot;Sorties PHP&amp;quot;: PHP 7.0 a doublé les performances grâce à une réécriture du Zend Engine et apporté enfin les type hints scalaires au langage.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.0 est sorti le 3 décembre. Un mois et demi plus tard, j&rsquo;ai migré deux projets et les résultats sont difficiles à ignorer.</p>
<p>Le chiffre phare : 2x plus rapide que PHP 5.6. Ce n&rsquo;est pas un benchmark cherry-pick — c&rsquo;est la médiane sur des applications réelles. Le Zend Engine a été réécrit pour utiliser une nouvelle représentation interne des valeurs, ce qui réduit significativement l&rsquo;utilisation mémoire et diminue les allocations. Sur un projet, le temps de réponse moyen a chuté de 40% sans aucune modification du code. On met à jour, et ça va plus vite.</p>
<p>Mais les performances ne sont pas la partie la plus intéressante.</p>
<h2 id="les-types-enfin">Les types, enfin</h2>
<p>PHP a eu les type hints pour les objets depuis la 5.0, pour les tableaux depuis la 5.1. En 7.0, on peut enfin déclarer des types scalaires pour les paramètres de fonctions et les valeurs 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">add</span>(<span style="color:#a6e22e">int</span> $a, <span style="color:#a6e22e">int</span> $b)<span style="color:#f92672">:</span> <span style="color:#a6e22e">int</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $a <span style="color:#f92672">+</span> $b;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>En mode strict (<code>declare(strict_types=1)</code>), passer un float à cette fonction lève une <code>TypeError</code>. En mode coercitif par défaut, PHP convertit la valeur. Cette distinction compte : le mode strict est par fichier, on peut donc l&rsquo;adopter progressivement sans tout casser d&rsquo;un coup.</p>
<p>Les déclarations de type de retour constituent l&rsquo;autre moitié. Placer l&rsquo;intention dans la signature plutôt que dans un docblock signifie que c&rsquo;est le moteur qui l&rsquo;applique, pas un code reviewer à moitié endormi.</p>
<h2 id="lopérateur-null-coalescent">L&rsquo;opérateur null coalescent</h2>
<p><code>??</code> est petit mais utilisé en permanence :</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>$username <span style="color:#f92672">=</span> $_GET[<span style="color:#e6db74">&#39;user&#39;</span>] <span style="color:#f92672">??</span> <span style="color:#e6db74">&#39;guest&#39;</span>;
</span></span></code></pre></div><p>Ça remplace <code>isset($_GET['user']) ? $_GET['user'] : 'guest'</code>. Il se chaîne aussi : <code>$a ?? $b ?? $c</code>. Après des années de bruit avec <code>isset()</code>, ça seul valait la mise à jour.</p>
<h2 id="la-partie-qui-casse">La partie qui casse</h2>
<p>La refonte de la gestion des erreurs est le vrai risque lors de la migration. Beaucoup d&rsquo;erreurs fatales sont maintenant des exceptions <code>Error</code>, attrapables mais différentes des <code>Exception</code>. Le code qui comptait sur les erreurs fatales pour stopper l&rsquo;exécution silencieusement a maintenant besoin d&rsquo;une gestion explicite. La suppression d&rsquo;erreurs avec <code>@</code> fonctionne aussi différemment par endroits.</p>
<p>Lire le guide de migration avant de toucher une appli en production. Le gain est réel, mais le fossé entre 5.6 et 7.0 est le plus large que PHP ait jamais eu.</p>
<h2 id="lopérateur-vaisseau-spatial">L&rsquo;opérateur vaisseau spatial</h2>
<p><code>&lt;=&gt;</code> est un opérateur de comparaison combiné qui retourne -1, 0 ou 1. Il est surtout là pour le tri :</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">usort</span>($users, <span style="color:#66d9ef">function</span> ($a, $b) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $a<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">age</span> <span style="color:#f92672">&lt;=&gt;</span> $b<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">age</span>;
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Avant ça, un comparateur de tri personnalisé était un petit exercice de mémoire arithmétique. <code>$a - $b</code> fonctionne pour les entiers mais plante silencieusement pour les flottants. <code>&lt;=&gt;</code> fait ce qu&rsquo;il faut pour chaque type comparable.</p>
<h2 id="les-classes-anonymes">Les classes anonymes</h2>
<p>On peut maintenant instancier une classe définie en ligne, sur le moment, sans lui donner de 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>$logger <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">class</span>($config) <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">LoggerInterface</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:#66d9ef">array</span> $config) {}
</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">log</span>(<span style="color:#a6e22e">string</span> $message)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">file_put_contents</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">config</span>[<span style="color:#e6db74">&#39;path&#39;</span>], $message <span style="color:#f92672">.</span> <span style="color:#a6e22e">PHP_EOL</span>, <span style="color:#a6e22e">FILE_APPEND</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Le cas d&rsquo;usage canonique, ce sont les doublures de test et les implémentations d&rsquo;interface ponctuelles qui ne méritent pas un fichier. Ça supprime une vraie friction : le fossé entre &ldquo;j&rsquo;ai besoin d&rsquo;un objet&rdquo; et &ldquo;je dois créer un fichier de classe pour un truc de 10 lignes&rdquo;.</p>
<h2 id="aléatoire-cryptographiquement-sûr">Aléatoire cryptographiquement sûr</h2>
<p>Les <code>rand()</code> et <code>mt_rand()</code> de PHP 5 n&rsquo;ont jamais été conçus pour la sécurité. La 7.0 ajoute deux fonctions qui le sont :</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>$token <span style="color:#f92672">=</span> <span style="color:#a6e22e">bin2hex</span>(<span style="color:#a6e22e">random_bytes</span>(<span style="color:#ae81ff">32</span>)); <span style="color:#75715e">// token hexadécimal de 64 caractères
</span></span></span><span style="display:flex;"><span>$pin   <span style="color:#f92672">=</span> <span style="color:#a6e22e">random_int</span>(<span style="color:#ae81ff">100000</span>, <span style="color:#ae81ff">999999</span>);
</span></span></code></pre></div><p><code>random_bytes()</code> puise dans le CSPRNG du système d&rsquo;exploitation. <code>random_int()</code> enveloppe ça pour les entiers. Ces fonctions remplacent tous les schémas de génération de tokens maison qui faisaient ça mal en silence, ce qui représente la majorité d&rsquo;entre eux.</p>
<h2 id="les-déclarations-use-groupées">Les déclarations use groupées</h2>
<p>Avant 7.0, importer cinq éléments depuis le même namespace nécessitait cinq instructions <code>use</code>. 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><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">App\Model\</span>{<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">Order</span>, <span style="color:#a6e22e">Product</span>};
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">App\Helpers\</span>{<span style="color:#a6e22e">formatDate</span>, <span style="color:#a6e22e">slugify</span>};
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">App\Config\</span>{<span style="color:#a6e22e">MAX_RETRIES</span>, <span style="color:#a6e22e">TIMEOUT</span>};
</span></span></code></pre></div><p>Petite amélioration ergonomique, mais qui réduit le bruit visuel en haut des fichiers avec des hiérarchies de namespaces profondes.</p>
<h2 id="les-générateurs-ont-grandi">Les générateurs ont grandi</h2>
<p>Les générateurs en 5.5 étaient intéressants mais incomplets. La 7.0 ajoute deux choses. Premièrement, un générateur peut maintenant avoir une valeur de retour, accessible après la fin de l&rsquo;itération :</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:#f92672">:</span> <span style="color:#a6e22e">Generator</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#e6db74">&#39;step 1&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#e6db74">&#39;step 2&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;done&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$gen <span style="color:#f92672">=</span> <span style="color:#a6e22e">process</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> ($gen <span style="color:#66d9ef">as</span> $step) { <span style="color:#75715e">/* ... */</span> }
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $gen<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getReturn</span>(); <span style="color:#75715e">// &#34;done&#34;
</span></span></span></code></pre></div><p>Deuxièmement, <code>yield from</code> délègue à un autre générateur ou itérable, en transmettant transparemment les valeurs et valeurs 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">inner</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Generator</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">2</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;inner done&#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">outer</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Generator</span> {
</span></span><span style="display:flex;"><span>    $result <span style="color:#f92672">=</span> <span style="color:#66d9ef">yield</span> <span style="color:#a6e22e">from</span> <span style="color:#a6e22e">inner</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">echo</span> $result; <span style="color:#75715e">// &#34;inner done&#34;
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">3</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Ça rend la composition de générateurs pratique sans avoir à câbler manuellement les valeurs entre eux.</p>
<h2 id="closurecall">Closure::call()</h2>
<p>Une façon plus directe de lier une closure à un objet et de l&rsquo;appeler 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:#66d9ef">class</span> <span style="color:#a6e22e">Counter</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">int</span> $count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$increment <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">int</span> $by)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">count</span> <span style="color:#f92672">+=</span> $by;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$increment<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">call</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Counter</span>(), <span style="color:#ae81ff">5</span>);
</span></span></code></pre></div><p><code>bindTo()</code> existait avant mais nécessitait deux étapes. <code>call()</code> les fusionne et est plus rapide à l&rsquo;exécution car il évite la création d&rsquo;une closure intermédiaire.</p>
<h2 id="syntaxe-déchappement-unicode-dans-les-chaînes">Syntaxe d&rsquo;échappement Unicode dans les chaînes</h2>
<p>On peut maintenant intégrer des caractères Unicode directement dans les chaînes entre guillemets doubles ou les heredocs via un point de 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">echo</span> <span style="color:#e6db74">&#34;\u{1F418}&#34;</span>; <span style="color:#75715e">// 🐘
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;\u{00E9}&#34;</span>;  <span style="color:#75715e">// é
</span></span></span></code></pre></div><p>C&rsquo;est mieux que de copier-coller des caractères depuis une table Unicode dans les fichiers sources, ce que les gens faisaient vraiment.</p>
<h2 id="un-unserialize-plus-sûr">Un unserialize() plus sûr</h2>
<p><code>unserialize()</code> a une longue histoire d&rsquo;être un vecteur d&rsquo;attaques par injection d&rsquo;objets. La 7.0 ajoute une option <code>allowed_classes</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>$data <span style="color:#f92672">=</span> <span style="color:#a6e22e">unserialize</span>($input, [<span style="color:#e6db74">&#39;allowed_classes&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">false</span>]);
</span></span><span style="display:flex;"><span>$data <span style="color:#f92672">=</span> <span style="color:#a6e22e">unserialize</span>($input, [<span style="color:#e6db74">&#39;allowed_classes&#39;</span> <span style="color:#f92672">=&gt;</span> [<span style="color:#a6e22e">User</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>, <span style="color:#a6e22e">Order</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>]]);
</span></span></code></pre></div><p>Passer <code>false</code> empêche toute instanciation d&rsquo;objet pendant la désérialisation. C&rsquo;est le comportement par défaut à adopter quand on désérialise des données non fiables.</p>
<h2 id="1234-division-entière">:1234: Division entière</h2>
<p><code>intdiv()</code> est une division entière explicite sans intermédiaire flottant :</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>$pages <span style="color:#f92672">=</span> <span style="color:#a6e22e">intdiv</span>(<span style="color:#a6e22e">count</span>($items), $perPage); <span style="color:#75715e">// int, pas besoin de cast
</span></span></span></code></pre></div><p>Oui, on pourrait caster le résultat d&rsquo;une division. <code>intdiv()</code> rend l&rsquo;intention claire et évite les cas limites de précision flottante que le cast introduit pour les grands nombres.</p>
<h2 id="les-constantes-en-tableaux">Les constantes en tableaux</h2>
<p>Avant 7.0, <code>define()</code> n&rsquo;acceptait que les valeurs scalaires. Les tableaux fonctionnaient avec <code>const</code> au niveau de la classe ou du namespace mais pas avec <code>define()</code>. Maintenant si :</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">define</span>(<span style="color:#e6db74">&#39;HTTP_METHODS&#39;</span>, [<span style="color:#e6db74">&#39;GET&#39;</span>, <span style="color:#e6db74">&#39;POST&#39;</span>, <span style="color:#e6db74">&#39;PUT&#39;</span>, <span style="color:#e6db74">&#39;DELETE&#39;</span>, <span style="color:#e6db74">&#39;PATCH&#39;</span>]);
</span></span></code></pre></div><p>Utile pour la configuration qui doit être une constante mais qui vit en dehors d&rsquo;une classe.</p>
<h2 id="des-assertions-avec-des-dents">Des assertions avec des dents</h2>
<p><code>assert()</code> a reçu une vraie refonte. En PHP 5, les assertions étaient un eval de chaînes à l&rsquo;exécution. Maintenant elles peuvent lever des exceptions et être complètement supprimées en production avec zéro overhead :</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">// Dans php.ini ou au bootstrap :
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// assert.active = 1 (dev), 0 (prod)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// assert.exception = 1
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">assert</span>($user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isVerified</span>(), <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\LogicException</span>(<span style="color:#e6db74">&#39;Unverified user reached checkout&#39;</span>));
</span></span></code></pre></div><p>Quand <code>assert.active = 0</code>, l&rsquo;expression n&rsquo;est jamais évaluée. Quand c&rsquo;est activé, une assertion qui échoue lève directement l&rsquo;exception fournie. C&rsquo;est enfin un outil qu&rsquo;on peut utiliser sans honte.</p>
<h2 id="la-refonte-de-session_start">La refonte de session_start()</h2>
<p><code>session_start()</code> accepte maintenant un tableau d&rsquo;options qui surchargent les directives <code>php.ini</code> pour cet appel :</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">session_start</span>([
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_lifetime&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">86400</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_secure&#39;</span>   <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_httponly&#39;</span>  <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_samesite&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;Lax&#39;</span>,
</span></span><span style="display:flex;"><span>]);
</span></span></code></pre></div><p>Avant ça, on définissait soit les options globalement dans <code>php.ini</code>, soit on appelait <code>ini_set()</code> avant <code>session_start()</code>. Aucune des deux n&rsquo;était top quand on avait besoin de configurations de session différentes dans différentes parties d&rsquo;une appli.</p>
]]></content:encoded></item></channel></rss>