<?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>Attributs on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/attributs/</link><description>Recent content in Attributs on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Fri, 12 Jan 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/attributs/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 7.0 : PHP 8.2 minimum et les annotations enfin disparues</title><link>https://guillaumedelre.github.io/fr/2024/01/12/symfony-7.0-php-8.2-minimum-et-les-annotations-enfin-disparues/</link><pubDate>Fri, 12 Jan 2024 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2024/01/12/symfony-7.0-php-8.2-minimum-et-les-annotations-enfin-disparues/</guid><description>Part 9 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 7.0 exige PHP 8.2, abandonne entièrement les annotations Doctrine et livre un composant Workflow reconstruit.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 7.0 est sorti le 29 novembre 2023, le même jour que 6.4. Le pattern tient : la version X.0 coupe le code déprécié et élève le plancher PHP. 7.0 exige PHP 8.2 et supprime tout ce que 6.4 avait marqué comme déprécié.</p>
<p>La suppression la plus visible : les annotations Doctrine. <code>@Route</code>, <code>@ORM\Column</code>, <code>@Assert</code> — disparus. Les attributs PHP natifs sont l&rsquo;approche recommandée depuis Symfony 5.2. 7.0 rend juste ça officiel.</p>
<h2 id="les-attributs-partout">Les attributs partout</h2>
<p>La migration des annotations vers les attributs est principalement mécanique : la syntaxe passe de <code>@</code> à <code>#[]</code>, et les références de classes passent des classes d&rsquo;annotation Doctrine aux classes d&rsquo;attribut 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:#75715e">// avant
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">/** @Route(&#39;/users&#39;, methods={&#34;GET&#34;}) */</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:#75715e">#[Route(&#39;/users&#39;, methods: [&#39;GET&#39;])]
</span></span></span></code></pre></div><p>Le vrai gain n&rsquo;est pas juste la syntaxe : les attributs sont validés par le moteur PHP, pas un parseur de docblock. Les IDEs peuvent les résoudre sans plugins personnalisés. Les outils d&rsquo;analyse statique les comprennent nativement. Fini les &ldquo;ça échoue silencieusement à l&rsquo;exécution à cause d&rsquo;une faute de frappe dans un commentaire.&rdquo;</p>
<h2 id="workflow-avec-attributs-php">Workflow avec attributs PHP</h2>
<p>Les listeners et guards d&rsquo;événements Workflow peuvent maintenant être enregistrés via des attributs :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">#[AsGuard(workflow: &#39;order&#39;, transition: &#39;ship&#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">canShip</span>(<span style="color:#a6e22e">Event</span> $event)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>$event<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getSubject</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isPaymentConfirmed</span>()) {
</span></span><span style="display:flex;"><span>        $event<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setBlocked</span>(<span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Le profiler de workflow, un panneau dédié montrant le marquage courant et les transitions disponibles, est un outil de debug vraiment utile quand on travaille avec des machines à états complexes.</p>
<h2 id="datepoint-dans-le-composant-clock">DatePoint dans le composant Clock</h2>
<p><code>DatePoint</code>, le <code>DateTime</code> immutable avec gestion stricte des erreurs introduit dans 6.4, est maintenant la façon recommandée de travailler avec les dates. Combiné avec les propriétés readonly de PHP 8.2, les objets-valeur de dates dans le code de domaine deviennent presque trivialement propres :</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">Order</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">DatePoint</span> $createdAt,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">DatePoint</span> $shippedAt <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><h2 id="ce-que-70-supprime">Ce que 7.0 supprime</h2>
<p>La liste complète des suppressions : le support des annotations Doctrine, le bridge du composant <code>Templating</code>, le bridge <code>ProxyManager</code>, le bridge <code>Monolog</code> pour les versions inférieures à 3.0, et le transport Sendinblue (remplacé par Brevo). Le support PHP 8.0 et 8.1 se termine aussi. 8.2 est le plancher maintenant.</p>
<p>Monter de 6.4 avec toutes les notices de dépréciation corrigées, et 7.0 est fluide. Sauter cette étape et on s&rsquo;expose à une mauvaise surprise.</p>
<h2 id="scheduler-et-assetmapper-diplômés">Scheduler et AssetMapper diplômés</h2>
<p>Deux composants qui sont sortis en expérimental dans 6.3 sont maintenant stables : Scheduler et AssetMapper. Stable signifie des APIs verrouillées, plus de mises en garde <code>@experimental</code>, et ils apparaissent correctement dans le guide de mise à jour. On peut vraiment compter sur eux maintenant.</p>
<p>Scheduler reçoit <code>#[AsCronTask]</code> et <code>#[AsPeriodicTask]</code> pour l&rsquo;enregistrement de tâches par attribut, la modification de planning à l&rsquo;exécution avec recalcul du heap, <code>FailureEvent</code>, et une option <code>--date</code> sur <code>schedule:debug</code>. AssetMapper ajoute le support des fichiers CSS dans l&rsquo;importmap, une commande <code>outdated</code>, une commande <code>audit</code>, et le préchargement automatique via WebLink.</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">#[AsCronTask(&#39;0 2 * * *&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">NightlyReportMessage</span> {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#[AsPeriodicTask(frequency: &#39;1 hour&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">HourlyCleanupMessage</span> {}
</span></span></code></pre></div><h2 id="le-câblage-de-services-reçoit-deux-nouveaux-attributs">Le câblage de services reçoit deux nouveaux attributs</h2>
<p><code>#[AutowireLocator]</code> et <code>#[AutowireIterator]</code> ont atterri dans 6.4 et sont stables dans 7.0. Ils remplacent la configuration verbose XML/YAML des service locators taggués par quelque chose qu&rsquo;on peut juste mettre directement en 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:#66d9ef">class</span> <span style="color:#a6e22e">HandlerRegistry</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></span><span style="display:flex;"><span>        <span style="color:#75715e">#[AutowireLocator(&#39;app.handler&#39;, indexAttribute: &#39;key&#39;)]
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">ContainerInterface</span> $handlers,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>#[Target]</code> est aussi plus intelligent : quand un service a un alias d&rsquo;autowiring nommé comme <code>invoice.lock.factory</code>, on peut maintenant écrire <code>#[Target('invoice')]</code> au lieu du nom complet de l&rsquo;alias. Moins de bruit quand le type dit déjà ce qu&rsquo;on veut.</p>
<h2 id="messenger-reçoit-une-gestion-plus-précise-des-échecs">Messenger reçoit une gestion plus précise des échecs</h2>
<p><code>RejectRedeliveredMessageException</code> dit au worker de ne pas retenter un message, ce qui est pratique quand un message arrive deux fois à cause d&rsquo;un timeout d&rsquo;ack du transport et qu&rsquo;on a besoin d&rsquo;une sémantique exactly-once. <code>messenger:failed:remove --all</code> vide tout le transport d&rsquo;échec en un coup, pas de boucle nécessaire. Les retries échoués peuvent aussi aller directement au transport d&rsquo;échec, en contournant entièrement la queue de retry.</p>
<p>Plusieurs hôtes Redis Sentinel sont maintenant supportés dans le DSN :</p>
<pre tabindex="0"><code>redis-sentinel://host1:26379,host2:26379,host3:26379/mymaster
</code></pre><h2 id="console-reçoit-les-noms-de-signaux-et-le-profilage-de-commandes">Console reçoit les noms de signaux et le profilage de commandes</h2>
<p><code>SignalMap</code> mappe les entiers de signaux à leurs noms POSIX. Quand un worker attrape <code>SIGTERM</code>, le log dit maintenant <code>SIGTERM</code> au lieu de <code>15</code>. Petite chose, vraie amélioration. <code>ConsoleTerminateEvent</code> est dispatché même quand le processus se termine via signal, ce qui n&rsquo;était pas le cas avant 7.0.</p>
<p>Le profilage de commandes arrive aussi : passer <code>--profile</code> à <code>bin/console</code> et les données collectées vont directement dans le profiler Symfony, navigable depuis l&rsquo;UI web.</p>
<h2 id="form--des-petites-choses-qui-saccumulent">Form : des petites choses qui s&rsquo;accumulent</h2>
<p><code>ChoiceType</code> reçoit une option <code>duplicate_preferred_choices</code>. La définir à <code>false</code> et on arrête de montrer la même option deux fois quand les choix préférés chevauchent la liste complète. <code>FormEvent::setData()</code> est déprécié pour les événements où les données sont déjà verrouillées à ce point du cycle de vie. La barre oblique auto-fermante sur les éléments <code>&lt;input&gt;</code> est aussi supprimée : <code>&lt;input&gt;</code> est un élément void en HTML5 et la barre oblique était techniquement invalide.</p>
<p>Le support des enums dans les formulaires est bien fait : <code>ChoiceType</code> rend les backed enums directement, et les enums translatable reçoivent leurs labels via le translator sans câblage personnalisé.</p>
<h2 id="httpfoundation--small-but-useful">HttpFoundation : small but useful</h2>
<p><code>Response::send()</code> reçoit un paramètre <code>$flush</code>. Passer <code>false</code> pour bufferiser la sortie sans la flusher au client, utile quand on enchaîne des middlewares qui doivent inspecter la réponse avant qu&rsquo;elle quitte le processus.</p>
<p><code>UriSigner</code> passe de HttpKernel à HttpFoundation, où il appartient sémantiquement. Même nom de classe, namespace différent.</p>
<p>Les cookies reçoivent le support CHIPS (Cookies Having Independent Partitioned State), le mécanisme navigateur pour les cookies cross-site dans une partition first-party. Ça ne compte que si on construit des widgets embarquables, mais bon à savoir que c&rsquo;est là.</p>
<h2 id="translation--provider-phrase-et-sortie-arborescente">Translation : provider Phrase et sortie arborescente</h2>
<p>Phrase rejoint Crowdin et Lokalise comme provider de traduction supporté. Le configurer dans <code>config/packages/translation.yaml</code> et les commandes <code>translation:push</code> / <code>translation:pull</code> gèrent la synchronisation.</p>
<p><code>translation:pull</code> reçoit une option <code>--as-tree</code> qui écrit les fichiers de traduction en YAML imbriqué plutôt qu&rsquo;en clés à notation pointée plate. Si c&rsquo;est vraiment mieux dépend entièrement de l&rsquo;équipe.</p>
<p><code>LocaleSwitcher::runWithLocale()</code> passe maintenant la locale courante comme argument au callback, évitant un appel <code>getLocale()</code> à l&rsquo;intérieur :</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>$switcher<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">runWithLocale</span>(<span style="color:#e6db74">&#39;fr&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">string</span> $locale) <span style="color:#66d9ef">use</span> ($mailer) {
</span></span><span style="display:flex;"><span>    $mailer<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">send</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">buildEmail</span>($locale));
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h2 id="quelques-choses-dans-serializer-et-domcrawler">Quelques choses dans Serializer et DomCrawler</h2>
<p>L&rsquo;attribut <code>Context</code> du Serializer peut maintenant cibler des classes spécifiques, donc un seul DTO peut se comporter différemment pendant la (dé)sérialisation selon quelle classe détient le contexte. <code>TranslatableNormalizer</code> arrive pour normaliser les objets qui implémentent <code>TranslatableInterface</code> : le translator est appelé pendant la normalisation, pas avant.</p>
<p><code>Crawler::attr()</code> reçoit un paramètre <code>$default</code>. Au lieu de null-checker la valeur de retour, on passe une valeur de repli :</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>$src <span style="color:#f92672">=</span> $crawler<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">attr</span>(<span style="color:#e6db74">&#39;src&#39;</span>, <span style="color:#e6db74">&#39;/placeholder.png&#39;</span>);
</span></span></code></pre></div><p><code>assertAnySelectorText()</code> et <code>assertAnySelectorTextContains()</code> rejoignent l&rsquo;ensemble d&rsquo;assertions DomCrawler. Ils passent si au moins un élément correspondant satisfait la condition, plutôt que d&rsquo;en exiger que tous correspondent.</p>
<h2 id="httpclient--réponses-har-pour-les-tests">HttpClient : réponses HAR pour les tests</h2>
<p><code>MockResponse</code> accepte maintenant les fichiers HAR (HTTP Archive). Enregistrer de vraies interactions HTTP dans le navigateur ou avec un proxy, déposer le fichier <code>.har</code> dans les fixtures de tests, et les rejouer :</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>$client <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MockHttpClient</span>(<span style="color:#a6e22e">HarFileResponseFactory</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromFile</span>(<span style="color:#66d9ef">__DIR__</span><span style="color:#f92672">.</span><span style="color:#e6db74">&#39;/fixtures/api.har&#39;</span>));
</span></span></code></pre></div><p>Bien mieux qu&rsquo;écrire des stubs de réponse à la main quand on traite avec une API complexe.</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></channel></rss>