tag:linuxfr.org,2005:/users/windu2b/newsLinuxFr.org : les dépêches de windu.2b2023-03-06T15:09:53+01:00/favicon.pngtag:linuxfr.org,2005:News/413522023-03-06T15:09:53+01:002023-03-06T15:09:53+01:00Sortie de Laravel 10Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Laravel, l'un des frameworks PHP les plus en vogue en ce moment, est sorti en version 10 le 7 février 2023.<br>
Petit tour des nouveautés !</p>
</div><ul><li>lien nᵒ 1 : <a title="https://laravel.com/" hreflang="en" href="https://linuxfr.org/redirect/111809">Site officiel</a></li><li>lien nᵒ 2 : <a title="https://laravel-news.com/laravel-10" hreflang="en" href="https://linuxfr.org/redirect/111810">Annonce de la sortie</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<ul>
<li>
<a href="#toc-liste-non-exhaustive-des-nouveaut%C3%A9s">Liste (non-exhaustive) des nouveautés</a><ul>
<li><a href="#toc-fin-du-support-de-php-80">Fin du support de PHP 8.0</a></li>
<li><a href="#toc-d%C3%A9clarations-natives-et-typage-fort">Déclarations natives et typage fort</a></li>
<li><a href="#toc-les-r%C3%A8gles-de-validation-invokable-deviennent-le-fonctionnement-par-d%C3%A9faut">Les règles de validation <em>Invokable</em> deviennent le fonctionnement par défaut</a></li>
<li><a href="#toc-process-layer">Process Layer</a></li>
<li><a href="#toc-d%C3%A9tecter-les-tests-les-plus-lents">Détecter les tests les plus lents</a></li>
</ul>
</li>
<li><a href="#toc-fin-du-partenariat-avec-le-laravel-certification-program">Fin du partenariat avec le "Laravel Certification Program"</a></li>
<li><a href="#toc-laravel-pennant">Laravel Pennant</a></li>
<li><a href="#toc-laravel-11">Laravel 11 ?</a></li>
</ul>
</li>
</ul>
<p>Un an après la version 9, Laravel maintient son rythme de sortie (qui était autrefois d'une nouvelle version majeure tous les six mois).<br>
Avec, toutefois, une évolution qui se veut sans (trop de) révolution : les changements sont plus discrets, et nécessitent moins d'intervention ou d'attention lors de la montée de version, <a href="https://laravel.com/docs/10.x/upgrade#main-content">comme le montre d'ailleurs la taille de la page "Upgrade Guide"</a>, qui est <a href="https://twitter.com/PovilasKorop/status/1625766366572605441">l'une des plus petites de ces dernières années.</a></p>
<h3 id="toc-liste-non-exhaustive-des-nouveautés">Liste (non-exhaustive) des nouveautés</h3>
<h4 id="toc-fin-du-support-de-php-80">Fin du support de PHP 8.0</h4>
<p>Avec cette nouvelle version vient l'abandon du support de la version 8.0 de PHP (qui est, de toute façon, dans sa phase "<em>security support</em>" <a href="https://www.php.net/supported-versions.php">depuis novembre 2022</a>). En effet, des nouveautés de PHP 8.1 et 8.2 (seules versions supportées, donc) sont utilisées par Laravel, telles que les <code>enum</code> et les propriétés de classes <em>readonly</em>.</p>
<h4 id="toc-déclarations-natives-et-typage-fort">Déclarations natives et typage fort</h4>
<p>Désormais, Laravel aura beaucoup plus recours au typage et aux déclarations natives. Finis les commentaires en PHPDoc : place aux types partout !<br>
Ainsi, <a href="https://github.com/laravel/framework/blob/9.x/src/Illuminate/Foundation/Console/stubs/cast.inbound.stub">ce fichier stub de Laravel 9</a> :</p>
<pre><code class="php"><span class="k">namespace</span> <span class="p">{{</span> <span class="k">namespace</span> <span class="p">}};</span>
<span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Model</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes</span><span class="p">;</span>
<span class="k">class</span> <span class="err">{{ </span><span class="nc">class</span> <span class="p">}}</span> <span class="k">implements</span> <span class="nx">CastsInboundAttributes</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Prepare the given value for storage.</span>
<span class="sd"> *</span>
<span class="sd"> * @param \Illuminate\Database\Eloquent\Model $model</span>
<span class="sd"> * @param string $key</span>
<span class="sd"> * @param mixed $value</span>
<span class="sd"> * @param array $attributes</span>
<span class="sd"> * @return mixed</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">set</span><span class="p">(</span><span class="nv">$model</span><span class="p">,</span> <span class="nx">string</span> <span class="nv">$key</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$attributes</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p><a href="https://github.com/laravel/framework/blob/be2ddb5c31b0b9ebc2738d9f37a9d4c960aa3199/src/Illuminate/Foundation/Console/stubs/cast.inbound.stub">devient désormais, en Laravel 10</a> :</p>
<pre><code class="php"><span class="k">namespace</span> <span class="p">{{</span> <span class="k">namespace</span> <span class="p">}};</span>
<span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Model</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes</span><span class="p">;</span>
<span class="k">class</span> <span class="err">{{ </span><span class="nc">class</span> <span class="p">}}</span> <span class="k">implements</span> <span class="nx">CastsInboundAttributes</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Prepare the given value for storage.</span>
<span class="sd"> *</span>
<span class="sd"> * @param array<string, mixed> $attributes</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">set</span><span class="p">(</span><span class="nx">Model</span> <span class="nv">$model</span><span class="p">,</span> <span class="nx">string</span> <span class="nv">$key</span><span class="p">,</span> <span class="nx">mixed</span> <span class="nv">$value</span><span class="p">,</span> <span class="k">array</span> <span class="nv">$attributes</span><span class="p">)</span><span class="o">:</span> <span class="nx">mixed</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p>Deux choses à noter :</p>
<ul>
<li>La documentation est certes réduite (inutile de déclarer le type d'un paramètre en commentaire ET dans la signature de la méthode), mais il reste encore les informations concernant la structure des tableaux (en attendant qu'une future version de PHP permette de déclarer cela, comme en Java ?) ;</li>
<li>L'exemple ci-dessus provient d'un fichier stub de Laravel : ce sont des fichiers "à trous" (d'où le fait que ni le <em>namespace</em> ni le nom de la classe ne soient renseignés) utilisés lorsque le développeur veut créer une nouvelle classe (modèle, contrôleur, job, …) au sein de son projet. Le but ici étant de ne pas limiter au framework uniquement cette nouveauté mais, au contraire, de propager cet usage du typage dans le code du développeur.</li>
</ul>
<h4 id="toc-les-règles-de-validation-invokable-deviennent-le-fonctionnement-par-défaut">Les règles de validation <em>Invokable</em> deviennent le fonctionnement par défaut</h4>
<p>Laravel fournit de base un ensemble de règles de validation (appelées <em>Rules</em>) pour vérifier que les données provenant des formulaires sont correctes. Il est néanmoins possible de créer les siennes.<br>
Jusqu'à présent, une telle rule devait être écrite ainsi :</p>
<pre><code class="php"><span class="k">namespace</span> <span class="nx">App\Rules</span><span class="p">;</span>
<span class="nx"> </span>
<span class="k">use</span> <span class="nx">Illuminate\Contracts\Validation\Rule</span><span class="p">;</span>
<span class="nx"> </span>
<span class="k">class</span> <span class="nc">Uppercase</span> <span class="k">implements</span> <span class="nx">Rule</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Determine if the validation rule passes.</span>
<span class="sd"> *</span>
<span class="sd"> * @param string $attribute</span>
<span class="sd"> * @param mixed $value</span>
<span class="sd"> * @return bool</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">passes</span><span class="p">(</span><span class="nv">$attribute</span><span class="p">,</span> <span class="nv">$value</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nb">strtoupper</span><span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="o">===</span> <span class="nv">$value</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx"> </span>
<span class="sd">/**</span>
<span class="sd"> * Get the validation error message.</span>
<span class="sd"> *</span>
<span class="sd"> * @return string</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">message</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="s1">'The :attribute must be uppercase.'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p>À partir de Laravel 9, on peut la simplifier en utilisant la méthode magique <code>__invoke</code> de PHP, et donc écrire la même <em>rule</em> ainsi :</p>
<pre><code class="php"><span class="k">namespace</span> <span class="nx">App\Rules</span><span class="p">;</span>
<span class="nx"> </span>
<span class="k">use</span> <span class="nx">Illuminate\Contracts\Validation\InvokableRule</span><span class="p">;</span>
<span class="nx"> </span>
<span class="k">class</span> <span class="nc">Uppercase</span> <span class="k">implements</span> <span class="nx">InvokableRule</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Run the validation rule.</span>
<span class="sd"> *</span>
<span class="sd"> * @param string $attribute</span>
<span class="sd"> * @param mixed $value</span>
<span class="sd"> * @param \Closure $fail</span>
<span class="sd"> * @return void</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="fm">__invoke</span><span class="p">(</span><span class="nv">$attribute</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="nv">$fail</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">strtoupper</span><span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="o">!==</span> <span class="nv">$value</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$fail</span><span class="p">(</span><span class="s1">'The :attribute must be uppercase.'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p>Néanmoins, pour que ce changement se fasse en douceur, il avait été décidé qu'il ne serait possible qu'en ajoutant le paramètre <code>--invokable</code> à la ligne de commande.<br>
Ainsi, pour obtenir la classe telle que dans l'exemple ci-dessus, il fallait faire : <code>php artisan make:rule Uppercase --invokable</code>.</p>
<p>Depuis Laravel 10, ce comportement est désormais celui par défaut, et le paramètre optionnel a donc disparu de la ligne de commande indiqué précédemment.</p>
<h4 id="toc-process-layer">Process Layer</h4>
<p>Déjà existant dans Symfony depuis longtemps, ce service-objet permet de lancer des processus en ligne de commande, et d’interagir avec (code d'erreur, valeur renvoyée, …). Comme souvent dans Laravel, l'usage d'une <em>fluent interface</em> y est très présent, permettant l'enchaînement d'appels de méthodes pour construire le processus voulu, avant de l'exécuter.</p>
<pre><code class="php"><span class="k">use</span> <span class="nx">Illuminate\Support\Facades\Process</span><span class="p">;</span>
<span class="nv">$result</span> <span class="o">=</span> <span class="nx">Process</span><span class="o">::</span><span class="na">run</span><span class="p">(</span><span class="s1">'ls -la'</span><span class="p">);</span>
<span class="nv">$result</span><span class="o">-></span><span class="na">successful</span><span class="p">();</span>
<span class="nv">$result</span><span class="o">-></span><span class="na">failed</span><span class="p">();</span>
<span class="nv">$result</span><span class="o">-></span><span class="na">exitCode</span><span class="p">();</span>
<span class="nv">$result</span><span class="o">-></span><span class="na">output</span><span class="p">();</span>
<span class="nv">$result</span><span class="o">-></span><span class="na">errorOutput</span><span class="p">();</span>
<span class="nv">$result</span><span class="o">-></span><span class="na">throw</span><span class="p">();</span>
<span class="nv">$result</span><span class="o">-></span><span class="na">throwIf</span><span class="p">(</span><span class="nv">$condition</span><span class="p">);</span></code></pre>
<p>À noter que, comme <a href="https://laravel.com/docs/10.x/http-client#main-content">le client HTTP</a> déjà présent dans Laravel, qui (comme son nom l'indique) permet d'interagir avec des appels HTTP(S), ce service est prévu pour pouvoir être "mocké", afin de faciliter l'écriture des tests.</p>
<h4 id="toc-détecter-les-tests-les-plus-lents">Détecter les tests les plus lents</h4>
<p>Une nouvelle option <code>--profile</code> est apparue, pour faire ressortir les 10 tests les plus lents à l'exécution. Cela permet de détecter ceux qui auraient peut-être besoin d'être optimisés ou, parce que ça n'est pas toujours possible de les rendre plus rapides, de voir lesquels devraient être regroupés dans un lot de tests lents (qui ne seraient pas exécutés tout le temps, par exemple).</p>
<h3 id="toc-fin-du-partenariat-avec-le-laravel-certification-program">Fin du partenariat avec le "Laravel Certification Program"</h3>
<p>Depuis plusieurs années, Laravel avait un partenariat avec un organisme extérieur, le reconnaissant comme seul autorisé à délivrer officiellement une certification de Laravel. <a href="https://twitter.com/taylorotwell/status/1610661365110247425">Depuis ce début d'année</a>, ce partenariat n'existe plus, et il n'y en a pas d'autres officiels pour autant (ce qui n'interdit pas quiconque de créer une certification Laravel).</p>
<h3 id="toc-laravel-pennant">Laravel Pennant</h3>
<p>Il s'agit non pas d'une nouveauté de Laravel 10, mais d'un <em>first-class package</em> dont la sortie coïncide quasiment, et qui était très demandé et attendu par la communauté.</p>
<p><a href="https://laravel.com/docs/10.x/pennant">Laravel Pennant</a> est un package officiel dont le but est de permettre de faire du <em>feature flag</em>. Il s'agit d'une technique d'ingénierie logicielle permettant d'activer/désactiver une fonctionnalité à tout ou partie des utilisateurs d'une application. Ainsi, on peut tester en production l'accueil qu'aura une nouvelle interface graphique, ne proposer dans un premier temps une fonctionnalité qu'à des utilisateurs triés sur le volet.</p>
<h3 id="toc-laravel-11">Laravel 11 ?</h3>
<p>Bien que la prochaine version majeure de Laravel ne soit pas attendue avec début 2024, on sait déjà au moins une chose la concernant : <a href="https://github.com/laravel/framework/pull/45526">elle abandonnera le support de PHP 8.1</a> ! Seules les versions 8.2 et 8.3 (dont la sortie est prévue en novembre 2023) de PHP seront donc supportées.</p>
</div><div><a href="https://linuxfr.org/news/sortie-de-laravel-10.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/129952/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/sortie-de-laravel-10#comments">ouvrir dans le navigateur</a>
</p>
windu.2bBenoît SibaudNÿcoXavier Teyssierbobble bubblehttps://linuxfr.org/nodes/129952/comments.atomtag:linuxfr.org,2005:News/409932022-12-05T19:36:21+01:002022-12-06T09:58:52+01:00PHP sort en version 8.2Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Après 3 Alpha, 3 Beta et 7 RC, et tout juste un an après la sortie de <a href="//linuxfr.org/news/la-version-8-1-de-php-et-creation-de-la-fondation-php">PHP 8.1</a>, PHP, le langage de programmation pour le Web le plus connu et le plus utilisé, devrait être disponible après-demain en version 8.2, la sortie initiale prévue au 24 novembre ayant été reportée.<br>
Petit tour (non-exhaustif) des nouveautés !</p>
</div><ul></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<ul>
<li>
<a href="#toc-nouvelles-fonctionnalit%C3%A9s">Nouvelles fonctionnalités</a><ul>
<li>
<a href="#toc-classes-en-lecture-seule-readonly-classes--rfc">Classes en lecture seule (<em>Readonly classes</em> : </a><a href="https://wiki.php.net/rfc/readonly_classes">RFC</a>)</li>
<li>
<a href="#toc-d%C3%A9pr%C3%A9ciation-des-propri%C3%A9t%C3%A9s-dynamiques-deprecate-dynamic-properties--rfc">Dépréciation des propriétés dynamiques (<em>Deprecate dynamic properties</em> : </a><a href="https://wiki.php.net/rfc/deprecate_dynamic_properties">RFC</a>)<ul>
<li><a href="#toc-ajouter-les-m%C3%A9thodes-magiques-__get-etou-__set-">Ajouter les méthodes magiques <code>__get</code> et/ou <code>__set</code> :</a></li>
<li><a href="#toc-utiliser-la-classe-stdclass-de-php-ou-en-h%C3%A9riter-">Utiliser la classe <code>stdClass</code> de PHP (ou en hériter) :</a></li>
<li><a href="#toc-ajouter-lattribut-allowdynamicproperties-%C3%A0-la-classe-">Ajouter l'attribut <code>#[AllowDynamicProperties]</code> à la classe :</a></li>
</ul>
</li>
<li>
<a href="#toc-nouveaux-types-de-retour--null-true-et-false-allow-null-and-false-as-stand-alone-types--rfc">Nouveaux types de retour : <code>null</code>, <code>true</code>, et <code>false</code> (<em>Allow null and false as stand-alone types</em> : </a><a href="https://wiki.php.net/rfc/null-false-standalone-types">RFC</a>)</li>
<li>
<a href="#toc-masquage-des-donn%C3%A9es-sensibles-dans-les-logs-redacting-parameters-in-back-traces--rfc">Masquage des données sensibles dans les logs (<em>Redacting parameters in back traces</em> : </a><a href="https://wiki.php.net/rfc/redact_parameters_in_back_traces">RFC</a>)</li>
<li>
<a href="#toc-constantes-dans-les-traits-rfc">Constantes dans les Traits (</a><a href="https://wiki.php.net/rfc/constants_in_traits">RFC</a>)</li>
<li>
<a href="#toc-retrait-du-support-de-libmysql-remove-support-for-libmysql-from-mysqli--rfc">Retrait du support de <em>libmysql</em> (<em>Remove support for libmysql from mysqli</em> : </a><a href="https://wiki.php.net/rfc/mysqli_support_for_libmysql">RFC</a>)</li>
<li><a href="#toc-liste-compl%C3%A8te-des-rfc">Liste complète des RFC</a></li>
</ul>
</li>
<li><a href="#toc-php-83">PHP 8.3</a></li>
</ul>
</li>
</ul>
<h3 id="toc-nouvelles-fonctionnalités">Nouvelles fonctionnalités</h3>
<h4 id="toc-classes-en-lecture-seule-readonly-classes--rfc">Classes en lecture seule (<em>Readonly classes</em> : <a href="https://wiki.php.net/rfc/readonly_classes">RFC</a>)</h4>
<p>Après la possibilité offerte par PHP 8.1 d'ajouter le mot-clé <em><a href="https://wiki.php.net/rfc/readonly_properties_v2">readonly</a></em> aux propriétés d'une classe, c'est désormais directement sur la classe que l'on peut le faire.<br>
Cela permet d'alléger le code, en évitant la répétition du même mot-clé sur chacune des propriétés d'une classe (au risque de l'oublier sur l'une d'entre elles, notamment lorsque le code vient à évoluer), et de le rendre plus clair (quand la classe est <em>readonly</em>, toutes ses propriétés le sont forcément).<br>
Cela permet aussi d'empêcher la création de propriétés dynamiques (propriétés non déclarées dans le corps de la classe).<br>
Le principal usage est bien évidemment les classes DTO (<em>Data Transfer Object</em>), en permettant d'instancier des objets garantis immuables.</p>
<p>On est donc passé de ceci, en PHP 8.0 :</p>
<pre><code class="php"><span class="k">class</span> <span class="nc">Book</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Constructeur PHP utilisant la nouvelle syntaxe "Constructor property promotion" apparue avec PHP 8.0</span>
<span class="sd"> * @see https://stitcher.io/blog/new-in-php-8#constructor-property-promotion-rfc</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
<span class="k">private</span> <span class="nx">string</span> <span class="nv">$title</span><span class="p">,</span>
<span class="k">private</span> <span class="nx">string</span> <span class="nv">$author</span><span class="p">;</span>
<span class="p">)</span>
<span class="p">{}</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">getTitle</span><span class="p">()</span><span class="o">:</span> <span class="nx">string</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="na">title</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">getAuthor</span><span class="p">()</span><span class="o">:</span> <span class="nx">string</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="na">author</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nv">$book</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Book</span><span class="p">(</span><span class="s1">'A Game of Thrones'</span><span class="p">,</span> <span class="s1">'George R.R. Martin'</span><span class="p">);</span>
<span class="nv">$book</span><span class="o">-></span><span class="na">getAuthor</span><span class="p">();</span> <span class="c1">// "George R.R. Martin"</span></code></pre>
<p>Puis ça, en PHP 8.1 :</p>
<pre><code class="php"><span class="k">class</span> <span class="nc">Book</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Les propriétés étant déclarées 'readonly', elles peuvent être déclarées 'public' sans risque.</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
<span class="k">public</span> <span class="nx">readonly</span> <span class="nx">string</span> <span class="nv">$title</span><span class="p">,</span>
<span class="k">public</span> <span class="nx">readonly</span> <span class="nx">string</span> <span class="nv">$author</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">{}</span>
<span class="c1">// Suppression des getters, devenus inutiles</span>
<span class="p">}</span>
<span class="nv">$book</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Book</span><span class="p">(</span><span class="s1">'A Game of Thrones'</span><span class="p">,</span> <span class="s1">'George R.R. Martin'</span><span class="p">);</span>
<span class="c1">// On peut désormais appeler directement la propriété, sans craindre de la modifier</span>
<span class="k">echo</span> <span class="nv">$book</span><span class="o">-></span><span class="na">author</span><span class="p">;</span> <span class="c1">// "George R.R. Martin"</span>
<span class="nv">$book</span><span class="o">-></span><span class="na">author</span> <span class="o">=</span> <span class="s1">'J.R.R. Tolkien'</span><span class="p">;</span> <span class="c1">// Error: Cannot modify readonly property Book::$author</span></code></pre>
<p>Pour arriver finalement à ceci, en PHP 8.2 :</p>
<pre><code class="php"><span class="c1">// utilisation du mot-clé 'readonly' sur la classe directement</span>
<span class="nx">readonly</span> <span class="k">class</span> <span class="nc">Book</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * La classe étant déclarée 'readonly', les propriétés peuvent être déclarées 'public' sans risque,</span>
<span class="sd"> * sans pour autant avoir encore besoin de les déclarer 'readonly' une à une.</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
<span class="k">public</span> <span class="nx">string</span> <span class="nv">$title</span><span class="p">,</span>
<span class="k">public</span> <span class="nx">string</span> <span class="nv">$author</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">{}</span>
<span class="c1">// Suppression des getters, devenus inutiles</span>
<span class="p">}</span>
<span class="nv">$book</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Book</span><span class="p">(</span><span class="s1">'A Game of Thrones'</span><span class="p">,</span> <span class="s1">'George R.R. Martin'</span><span class="p">);</span>
<span class="k">echo</span> <span class="nv">$book</span><span class="o">-></span><span class="na">author</span><span class="p">;</span> <span class="c1">// "George R.R. Martin"</span>
<span class="nv">$book</span><span class="o">-></span><span class="na">author</span> <span class="o">=</span> <span class="s1">'J.R.R. Tolkien'</span><span class="p">;</span> <span class="c1">// Error: Cannot modify readonly property Book::$author</span>
<span class="nv">$book</span><span class="o">-></span><span class="na">genre</span> <span class="o">=</span> <span class="s1">'Epic fantasy'</span><span class="p">;</span> <span class="c1">// Uncaught Error: Cannot create dynamic property Book::$genre</span></code></pre>
<p><strong>ATTENTION !</strong><br>
Un objet <em>readonly</em> n'est pas forcément immuable ! Si un objet A a une référence vers un objet B (composition), le fait que cette référence soit <em>readonly</em> garantit uniquement que A ne pointera jamais vers C à la place de B. Mais cela n'empêche pas pour autant les propriétés de B de pouvoir changer dans le temps (si elles ne sont pas elles-mêmes <em>readonly</em>, évidemment).</p>
<h4 id="toc-dépréciation-des-propriétés-dynamiques-deprecate-dynamic-properties--rfc">Dépréciation des propriétés dynamiques (<em>Deprecate dynamic properties</em> : <a href="https://wiki.php.net/rfc/deprecate_dynamic_properties">RFC</a>)</h4>
<p>Dans la continuité du refus d'ajouter une propriété dynamique sur une classe <em>readonly</em>, une Deprecated est désormais levée lorsqu'on fait appel à une propriété inexistante au sein de la classe/de l'objet.<br>
Ex :</p>
<pre><code class="php"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
<span class="k">public</span> <span class="nv">$name</span><span class="p">;</span>
<span class="p">}</span>
<span class="nv">$user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">;</span>
<span class="c1">// Assigns declared property User::$name</span>
<span class="nv">$user</span><span class="o">-></span><span class="na">name</span> <span class="o">=</span> <span class="s2">"foo"</span><span class="p">;</span>
<span class="c1">// Oops, a typo:</span>
<span class="nv">$user</span><span class="o">-></span><span class="na">nane</span> <span class="o">=</span> <span class="s2">"foo"</span><span class="p">;</span>
<span class="c1">// PHP <= 8.1: Silently creates dynamic $user->nane property.</span>
<span class="c1">// PHP 8.2: "Deprecated: Creation of dynamic property User::$nane is deprecated"</span>
<span class="c1">// PHP 9.0: Throws Error exception.</span></code></pre>
<p>Néanmoins, les cas où il faut pouvoir continuer à créer des propriétés dynamiques ont été pris en compte. Pour cela, 3 solutions :</p>
<h5 id="toc-ajouter-les-méthodes-magiques-__get-etou-__set-">Ajouter les méthodes magiques <code>__get</code> et/ou <code>__set</code> :</h5>
<pre><code class="php"><span class="k">class</span> <span class="nc">Post</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">array</span> <span class="nv">$properties</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">public</span> <span class="k">function</span> <span class="fm">__set</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$name</span><span class="p">,</span> <span class="nx">mixed</span> <span class="nv">$value</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span>
<span class="p">{</span>
<span class="nv">$this</span><span class="o">-></span><span class="na">properties</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$value</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// …</span>
<span class="p">}</span>
<span class="c1">// …</span>
<span class="nv">$post</span><span class="o">-></span><span class="na">name</span> <span class="o">=</span> <span class="s1">'Name'</span><span class="p">;</span> <span class="c1">// Pas de message de dépréciation</span></code></pre>
<h5 id="toc-utiliser-la-classe-stdclass-de-php-ou-en-hériter-">Utiliser la classe <code>stdClass</code> de PHP (ou en hériter) :</h5>
<pre><code class="php"><span class="cm">/*</span>
<span class="cm"> * Ne faites pas ça : ça marche mais c'est une solution fortement déconseillée !</span>
<span class="cm"> */</span>
<span class="k">class</span> <span class="nc">Post</span> <span class="k">extends</span> <span class="k">stdClass</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="nv">$post</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Post</span><span class="p">();</span>
<span class="nv">$post</span><span class="o">-></span><span class="na">name</span> <span class="o">=</span> <span class="s1">'Name'</span><span class="p">;</span> <span class="c1">// Pas de message de dépréciation</span></code></pre>
<h5 id="toc-ajouter-lattribut-allowdynamicproperties-à-la-classe-">Ajouter l'attribut <code>#[AllowDynamicProperties]</code> à la classe :</h5>
<pre><code class="php"><span class="cm">/*</span>
<span class="cm"> * Solution la plus recommandée pour gérer le code legacy, car l'impact est nul.</span>
<span class="cm"> */</span>
<span class="c1">#[AllowDynamicProperties]</span>
<span class="k">class</span> <span class="nc">Post</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="nv">$post</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Post</span><span class="p">();</span>
<span class="nv">$post</span><span class="o">-></span><span class="na">name</span> <span class="o">=</span> <span class="s1">'Name'</span><span class="p">;</span> <span class="c1">// Pas de message de dépréciation</span></code></pre>
<p>À terme (PHP 9), le message passera de <code>Deprecated</code> à la levée d'une exception de type <code>Error</code>.</p>
<h4 id="toc-nouveaux-types-de-retour--null-true-et-false-allow-null-and-false-as-stand-alone-types--rfc">Nouveaux types de retour : <code>null</code>, <code>true</code>, et <code>false</code> (<em>Allow null and false as stand-alone types</em> : <a href="https://wiki.php.net/rfc/null-false-standalone-types">RFC</a>)</h4>
<p>Il est désormais possible d'indiquer que le type de retour d'une fonction/méthode est <code>null</code>, <code>true</code>, ou <code>false</code>. C'est notamment utile pour les fonctions de PHP qui renvoient une valeur, ou <code>false</code> si un problème a été rencontré.<br>
Par exemple :</p>
<pre><code class="php"><span class="cm">/*</span>
<span class="cm"> * Trouve la première occurrence de $needle dans $haystack</span>
<span class="cm"> * @return la portion de la chaîne, ou false si needle n'est pas trouvé.</span>
<span class="cm"> */</span>
<span class="nb">strstr</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$haystack</span><span class="p">,</span> <span class="nx">string</span> <span class="nv">$needle</span><span class="p">,</span> <span class="nx">bool</span> <span class="nv">$before_needle</span> <span class="o">=</span> <span class="k">false</span><span class="p">)</span><span class="o">:</span> <span class="nx">string</span><span class="o">|</span><span class="k">false</span></code></pre>
<h4 id="toc-masquage-des-données-sensibles-dans-les-logs-redacting-parameters-in-back-traces--rfc">Masquage des données sensibles dans les logs (<em>Redacting parameters in back traces</em> : <a href="https://wiki.php.net/rfc/redact_parameters_in_back_traces">RFC</a>)</h4>
<p>Il est désormais possible d'indiquer que la valeur d'un paramètre ne doit jamais se retrouver dans des traces d'erreurs. C'est évidemment très utile pour éviter qu'un mot de passe ne finisse en clair dans un fichier de logs.<br>
Pour cela, il faut ajouter un attribut à la variable concernée, comme ceci :</p>
<pre><code class="php"><span class="k">function</span> <span class="nf">test</span><span class="p">(</span>
<span class="nv">$foo</span> <span class="o">=</span> <span class="k">null</span><span class="p">,</span>
<span class="c1">#[\SensitiveParameter] $bar = null,</span>
<span class="nv">$baz</span> <span class="o">=</span> <span class="k">null</span>
<span class="p">)</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nx">\Exception</span><span class="p">(</span><span class="s1">'Error'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">test</span><span class="p">(</span><span class="nx">baz</span><span class="o">:</span> <span class="s1">'baz'</span><span class="p">);</span>
<span class="cm">/*</span>
<span class="cm">Fatal error: Uncaught Exception: Error in test.php:8</span>
<span class="cm">Stack trace:</span>
<span class="cm">#0 test.php(11): test(NULL, Object(SensitiveParameterValue), 'baz')</span>
<span class="cm">#1 {main}</span>
<span class="cm"> thrown in test.php on line 8</span>
<span class="cm">*/</span></code></pre>
<p>Comme on le voit dans la trace d'erreur, la valeur du paramètre en entrée <code>$bar</code> a été remplacée par <code>Object(SensitiveParameterValue)</code>.</p>
<h4 id="toc-constantes-dans-les-traits-rfc">Constantes dans les Traits (<a href="https://wiki.php.net/rfc/constants_in_traits">RFC</a>)</h4>
<p>Il est désormais possible d'inclure des constantes au sein d'un Trait.<br>
La syntaxe suivante est donc désormais valide :</p>
<pre><code class="php"><span class="k">trait</span> <span class="nx">T</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">const</span> <span class="no">CONSTANT</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">doSomething</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
<span class="c1">// Fatal Error</span>
<span class="k">echo</span> <span class="nx">T</span><span class="o">::</span><span class="na">CONSTANT</span><span class="p">;</span>
<span class="c1">// OK</span>
<span class="k">echo</span> <span class="nx">self</span><span class="o">::</span><span class="na">CONSTANT</span><span class="p">;</span>
<span class="k">echo</span> <span class="k">static</span><span class="o">::</span><span class="na">CONSTANT</span><span class="p">;</span>
<span class="k">echo</span> <span class="nv">$this</span><span class="o">::</span><span class="na">CONSTANT</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<h4 id="toc-retrait-du-support-de-libmysql-remove-support-for-libmysql-from-mysqli--rfc">Retrait du support de <em>libmysql</em> (<em>Remove support for libmysql from mysqli</em> : <a href="https://wiki.php.net/rfc/mysqli_support_for_libmysql">RFC</a>)</h4>
<p>Le support de la bibliothèque <em>libmysql</em> a été retiré de PHP, pour ne conserver désormais que le support de <em>mysqlnd</em>. Si vous utilisez PHP avec MySQL (que ce soit avec l'extension PHP <em>mysqli</em> ou avec <em>PDO_mysql</em>), vous ne devriez même pas vous en rendre compte : <em>mysqlnd</em> est utilisée par défaut depuis… PHP 5.4 (sortie en 2012).</p>
<h4 id="toc-liste-complète-des-rfc">Liste complète des RFC</h4>
<p>La liste complète des apports de cette nouvelle version est <a href="https://wiki.php.net/rfc#php_82">consultable ici</a>.</p>
<h3 id="toc-php-83">PHP 8.3</h3>
<p>La prochaine version sera la 8.3, <a href="https://wiki.php.net/todo/php83">prévue (pour l'instant) pour novembre 2023</a>.<br>
La liste des nouvelles fonctionnalités n'est évidemment pas encore arrêtée, mais on peut d'ores et déjà <a href="https://wiki.php.net/rfc#php_83">la retrouver ici</a>.</p>
</div><div><a href="https://linuxfr.org/news/php-sort-en-version-8-2.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/127613/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/php-sort-en-version-8-2#comments">ouvrir dans le navigateur</a>
</p>
windu.2bEllendhelvmagninbobble bubblePierre JarillonYves BourguignonNils Ratusznikpalm123https://linuxfr.org/nodes/127613/comments.atomtag:linuxfr.org,2005:News/408332022-02-11T19:56:51+01:002022-02-11T19:56:51+01:00Laravel 9 est sortiLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Laravel, un framework PHP déjà évoqué dans ces colonnes, est sorti en version 9.<br>
Découvrons ensemble les nouveautés !</p>
</div><ul><li>lien nᵒ 1 : <a title="https://laravel.com/" hreflang="en" href="https://linuxfr.org/redirect/109745">Laravel : site officiel</a></li><li>lien nᵒ 2 : <a title="https://laravel-news.com/laravel-9-released" hreflang="en" href="https://linuxfr.org/redirect/109746">Laravel 9 : annonce officielle</a></li><li>lien nᵒ 3 : <a title="https://laravel.com/docs/9.x/releases" hreflang="en" href="https://linuxfr.org/redirect/109928">Release Notes</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<ul>
<li>
<a href="#toc-quoi-de-neuf">Quoi de neuf ?</a><ul>
<li><a href="#toc-nouvelle-version-lts">Nouvelle version LTS</a></li>
<li><a href="#toc-version-de-php">Version de PHP</a></li>
<li><a href="#toc-migration-de-swiftmailer-%C3%A0-symfony-mailer">Migration de SwiftMailer à Symfony Mailer</a></li>
<li><a href="#toc-anonymous-stub-migrations-par-d%C3%A9faut"><em>Anonymous Stub Migrations</em> par défaut</a></li>
<li><a href="#toc-nouveau-design-pour-la-commande-routeslist">Nouveau design pour la commande <code>routes:list</code></a></li>
<li><a href="#toc-flysystem-3x">Flysystem 3.x</a></li>
<li><a href="#toc-nouvelle-fa%C3%A7on-de-d%C3%A9finir-des-accesseursmutateurs-aux-mod%C3%A8les">Nouvelle façon de définir des accesseurs/mutateurs aux modèles</a></li>
<li><a href="#toc-support-des-enums">Support des Enums</a></li>
<li><a href="#toc-affichage-de-la-couverture-de-code">Affichage de la couverture de code</a></li>
<li><a href="#toc-et-bien-dautres-choses-encore">Et bien d'autres choses encore</a></li>
</ul>
</li>
<li><a href="#toc-laracon-2022">Laracon 2022</a></li>
<li><a href="#toc-laravel-nova-4">Laravel Nova 4</a></li>
</ul>
</li>
</ul>
<p>Un an et demi après Laravel 8 et avec six mois de retard sur la date initialement prévue, dû aux changements <a href="//linuxfr.org/news/laravel-8-est-sorti#toc-calendrier-de-sortie">à plusieurs reprises du calendrier des sorties</a>, Laravel 9 est finalement sorti ce 08 février 2022.</p>
<p>Pour marquer le coup, l'équipe de Laravel en a profité pour rafraîchir le site officiel.</p>
<h3 id="toc-quoi-de-neuf">Quoi de neuf ?</h3>
<h4 id="toc-nouvelle-version-lts">Nouvelle version LTS</h4>
<p>Première info qui a son importance : il s'agit d'une version LTS, en remplacement de la précédente (Laravel 6, qui datait de septembre 2019). Cette sortie entraîne aussi l'arrêt des correctifs d'erreurs, mais pas encore des correctifs de sécurité, pour cette dernière (<a href="https://laravelversions.com/fr">cf. le calendrier officiel des versions, pour plus de détails</a>).</p>
<h4 id="toc-version-de-php">Version de PHP</h4>
<p>Le premier impact notable est la nécessité d'utiliser PHP 8.0 <em>a minima</em>. Ceci est notamment dû au fait que Laravel 9 repose sur des briques de Symfony 6.0 (<a href="https://symfony.com/releases/6.0">sortie en novembre dernier</a>) et qui nécessite, vous l'aurez compris, PHP 8.<br>
À noter cependant qu'il faudra utiliser PHP 8.1, si vous comptez avoir recours aux <code>enums</code> (cf. le chapitre <a href="//linuxfr.org/redaction/news/laravel-9-est-sorti#toc-support-des-enums">Support des enums</a>)</p>
<h4 id="toc-migration-de-swiftmailer-à-symfony-mailer">Migration de SwiftMailer à Symfony Mailer</h4>
<p>Toujours du fait de cette dépendance à (une partie de) Symfony 6.0, il faut désormais passer de SwiftMailer à Symfony Mailer, le premier ayant été déprécié par les développeurs de Symfony.<br>
C'est sans doute là le point de mise à jour le plus délicat de cette nouvelle version mais, comme à chaque fois, <a href="https://laravel.com/docs/9.x/upgrade">la documentation de Laravel explique point par point ce qu'il faut modifier/vérifier lors de la mise à jour.</a></p>
<p>Pour ceux qui veulent en savoir plus sur les raisons de ce changement par Symfony, vous pouvez vous reporter à <a href="https://symfony.com/blog/the-end-of-swiftmailer">ce billet de blog</a>.</p>
<h4 id="toc-anonymous-stub-migrations-par-défaut">
<em>Anonymous Stub Migrations</em> par défaut</h4>
<p><a href="//linuxfr.org/news/laravel-8-est-sorti#toc-anonymous-migrations">On en parlait déjà dans une précédente dépêche</a> : il s'agit ici d'une nouveauté apparue dans <a href="https://laravel-news.com/laravel-anonymous-migrations">Laravel 8.37</a>, consistant à rendre anonyme les scripts de migration.<br>
Pour rappel, les scripts de migration sont un mécanisme de migration de la structure de la base de données, reposant sur des scripts horodatés.<br>
Jusqu'à présent, chaque script était une classe (héritant de la classe <code>Migration</code> fournie par Laravel) contenant les modifications à effectuer.<br>
Problème : cette classe doit avoir un nom unique (car tous les scripts de migration sont dans le même <em>namespace</em>). Ce qui n'est pas toujours pratique, quand le nombre de scripts est de plusieurs centaines.<br>
Le fait de rendre ces scripts anonymes résout le problème du conflit entre classes de même nom.</p>
<p>On passe donc de ceci :</p>
<pre><code class="php"><span class="k">use</span> <span class="nx">Illuminate\Database\Migrations\Migration</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Database\Schema\Blueprint</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Support\Facades\Schema</span><span class="p">;</span>
<span class="k">class</span> <span class="nc">MaClasseAvecUnNomUniquePourNePasEntrerEnConflitAvecLes300AutresClassesDeMigration</span> <span class="k">extends</span> <span class="nx">Migration</span> <span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Run the migrations.</span>
<span class="sd"> *</span>
<span class="sd"> * @return void</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">up</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">Schema</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s1">'people'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">string</span><span class="p">(</span><span class="s1">'first_name'</span><span class="p">)</span><span class="o">-></span><span class="na">nullable</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">};</span></code></pre>
<p>à cela :</p>
<pre><code class="php"><span class="k">use</span> <span class="nx">Illuminate\Database\Migrations\Migration</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Database\Schema\Blueprint</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Support\Facades\Schema</span><span class="p">;</span>
<span class="k">return</span> <span class="k">new</span> <span class="k">class</span> <span class="nc">extends</span> <span class="nx">Migration</span> <span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Run the migrations.</span>
<span class="sd"> *</span>
<span class="sd"> * @return void</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">up</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">Schema</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s1">'people'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">string</span><span class="p">(</span><span class="s1">'first_name'</span><span class="p">)</span><span class="o">-></span><span class="na">nullable</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">};</span></code></pre>
<p>Laravel 8.37 a permis d'écrire des scripts avec des classes anonymes, Laravel 9 rend ce comportement par défaut.</p>
<h4 id="toc-nouveau-design-pour-la-commande-routeslist">Nouveau design pour la commande <code>routes:list</code>
</h4>
<p>La commande <code>php artisan routes:list</code>, qui sert à lister toutes les URL auquel votre projet peut répondre (en indiquant pour chacune le verbe HTTP concerné, l'alias éventuel, le contrôleur qui sera exécuté, les <em>middlewares</em> invoqués, …) se refait une fraîcheur !</p>
<p>Désormais, la sortie de cette commande passe de ceci : <img src="//img.linuxfr.org/img/68747470733a2f2f692e737461636b2e696d6775722e636f6d2f62365a44712e706e67/b6ZDq.png" alt="Old design for routes:list" title="Source : https://i.stack.imgur.com/b6ZDq.png"></p>
<p>à cela : <img src="//img.linuxfr.org/img/68747470733a2f2f6c61726176656c6e6577732e73332e616d617a6f6e6177732e636f6d2f696d616765732f4649585755712d5873416b477a62632e6a706567/FIXWUq-XsAkGzbc.jpeg" alt="New Design for routes:list" title="Source : https://laravelnews.s3.amazonaws.com/images/FIXWUq-XsAkGzbc.jpeg"></p>
<p>Coloration syntaxique des verbes HTTP et des paramètres dans l'URL, affichage désormais <em>responsive</em> (finis les tableaux plus grands que la largeur de la console, et qui débordent sur la ligne du dessous, rendant l'affichage difficilement lisible), informations affichées dépendant de paramètres passés à la commande (ex : les <em>middlewares</em> n'apparaissent plus, par défaut), …</p>
<h4 id="toc-flysystem-3x">Flysystem 3.x</h4>
<p>Flystystem est une couche d'abstraction pour accéder à des systèmes de fichiers, locaux (sur disque ou en mémoire) ou distants (FTP, AWS S3, …).<br>
C'est notamment par lui que transitent les uploads depuis un formulaire, les envois/réceptions vers/depuis des sources extérieures (cloud, …).</p>
<h4 id="toc-nouvelle-façon-de-définir-des-accesseursmutateurs-aux-modèles">Nouvelle façon de définir des accesseurs/mutateurs aux modèles</h4>
<p>Par défaut dans Laravel, les propriétés des classes-modèles correspondent aux champs de la table à laquelle est liée ladite classe. Mais il est bien évidemment possible d'ajouter de nouvelles propriétés, sans que celles-ci ne soient stockées en base.</p>
<p>Ex :</p>
<pre><code class="php"><span class="k">public</span> <span class="k">function</span> <span class="nf">getFullnameAttribute</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="na">firstname</span> <span class="o">.</span> <span class="s1">' '</span> <span class="o">.</span> <span class="nv">$this</span><span class="o">-></span><span class="na">lastname</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">setFullameAttribute</span><span class="p">(</span><span class="nv">$fullname</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">list</span><span class="p">(</span><span class="nv">$firstname</span><span class="p">,</span> <span class="nv">$lastname</span><span class="p">)</span> <span class="o">=</span> <span class="nb">explode</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="nv">$fullname</span><span class="p">);</span>
<span class="nv">$this</span><span class="o">-></span><span class="na">attributes</span><span class="p">[</span><span class="s1">'firstname'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$firstname</span><span class="p">;</span>
<span class="nv">$this</span><span class="o">-></span><span class="na">attributes</span><span class="p">[</span><span class="s1">'lastname'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$lastname</span><span class="p">;</span>
<span class="p">}</span></code></pre>
<p>Pour des raisons évidentes "d'auto-détection" des propriétés ajoutées, les noms des méthodes suivent une syntaxe imposée par Laravel : <code>getXxxAttribute</code> et <code>setXxxAttribute</code>.</p>
<p>Laravel 9 propose désormais une nouvelle approche (tout en gardant l'ancienne toujours utilisable) :</p>
<pre><code class="php"><span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Casts\Attribute</span><span class="p">;</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">fullname</span><span class="p">()</span><span class="o">:</span> <span class="nx">Attribute</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nx">Attribute</span><span class="p">(</span>
<span class="nx">get</span><span class="o">:</span> <span class="nx">fn</span> <span class="p">()</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="na">firstname</span> <span class="o">.</span> <span class="s1">' '</span> <span class="o">.</span> <span class="nv">$this</span><span class="o">-></span><span class="na">lastname</span><span class="p">,</span>
<span class="nx">set</span><span class="o">:</span> <span class="nx">fn</span> <span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="o">=></span> <span class="p">[</span>
<span class="s1">'firstname'</span> <span class="o">=></span> <span class="nb">explode</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="nv">$fullname</span><span class="p">)[</span><span class="mi">0</span><span class="p">],</span>
<span class="s1">'lastname'</span> <span class="o">=></span> <span class="nb">explode</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="nv">$fullname</span><span class="p">)[</span><span class="mi">1</span><span class="p">],</span>
<span class="p">],</span>
<span class="p">);</span>
<span class="p">}</span></code></pre>
<p>À noter l'usage des paramètres nommés <code>get:</code> et <code>set:</code> qui sont une nouveauté de PHP 8.</p>
<h4 id="toc-support-des-enums">Support des Enums</h4>
<p>PHP 8.1 ayant apporté le support des <code>enums</code>, il n'a pas fallu longtemps pour que Laravel en fasse autant !<br>
Il est donc désormais possible de caster une propriété d'un modèle pour qu'elle n'accepte que les valeurs supportées par un enum donné :</p>
<pre><code class="php"><span class="k">use</span> <span class="nx">App\Enums\ServerStatus</span><span class="p">;</span>
<span class="sd">/**</span>
<span class="sd"> * The attributes that should be cast.</span>
<span class="sd"> *</span>
<span class="sd"> * @var array</span>
<span class="sd"> */</span>
<span class="k">protected</span> <span class="nv">$casts</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'status'</span> <span class="o">=></span> <span class="nx">ServerStatus</span><span class="o">::</span><span class="na">class</span><span class="p">,</span>
<span class="p">];</span></code></pre>
<p>Ici, <code>ServerStatus</code> est un <code>enum</code> contenant un certain nombre de valeurs. La propriété <code>status</code> ne peut désormais accepter autre chose que ces valeurs.</p>
<p>Autre possibilité : avoir une URL qui n'accepte que les valeurs d'un <code>enum</code> donné :</p>
<pre><code class="php"><span class="nx">enum</span> <span class="nx">Category</span><span class="o">:</span> <span class="nx">string</span>
<span class="p">{</span>
<span class="k">case</span> <span class="nx">Fruits</span> <span class="o">=</span> <span class="s1">'fruits'</span><span class="p">;</span>
<span class="k">case</span> <span class="nx">People</span> <span class="o">=</span> <span class="s1">'people'</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">Route</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">'/categories/{category}'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Category</span> <span class="nv">$category</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nv">$category</span><span class="o">-></span><span class="na">value</span><span class="p">;</span>
<span class="p">});</span></code></pre>
<p>Toute autre valeur que "fruits" ou "people" provoquera une erreur 404.</p>
<h4 id="toc-affichage-de-la-couverture-de-code">Affichage de la couverture de code</h4>
<p>La commmande <code>php artisan test</code>, qui sert à lancer les tests unitaires/features d'un projet Laravel, a reçu un nouveau paramètre optionnel <code>--coverage</code> permettant d'afficher la couverture de code, sous forme de pourcentage.<br>
Il est même possible de définir un pourcentage minimum à atteindre, en-deçà duquel l'exécution des tests sera considérée comme ayant échoué.<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f757365722d696d616765732e67697468756275736572636f6e74656e742e636f6d2f353435373233362f3134393938393835332d61323961373632392d326266612d346266332d626266372d6364626133333965633135372e706e67/149989853-a29a7629-2bfa-4bf3-bbf7-cdba339ec157.png" alt="Test Coverage Using Artisan test Command" title="Source : https://user-images.githubusercontent.com/5457236/149989853-a29a7629-2bfa-4bf3-bbf7-cdba339ec157.png"></p>
<h4 id="toc-et-bien-dautres-choses-encore">Et bien d'autres choses encore</h4>
<p>La liste complète des améliorations est à retrouver sur la page "Release notes" indiquée en début de dépêche.</p>
<h3 id="toc-laracon-2022">Laracon 2022</h3>
<p><a href="https://laracon.net/">La conférence annuelle sur Laravel commence ce 9 février</a>. Points particuliers : elle est intégralement en ligne (pour des raisons évidentes d'actualité sanitaire), mais surtout : les places sont gratuites ! Merci pour cela aux sponsors de Laravel qui l'ont financée intégralement.</p>
<h3 id="toc-laravel-nova-4">Laravel Nova 4</h3>
<p>Nova est un package développé par l'équipe de Laravel permettant de créer et gérer un dashboard admin. Sortie en version 4 est prévu pour le mois prochain.<br>
À noter que, contrairement à Laravel, Nova est payant et propriétaire.</p>
</div><div><a href="https://linuxfr.org/news/laravel-9-est-sorti.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/126499/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/laravel-9-est-sorti#comments">ouvrir dans le navigateur</a>
</p>
windu.2bvmagninXavier TeyssierBenoît Sibaudhttps://linuxfr.org/nodes/126499/comments.atomtag:linuxfr.org,2005:News/406262021-12-01T21:07:35+01:002021-12-01T21:07:35+01:00PEST soit des tests unitairesLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>PEST est un nouveau framework en PHP, permettant de rédiger des tests unitaires.<br>
Basé sur le célèbre (pour ceux qui développent en PHP) PHUnit, PEST a pour lui d’être plus élégant et simple à utiliser, et apporte notamment une plus grande fluidité dans l’écriture des tests unitaires.<br>
Il a été créé par Nuno Maduro, membre de la core team de <a href="//linuxfr.org/tags/laravel/public">Laravel</a>, en <a href="https://github.com/sponsorware/docs">sponsorware</a>. Depuis le printemps 2020, il est publié sous <a href="https://github.com/pestphp/pest/blob/master/LICENSE.md">licence MIT</a>. Il ne possède aucune filiation avec Laravel, on peut tout à fait s’en servir sans.<br>
Petit tour d’horizon !</p>
<p><em>(tous les exemples de code de cette dépêche proviennent de la documentation officielle de PEST)</em></p>
</div><ul><li>lien nᵒ 1 : <a title="https://pestphp.com/" hreflang="en" href="https://linuxfr.org/redirect/109469">Site officiel</a></li><li>lien nᵒ 2 : <a title="https://github.com/pestphp/pest" hreflang="en" href="https://linuxfr.org/redirect/109470">Dépôt officiel (github)</a></li><li>lien nᵒ 3 : <a title="https://github.com/pestphp/pest/blob/master/LICENSE.md" hreflang="en" href="https://linuxfr.org/redirect/109471">Licence MIT</a></li><li>lien nᵒ 4 : <a title="https://twitter.com/enunomaduro" hreflang="en" href="https://linuxfr.org/redirect/109472">Nuno Maduro créateur du projet</a></li><li>lien nᵒ 5 : <a title="https://www.youtube.com/playlist?list=PLb4Xm6fDKP2o3F1uP9DM7MfTO7P0W_mmB" hreflang="en" href="https://linuxfr.org/redirect/109481">PEST in practice (série de vidéos)</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-premiers-tests">Premiers tests</a></li>
<li>
<a href="#toc-expectations">Expectations</a><ul>
<li><a href="#toc-higher-order-tests">Higher Order Tests</a></li>
<li><a href="#toc-custom-expectations">Custom expectations</a></li>
</ul>
</li>
</ul>
<h2 id="toc-premiers-tests">Premiers tests</h2>
<p>Pour commencer, voyons à quoi ressemble un test basique, avec PEST.</p>
<pre><code class="php"><span class="o"><?</span><span class="nx">php</span>
<span class="nx">test</span><span class="p">(</span><span class="s1">'asserts true is true'</span><span class="p">,</span> <span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-></span><span class="na">assertTrue</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>
<span class="nx">expect</span><span class="p">(</span><span class="k">true</span><span class="p">)</span><span class="o">-></span><span class="na">toBeTrue</span><span class="p">();</span>
<span class="p">});</span></code></pre>
<p>Contrairement à PHPUnit, qui regroupe chaque test dans une méthode de classe, chaque test dans PEST consiste à appeler la fonction <code>test</code> en lui passant 2 paramètres : la description du test (texte libre) et une fonction anonyme (<em>Closure</em>) contenant le code du test proprement dit. Le tout dans un simple fichier PHP (pas de classe, pas de <em>namespace</em>, rien de tout ça n’est nécessaire).</p>
<p>Comme PEST repose sur PHPUnit, on peut utiliser les méthodes <code>assertXXX</code> déjà existantes et connues de tous, à travers <code>$this</code> (qui est un objet de type <code>PHPUnit\Framework\TestCase</code>, ce qui montre que PEST est une surcouche à PHPUnit, avec du sucre syntaxique dedans).<br>
Mais on peut aussi utiliser les nouvelles méthodes <code>toBeXXX</code> mises à notre disposition par PEST, et dont on parlera plus loin.</p>
<p>Si le test ci-dessus réussit, PEST affichera :</p>
<pre><code class="php"><span class="nx">✓</span> <span class="nx">asserts</span> <span class="k">true</span> <span class="nx">is</span> <span class="k">true</span></code></pre>
<p>À noter que la fonction <code>test</code> a une petite sœur : <code>it</code> (même signature). La seule différence est que cette dernière affichera ceci, si le test réussit :</p>
<pre><code class="php"><span class="nx">✓</span> <span class="nx">it</span> <span class="nx">asserts</span> <span class="k">true</span> <span class="nx">is</span> <span class="k">true</span></code></pre>
<h2 id="toc-expectations">Expectations</h2>
<p>Bien que PEST puisse s’utiliser comme PHPUnit (ce qui permet doucement de basculer d’un framework à l’autre), on peut bien évidemment aller plus loin, en tirant profit de sa spécificité : les expectations.<br>
Concrètement, cela consiste en une méthode <code>expect</code>, à qui on passe la variable à tester, et toute une série de méthodes fluides (<em>fluent interfaces</em>) qui vont chacune vérifier que la variable respecte telle ou telle règle.</p>
<p>Reprenons l’exemple du début :</p>
<pre><code class="php"><span class="o"><?</span><span class="nx">php</span>
<span class="nx">test</span><span class="p">(</span><span class="s1">'expect true to be true'</span><span class="p">,</span> <span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// assertion</span>
<span class="nv">$this</span><span class="o">-></span><span class="na">assertTrue</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>
<span class="c1">// expectation</span>
<span class="nx">expect</span><span class="p">(</span><span class="k">true</span><span class="p">)</span><span class="o">-></span><span class="na">toBe</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>
<span class="p">});</span></code></pre>
<p>On voit que la méthode <code>toBe</code> vérifie que la valeur <code>true</code> (passée en paramètre de la fonction <code>expect</code>) est bien égale à <code>true</code>.</p>
<p>Voyons maintenant un cas un peu plus poussé (et plus proche de ce qu’on peut être amené à écrire, dans la vraie vie) :</p>
<pre><code class="php"><span class="nx">expect</span><span class="p">(</span><span class="nv">$user</span><span class="p">)</span><span class="o">-></span><span class="na">toBeInstanceOf</span><span class="p">(</span><span class="nx">User</span><span class="o">::</span><span class="na">class</span><span class="p">);</span></code></pre>
<p>Ici, la méthode <code>toBeInstanceOf</code> vérifie que la variable <code>$user</code> est bien de type <code>User</code>.</p>
<p>On ne va pas lister ici la palanquée de méthodes <code>toBeXXX</code> qui existent : <a href="https://pestphp.com/docs/expectations#available-expectations">la documentation est là pour ça</a>, avec un exemple pour chacune d’elles.</p>
<p>Rappelons toutefois qu’il est tout à fait possible de chaîner les appels de méthodes, pour tester plusieurs choses sur une même valeur.</p>
<p>Exemple :</p>
<pre><code class="php"><span class="nx">expect</span><span class="p">(</span><span class="s1">'{"name":"Nuno","credit":1000.00}'</span><span class="p">)</span>
<span class="o">-></span><span class="na">json</span><span class="p">()</span>
<span class="o">-></span><span class="na">toHaveCount</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span></code></pre>
<h3 id="toc-higher-order-tests">Higher Order Tests</h3>
<p>Avec le chaînage des méthodes vient aussi une autre possibilité de PEST : pouvoir tester les valeurs au sein de la variable passée à la fonction <code>expect</code>. Ainsi, plutôt que de faire plusieurs appels à <code>expect</code> (ce qui reste tout à fait possible), comme suit :</p>
<pre><code class="php"><span class="nx">expect</span><span class="p">(</span><span class="nv">$user</span><span class="o">-></span><span class="na">first_name</span><span class="p">)</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Nuno'</span><span class="p">);</span>
<span class="nx">expect</span><span class="p">(</span><span class="nv">$user</span><span class="o">-></span><span class="na">last_name</span><span class="p">)</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Maduro'</span><span class="p">);</span>
<span class="nx">expect</span><span class="p">(</span><span class="nv">$user</span><span class="o">-></span><span class="na">withTitle</span><span class="p">(</span><span class="s1">'Mr'</span><span class="p">))</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Mr Nuno Maduro'</span><span class="p">);</span></code></pre>
<p>On peut tout à fait faire ceci :</p>
<pre><code class="php"><span class="nx">expect</span><span class="p">(</span><span class="nv">$user</span><span class="p">)</span>
<span class="o">-></span><span class="na">first_name</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Nuno'</span><span class="p">)</span>
<span class="o">-></span><span class="na">last_name</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Maduro'</span><span class="p">)</span>
<span class="o">-></span><span class="na">withTitle</span><span class="p">(</span><span class="s1">'Mr'</span><span class="p">)</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Mr Nuno Maduro'</span><span class="p">);</span></code></pre>
<p>Et là, on commence à toucher à ce qui rend PEST si attrayant : le chaînage est sans limite ! On peut tester toutes les données contenues à l’intérieur de la valeur passée en paramètre à <code>expect</code>, que ce soit les propriétés d’un objet, comme on vient de le voir, ou les différents éléments d’un tableau, comme ci-dessous :</p>
<pre><code class="php"><span class="nx">expect</span><span class="p">([</span><span class="s1">'name'</span> <span class="o">=></span> <span class="s1">'Nuno'</span><span class="p">,</span> <span class="s1">'companies'</span> <span class="o">=></span> <span class="p">[</span><span class="s1">'Pest'</span><span class="p">,</span> <span class="s1">'Laravel'</span><span class="p">]])</span>
<span class="o">-></span><span class="na">name</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Nuno'</span><span class="p">)</span>
<span class="o">-></span><span class="na">companies</span><span class="o">-></span><span class="na">toHaveCount</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">-></span><span class="na">each</span><span class="o">-></span><span class="na">toBeString</span></code></pre>
<p>(notez l’usage de <code>each</code>, qui permet de boucler sur les sous-éléments du tableau)</p>
<p>Comme dit précédemment, il n’y a pas de limite dans le chaînage, ni dans le parcours en profondeur d’un objet ou d’un tableau.<br>
Ainsi, il est tout à fait possible d’aller chercher les propriétés d’un objet, et de tester qu’elles répondent elles aussi à certains critères.</p>
<p>Exemple :</p>
<pre><code class="php"><span class="nx">expect</span><span class="p">(</span><span class="nv">$user</span><span class="p">)</span>
<span class="o">-></span><span class="na">companies</span><span class="o">-></span><span class="na">first</span><span class="p">()</span><span class="o">-></span><span class="na">owner</span><span class="o">-></span><span class="na">toBeInstanceOf</span><span class="p">(</span><span class="nx">User</span><span class="o">::</span><span class="na">class</span><span class="p">)</span><span class="o">-></span><span class="na">not</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="nv">$user</span><span class="p">)</span>
<span class="o">-></span><span class="na">name</span><span class="o">-></span><span class="na">toEqual</span><span class="p">(</span><span class="s1">'Nuno'</span><span class="p">);</span></code></pre>
<p>Dans cet exemple, on comprend que l’objet <code>$user</code> a 2 propriétés testées : <code>companies</code> (qui semble renvoyer une liste d’objets), dont on vérifie que le <code>owner</code> du premier élément est bien un objet de type <code>User</code>, mais n’est pas <code>$user</code>. Et <code>name</code>, dont on vérifie la valeur. Ici, <code>name</code> se rapporte à <code>$user</code>, et non à <code>companies</code> : PEST est capable de revenir à l’objet initialement traité (celui passé en paramètre à <code>expect</code>). Et ce, sans limite !</p>
<h3 id="toc-custom-expectations">Custom expectations</h3>
<p>La liste des expectations a beau être très grande, et de nouvelles méthodes apparaissent chaque semaine, il y a toujours un moment où il manque celle dont on a besoin, pour couvrir son cas particulier.</p>
<p>Fort heureusement, PEST permet l’ajout de nouvelles méthodes, grâce à la méthode <code>extend()</code>.</p>
<p>Exemple :</p>
<pre><code class="php"><span class="c1">// tests/Pest.php</span>
<span class="nx">expect</span><span class="p">()</span><span class="o">-></span><span class="na">extend</span><span class="p">(</span><span class="s1">'toBeWithinRange'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nv">$min</span><span class="p">,</span> <span class="nv">$max</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="na">toBeGreaterThanOrEqual</span><span class="p">(</span><span class="nv">$min</span><span class="p">)</span>
<span class="o">-></span><span class="na">toBeLessThanOrEqual</span><span class="p">(</span><span class="nv">$max</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">test</span><span class="p">(</span><span class="s1">'numeric ranges'</span><span class="p">,</span> <span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">expect</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span><span class="o">-></span><span class="na">toBeWithinRange</span><span class="p">(</span><span class="mi">90</span><span class="p">,</span> <span class="mi">110</span><span class="p">);</span>
<span class="p">});</span></code></pre>
</div><div><a href="https://linuxfr.org/news/pest-soit-des-tests-unitaires.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/125226/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/pest-soit-des-tests-unitaires#comments">ouvrir dans le navigateur</a>
</p>
windu.2bYsabeau 🧶 🧦barmic 🦦Yves BourguignonBAudhttps://linuxfr.org/nodes/125226/comments.atomtag:linuxfr.org,2005:News/407372021-11-07T10:00:39+01:002021-11-08T17:40:01+01:00PHPStan est sorti en version 1.0Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>PHPStan, un outil d’analyse statique de code PHP, vient de sortir en version 1.0. Pour l’occasion, un nouveau niveau de criticité a été créé.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://phpstan.org/" hreflang="en" href="https://linuxfr.org/redirect/109357">Site officiel</a></li><li>lien nᵒ 2 : <a title="https://github.com/phpstan/phpstan" hreflang="en" href="https://linuxfr.org/redirect/109358">Dépôt Github</a></li><li>lien nᵒ 3 : <a title="https://phpstan.org/blog/phpstan-1-0-released" hreflang="en" href="https://linuxfr.org/redirect/109359">L'annonce officielle</a></li><li>lien nᵒ 4 : <a title="https://laravel-news.com/larastan-v1-released" hreflang="en" href="https://linuxfr.org/redirect/109360">Larastan passe aussi en version 1.0</a></li></ul><div><p>PHPStan est une bibliothèque en PHP permettant de faire de l’analyse statique de code PHP. Elle permet de détecter des erreurs dans du code, sans avoir besoin de l’exécuter (à la différence des outils de débogage ou de profiling, qui se basent sur une vraie exécution du code). Créé il y a 6 ans, PHPStan est devenu depuis un outil de référence pour ceux qui souhaitent surveiller la qualité de leur code.</p>
<p>Pour rappel, les niveaux de criticité couvrent les cas suivants (<a href="https://phpstan.org/user-guide/rule-levels">tirés de la documentation officielle</a>) :</p>
<ul>
<li>0 : vérifications basiques, classes ou fonctions inconnues, méthodes inconnues appelées par <code>$this</code>, nombre d’arguments passés pour ces méthodes ou fonctions incorrect, variables jamais définies</li>
<li>1 : variables potentiellement non définies, <a href="https://www.php.net/manual/fr/language.oop5.magic.php">méthodes magiques</a> et propriétés sur des classes avec <code>__call</code> et <code>__get</code>
</li>
<li>2 : méthodes inconnues vérifiées sur toutes les expressions (pas seulement sur <code>$this</code>), validation de PHPDocs</li>
<li>3 : types de retour, types assignés à des propriétés</li>
<li>4 : vérification basique de code inutilisé - <code>instanceof</code> toujours à « false » et autres vérifications de type, branches <code>else</code> inutilisées, code injoignable après un <code>return;</code>, etc.</li>
<li>5 : vérification du type des arguments passés aux méthodes et aux fonctions</li>
<li>6 : reporter les indices de type</li>
<li>7 : reporter les types d’union en partie erronées - Si vous appelez une méthode qui existe seulement pour certains types dans un type d’union le niveau 7 commence à vous en informer; autres situations potentiellement incorrectes</li>
<li>8 : reporter les méthodes d’appel et les propriétés d’accès sur des types nullable</li>
<li>9 : être strict sur le type <code>mixed</code> - la seule opération que vous pouvez faire ce type est de le passer vers un autre type <code>mixed</code>
</li>
</ul>
<p>C’est ce niveau 9 qui a donc été ajouté, avec la version 1.0 : il permet de limiter l’usage du type <code>mixed</code> (un type indiquant que le retour d’une fonction peut être de différents types).</p>
<h3 id="toc-goodies-from-bleeding-edge">Goodies from Bleeding Edge</h3>
<p>Pour fêter la sortie de cette première version stable, <a href="https://phpstan.org/merch">l’équipe propose quelques produits dérivés (t-shirts et badges)</a>.</p>
<h3 id="toc-larastan-aussi-est-en-version-10">Larastan aussi est en version 1.0</h3>
<p><a href="https://github.com/nunomaduro/larastan">Larastan</a>, un wrapper autour de PHPStan spécifiquement développé pour le framework <a href="https://fr.m.wikipedia.org/wiki/Laravel">Laravel</a> passe lui aussi en version 1.0.</p>
<p>Et vous, vous en êtes à quel niveau ?</p>
</div><div><a href="https://linuxfr.org/news/phpstan-est-sorti-en-version-1-0.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/125853/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/phpstan-est-sorti-en-version-1-0#comments">ouvrir dans le navigateur</a>
</p>
windu.2bBenoît SibaudclaudexYsabeau 🧶 🧦palm123bobble bubbletisaachttps://linuxfr.org/nodes/125853/comments.atomtag:linuxfr.org,2005:News/402342021-08-14T14:19:23+02:002021-08-14T14:28:09+02:00Laravel 8 est sortiLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Laravel 8, la dernière version du framework PHP est sortie le 8 septembre 2020.</p>
</div><ul></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<ul>
<li><a href="#toc-calendrier-de-sortie">Calendrier de sortie</a></li>
<li><a href="#toc-models-directory">Models Directory</a></li>
<li><a href="#toc-model-factory-classes">Model Factory Classes</a></li>
<li><a href="#toc-laravel-jetstream">Laravel Jetstream</a></li>
<li>
<a href="#toc-migration-squashing">Migration Squashing</a><ul>
<li><a href="#toc-sole">Sole()</a></li>
</ul>
</li>
<li>
<a href="#toc-autres-nouveaut%C3%A9s-apparues-dans-les-versions-mineures">Autres nouveautés apparues dans les versions mineures</a><ul>
<li><a href="#toc-anonymous-migrations">Anonymous Migrations</a></li>
<li><a href="#toc-v%C3%A9rification-que-le-mot-de-passe-na-pas-fuit%C3%A9">Vérification que le mot de passe n'a pas fuité</a></li>
<li><a href="#toc-one-of-many">One of Many</a></li>
<li><a href="#toc-parallel-testing">Parallel testing</a></li>
<li><a href="#toc-disable-lazy-loading">Disable Lazy-loading</a></li>
</ul>
</li>
<li>
<a href="#toc-et-maintenant">Et maintenant ?</a><ul>
<li><a href="#toc-laravel-9-lts">Laravel 9 LTS</a></li>
<li><a href="#toc-laracon-2021">Laracon 2021</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<p><a href="https://fr.wikipedia.org/wiki/Laravel">Laravel</a> est un framework PHP, qui existe depuis 2011 (<a href="https://laravel-news.com/laravel-is-10-years-old">il a fêté ses 10 ans en juin</a>). <a href="https://www.grafikart.fr/tutoriels/laravel-symfony-866">Parfois comparé</a> à <a href="https://fr.wikipedia.org/wiki/Symfony">Symfony</a> (qui date de 2005), avec qui <a href="https://symfony.com/projects/laravel">il partage certaines briques de code</a> tout en se voulant plus facile à configurer, il n'a cependant pas à rougir de ses possibilités. Que ce soit en interne, ou <em>via</em> les nombreuses extensions existantes.</p>
<p>Cette nouvelle version continue le travail commencé avec la version 7, en enrichissant le framework de nouvelles possibilités et améliorations.</p>
<h3 id="toc-calendrier-de-sortie">Calendrier de sortie</h3>
<p>À l'origine, cette dépêche devait parler de Laravel 9.0 LTS, prévue initialement pour mars 2021.<br>
Mais les développeurs du framework ont entre-temps jugé que Laravel était désormais suffisamment stable et abouti pour ne plus avoir besoin d'une sortie majeure tous les 6 mois. <a href="https://blog.laravel.com/updates-to-laravels-versioning-policy">Le rythme est donc désormais d'une sortie majeure tous les 12 mois, et une sortie mineure toutes les semaines</a>.</p>
<p>Tout ceci reportait donc Laravel 9 à septembre 2021, entraînant la parution de certaines nouvelles fonctionnalités dans des versions mineures de Laravel 8, et la prolongation de la durée du support de Laravel 6 LTS.<br>
(cf. <a href="https://laravelversions.com/">le calendrier des versions officiellement supportées</a>)</p>
<p>Il faudra finalement attendre janvier 2022, afin que cette nouvelle version LTS puisse reposer sur <a href="https://symfony.com/releases/6.0">Symfony 6.0, lui-même prévu pour novembre 2021</a>. Le calendrier des sorties suivantes (Laravel 10 et 11) est décalé en conséquence.</p>
<h3 id="toc-models-directory">Models Directory</h3>
<p>Jusqu'à présent, les classes (héritant de) <code>Models</code> étaient rangées directement dans le répertoire <em>app</em>, contrairement à toutes les autres classes (<code>Controllers</code>, <code>Requests</code>, <code>Providers</code>, <code>Notifications</code>, <code>Rules</code>, …) qui ont toutes leur (sous-)répertoire attribué.<br>
Désormais, le répertoire <em>app/Models</em> existe. À noter que la commande <code>php artisan make:model XXX</code> (qui permet de générer un squelette de fichier PHP pour la classe-modèle <em>XXX</em>) tient compte de l'existence (ou non) de ce répertoire. Bien pratique si on veut conserver l'arborescence d'un projet déjà existant, sans avoir à déplacer les classes dans ce nouveau répertoire, au risque d'oublier de modifier le <em>namespace</em>, ou les imports de classes.</p>
<h3 id="toc-model-factory-classes">Model Factory Classes</h3>
<p>Dans Laravel, les <em>factories</em> sont des générateurs d'objets (des instances de <code>Models</code>) avec des données de tests. Le but étant de pouvoir créer des objets reposant sur les classes-métiers du projet, afin de les utiliser au sein des tests unitaires.<br>
Désormais, les factories se basent sur une classe <code>Factory</code>, et suivent une structure beaucoup plus orientée POO.</p>
<pre><code class="php"> <span class="c1">// Factory en Laravel 6 LTS</span>
<span class="nv">$factory</span><span class="o">-></span><span class="na">define</span><span class="p">(</span><span class="nx">User</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Faker</span> <span class="nv">$faker</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="s1">'firstname'</span> <span class="o">=></span> <span class="nv">$faker</span><span class="o">-></span><span class="na">name</span><span class="p">,</span>
<span class="s1">'lastname'</span> <span class="o">=></span> <span class="nv">$faker</span><span class="o">-></span><span class="na">name</span><span class="p">,</span>
<span class="s1">'email'</span> <span class="o">=></span> <span class="nv">$faker</span><span class="o">-></span><span class="na">unique</span><span class="p">()</span><span class="o">-></span><span class="na">safeEmail</span><span class="p">,</span>
<span class="s1">'email_verified_at'</span> <span class="o">=></span> <span class="nx">now</span><span class="p">(),</span>
<span class="s1">'password'</span> <span class="o">=></span> <span class="s1">'$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi'</span><span class="p">,</span> <span class="c1">// password</span>
<span class="s1">'remember_token'</span> <span class="o">=></span> <span class="nx">Str</span><span class="o">::</span><span class="na">random</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span>
<span class="p">];</span>
<span class="p">});</span></code></pre>
<pre><code class="php"> <span class="c1">// Factory en Laravel 8</span>
<span class="k">class</span> <span class="nc">UserFactory</span> <span class="k">extends</span> <span class="nx">Factory</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * The name of the factory's corresponding model.</span>
<span class="sd"> *</span>
<span class="sd"> * @var string</span>
<span class="sd"> */</span>
<span class="k">protected</span> <span class="nv">$model</span> <span class="o">=</span> <span class="nx">User</span><span class="o">::</span><span class="na">class</span><span class="p">;</span>
<span class="sd">/**</span>
<span class="sd"> * Define the model's default state.</span>
<span class="sd"> *</span>
<span class="sd"> * @return array</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">definition</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="s1">'firstname'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="na">faker</span><span class="o">-></span><span class="na">name</span><span class="p">(),</span>
<span class="s1">'lastname'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="na">faker</span><span class="o">-></span><span class="na">name</span><span class="p">(),</span>
<span class="s1">'email'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="na">faker</span><span class="o">-></span><span class="na">unique</span><span class="p">()</span><span class="o">-></span><span class="na">safeEmail</span><span class="p">(),</span>
<span class="s1">'email_verified_at'</span> <span class="o">=></span> <span class="nx">now</span><span class="p">(),</span>
<span class="s1">'password'</span> <span class="o">=></span> <span class="s1">'$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi'</span><span class="p">,</span> <span class="c1">// password</span>
<span class="s1">'remember_token'</span> <span class="o">=></span> <span class="nx">Str</span><span class="o">::</span><span class="na">random</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span>
<span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<h3 id="toc-laravel-jetstream">Laravel Jetstream</h3>
<p>Jetstream est un kit de démarrage regroupant les fonctionnalités communes à tout nouveau projet : register/login, vérification d'email et authentification en deux étapes, gestion des sessions. Jetstream vient en remplacement de l'ancien système de register/login (templates HTML et contrôleurs) proposé par Laravel : <code>laravel-ui</code>.</p>
<h3 id="toc-migration-squashing">Migration Squashing</h3>
<p>Laravel propose, de base, un mécanisme de migration des schémas de bases de données, permettant de créer (ou de mettre à jour) les tables. Plutôt que d'écrire des requêtes SQL, dont la grammaire peut dépendre du SGBDR utilisé, ce mécanisme repose intégralement sur des classes et méthodes fournies par le framework Laravel. Charge à lui ensuite d'appeler la bonne implémentation, en fonction du SGBDR destinataire (MySQL, PostgreSQL, SQLite et SQL Server sont les seuls officiellement supportés, actuellement).<br>
Chaque migration se compose d'un fichier (dont le nom est horodaté, ce qui permet de définir leur ordre d'exécution) indiquant les modifications à effectuer sur la base de données.</p>
<p>Exemple :</p>
<pre><code class="php"> <span class="k">class</span> <span class="nc">CreateUsersTable</span> <span class="k">extends</span> <span class="nx">Migration</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Run the migrations.</span>
<span class="sd"> *</span>
<span class="sd"> * @return void</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">up</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">Schema</span><span class="o">::</span><span class="na">create</span><span class="p">(</span><span class="s1">'users'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">increments</span><span class="p">(</span><span class="s1">'id'</span><span class="p">);</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">unsignedInteger</span><span class="p">(</span><span class="s1">'gender_id'</span><span class="p">)</span>
<span class="o">-></span><span class="na">default</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">string</span><span class="p">(</span><span class="s1">'firstname'</span><span class="p">);</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">string</span><span class="p">(</span><span class="s1">'lastname'</span><span class="p">);</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">string</span><span class="p">(</span><span class="s1">'email'</span><span class="p">)</span><span class="o">-></span><span class="na">unique</span><span class="p">();</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">timestamp</span><span class="p">(</span><span class="s1">'email_verified_at'</span><span class="p">)</span>
<span class="o">-></span><span class="na">nullable</span><span class="p">();</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">string</span><span class="p">(</span><span class="s1">'password'</span><span class="p">);</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">timestamps</span><span class="p">();</span> <span class="c1">// Ajoute les champs 'created_at' et 'updated_at'</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">softDeletes</span><span class="p">();</span> <span class="c1">// Ajoute le champ 'deleted_at'</span>
<span class="c1">// On ajoute une FK vers la table 'genders', qui doit avoir été créée dans un script précédent</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">foreign</span><span class="p">(</span><span class="s1">'gender_id'</span><span class="p">)</span>
<span class="o">-></span><span class="na">references</span><span class="p">(</span><span class="s1">'id'</span><span class="p">)</span>
<span class="o">-></span><span class="na">on</span><span class="p">(</span><span class="s1">'genders'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="sd">/**</span>
<span class="sd"> * Reverse the migrations.</span>
<span class="sd"> *</span>
<span class="sd"> * @return void</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">down</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">Schema</span><span class="o">::</span><span class="na">hasTable</span><span class="p">(</span><span class="s1">'users'</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// On supprime la FK, avant de supprimer la table</span>
<span class="nx">Schema</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s1">'users'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">dropForeign</span><span class="p">(</span><span class="s1">'users_gender_id_foreign'</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">Schema</span><span class="o">::</span><span class="na">drop</span><span class="p">(</span><span class="s1">'users'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p>Ces scripts de migration sont donc l'équivalent d'un <code>versioning</code> de la structure de la base de données. Laravel permet d'ailleurs de revenir en arrière, grâce à la méthode <code>down</code> (avec le risque, évidemment, d'une perte de données si cette méthode supprime des champs/tables dans lesquels des données auront été insérées).</p>
<p>Avec la nouvelle commande <code>php artisan schema:dump</code>, il est désormais possible de générer un seul (gros) fichier au format SQL, correspondant à la structure de la base de données. Désormais, si un tel fichier existe, Laravel l'exécutera en premier, puis exécutera les scripts de migration créés après (toujours en se basant sur l'horodatage dans les noms des fichiers). Utile lorsqu'on a un projet comptant plusieurs centaines de scripts de migration, et dont le temps d'exécution peut devenir rédhibitoire (ce qui peut vite avoir un impact non négligeable sur le temps d'exécution des tests, si la base de données est recréée avant chacun d'entre eux).<br>
À noter que cette commande ne fonctionne pour l'instant qu'avec MySQL et PostgreSQL.</p>
<h4 id="toc-sole">Sole()</h4>
<p>La méthode <code>sole()</code>, apparue dans <a href="https://laravel-news.com/laravel-8-23-0">Laravel 8.23</a>, permet de renvoyer le seul et unique objet correspondant au(x) critère(s). Si aucun ou plusieurs objets correspondent, une exception est levée.<br>
Cela est utile dans le cas où on est sûr de n'obtenir qu'un seul objet, quand on filtre une collection suivant un critère (ex : les informations d'une facture quand son statut est <code>INITIALIZED</code>).<br>
Avant cela, il fallait forcément filtrer à nouveau la collection, pour n'obtenir que le 1er élément, quand bien même il n'y en avait qu'un.</p>
<p>Exemple :</p>
<pre><code class="php"> <span class="c1">// Laravel 6 LTS</span>
<span class="nv">$book</span> <span class="o">=</span> <span class="nx">Book</span><span class="o">::</span><span class="na">where</span><span class="p">(</span><span class="s1">'title'</span><span class="p">,</span> <span class="s1">'like'</span><span class="p">,</span> <span class="s1">'%War%'</span><span class="p">)</span><span class="o">-></span><span class="na">first</span><span class="p">();</span> <span class="c1">// Il faut préciser qu'on veut le premier élément, sinon on obtient une liste (de 1 élément, potentiellement)</span>
<span class="c1">// Laravel 8</span>
<span class="nv">$book</span> <span class="o">=</span> <span class="nx">Book</span><span class="o">::</span><span class="na">where</span><span class="p">(</span><span class="s1">'title'</span><span class="p">,</span> <span class="s1">'like'</span><span class="p">,</span> <span class="s1">'%War%'</span><span class="p">)</span><span class="o">-></span><span class="na">sole</span><span class="p">();</span> <span class="c1">// Renvoie forcément un seul élément, ou lève une exception</span></code></pre>
<h3 id="toc-autres-nouveautés-apparues-dans-les-versions-mineures">Autres nouveautés apparues dans les versions mineures</h3>
<p>La liste ci-dessous n'a pas vocation à être exhaustive, mais à montrer que le cycle désormais annuel n'a pas empêché l'apparition de nouvelles fonctionnalités. Elles sont listées dans l'ordre de leur apparition.</p>
<h4 id="toc-anonymous-migrations">Anonymous Migrations</h4>
<p>Comme dit précédemment, Laravel a un mécanisme de migration de la structure de la base de données, reposant sur des scripts horodatés.<br>
Jusqu'à présent, chaque script est une classe (héritant de la classe <code>Migration</code> fournie par Laravel) contenant les modifications à effectuer.<br>
Problème : cette classe doit avoir un nom unique (car tous les scripts de migration sont dans le même <code>namespace</code>). Ce qui n'est pas toujours pratique, quand le nombre de scripts est de plusieurs centaines.<br>
<a href="https://laravel-news.com/laravel-anonymous-migrations">Depuis Laravel 8.37</a>, il est possible d'avoir une classe anonyme.</p>
<p>Exemple :</p>
<pre><code class="php"><span class="o"><?</span><span class="nx">php</span>
<span class="k">use</span> <span class="nx">Illuminate\Database\Migrations\Migration</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Database\Schema\Blueprint</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Support\Facades\Schema</span><span class="p">;</span>
<span class="k">return</span> <span class="k">new</span> <span class="k">class</span> <span class="nc">extends</span> <span class="nx">Migration</span> <span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Run the migrations.</span>
<span class="sd"> *</span>
<span class="sd"> * @return void</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">up</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">Schema</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s1">'people'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">string</span><span class="p">(</span><span class="s1">'first_name'</span><span class="p">)</span><span class="o">-></span><span class="na">nullable</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="sd">/**</span>
<span class="sd"> * Reverse the migrations.</span>
<span class="sd"> *</span>
<span class="sd"> * @return void</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">down</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">Schema</span><span class="o">::</span><span class="na">table</span><span class="p">(</span><span class="s1">'people'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="nx">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$table</span><span class="o">-></span><span class="na">dropColumn</span><span class="p">(</span><span class="s1">'first_name'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">};</span></code></pre>
<h4 id="toc-vérification-que-le-mot-de-passe-na-pas-fuité">Vérification que le mot de passe n'a pas fuité</h4>
<p><a href="https://laravel-news.com/password-validation-rule-object-in-laravel-8">Laravel 8.39</a> a ajouté une nouvelle règle de validation (appelées <code>validation rules</code>) : <code>uncompromised()</code>. Elle permet de vérifier que le mot de passe saisi par l'utilisateur (lors de son inscription, par ex.) ne fait pas partie des mots de passe ayant fuité, suite au piratage de sites Web à grand public. Cette vérification repose sur <a href="https://haveibeenpwned.com/API/v2">l'API du site HaveIBeenPwned.com</a></p>
<p>Pour rappel, les <code>validation rules</code> sont <a href="https://laravel.com/docs/8.x/validation#available-validation-rules">un ensemble de règles de validation de champs de formulaires</a>.<br>
<a href="https://laravel.com/docs/8.x/validation#available-validation-rules">Laravel en fournit un grand nombre</a> (<code>required</code>, <code>exists</code>, <code>min</code>, <code>string</code>, …), mais il est tout à fait possible d'en créer de nouvelles.</p>
<h4 id="toc-one-of-many">One of Many</h4>
<p>Laravel permet le mapping ORM grâce à des méthodes (<code>hasOne</code>, <code>belongsToMany</code>, …) dont héritent tous les modèles et qui permettent de renvoyer le ou les objets qui leur sont liés. Il suffit pour cela d'écrire une méthode dans la classe-modèle concernée.</p>
<p>Exemple : </p>
<pre><code class="php"><span class="o"><?</span><span class="nx">php</span>
<span class="k">namespace</span> <span class="nx">App\Models</span><span class="p">;</span>
<span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Model</span><span class="p">;</span>
<span class="k">class</span> <span class="nc">User</span> <span class="k">extends</span> <span class="nx">Model</span>
<span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * Un utilisateur peut avoir un téléphone (de type `Phone`) associé</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">phone</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="na">hasOne</span><span class="p">(</span><span class="nx">Phone</span><span class="o">::</span><span class="na">class</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p>Par défaut, Laravel devine les clés primaires et étrangères, ainsi que les tables concernées. Mais on peut ajouter des paramètres à cette méthode, si les noms des champs ne respectent pas la structure par défaut de Laravel.</p>
<p>Depuis <a href="https://laravel-news.com/one-of-many-eloquent-relationship">Laravel 8.42</a>, il est désormais possible de transformer une relation <code>one-to-many</code> en <code>one-to-one</code>, et ainsi de ne renvoyer qu'un élément.<br>
C'est notamment utile pour récupérer la première/dernière fois qu'un utilisateur s'est connecté, ou le prix actuel (le dernier enregistré en base) d'un produit.</p>
<p>Exemple :</p>
<pre><code class="php"><span class="sd">/**</span>
<span class="sd"> * Renvoie le dernier ordre passé par ce User.</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">latestOrder</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="na">hasOne</span><span class="p">(</span><span class="nx">Order</span><span class="o">::</span><span class="na">class</span><span class="p">)</span><span class="o">-></span><span class="na">latestOfMany</span><span class="p">();</span>
<span class="p">}</span></code></pre>
<h4 id="toc-parallel-testing">Parallel testing</h4>
<p>Le report de Laravel 9 a entraîné la parution, en "avance" d'une amélioration très attendue : <a href="https://blog.laravel.com/laravel-parallel-testing-is-now-available">la possibilité de pouvoir enfin exécuter les tests en parallèle !</a><br>
Pour générer et exécuter des tests, Laravel utilise la librairie <a href="https://phpunit.de/">PHPUnit</a>, enrichie d'un ensemble de classes et méthodes permettant de tester des fonctionnalités propres à Laravel (exécution des scripts de migration pour construire et remplir la base de données avant d'exécuter les tests, appels d'URL pour des tests <em>features</em> de bout en bout, tests des retours JSON, …).<br>
Pour pouvoir exécuter les tests en parallèle, il fallait jusque-là passer par la librairie <a href="https://github.com/paratestphp/paratest">Paratest</a>. Désormais, Laravel intègre directement une solution basée sur Paratest.</p>
<h4 id="toc-disable-lazy-loading">Disable Lazy-loading</h4>
<p>Grâce à Eloquent (son ORM intégré), Laravel permet facilement d'accéder aux relations d'un objet.</p>
<p>Par exemple :</p>
<pre><code class="php"><span class="c1">// UserController.php</span>
<span class="nv">$user</span> <span class="o">=</span> <span class="nx">User</span><span class="o">::</span><span class="na">find</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="c1">// topics.blade.php</span>
<span class="o"><</span><span class="nx">h2</span><span class="o">></span><span class="nx">Topics</span><span class="o"></</span><span class="nx">h2</span><span class="o">></span>
<span class="o">@</span><span class="k">foreach</span><span class="p">(</span><span class="nv">$user</span><span class="o">-></span><span class="na">topics</span> <span class="k">as</span> <span class="nv">$topic</span><span class="p">)</span>
<span class="o"><</span><span class="nx">h3</span><span class="o">></span><span class="p">{{</span> <span class="nv">$topic</span><span class="o">-></span><span class="na">title</span> <span class="p">}}</span><span class="o"></</span><span class="nx">h3</span><span class="o">></span>
<span class="o">@</span><span class="k">foreach</span><span class="p">(</span><span class="nv">$topic</span><span class="o">-></span><span class="na">comments</span> <span class="k">as</span> <span class="nv">$comment</span><span class="p">)</span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="p">{{</span> <span class="nv">$comment</span><span class="o">-></span><span class="na">body</span> <span class="p">}}</span>
<span class="o"></</span><span class="nx">p</span><span class="o">></span>
<span class="o">@</span><span class="k">endforeach</span>
<span class="o">@</span><span class="k">endforeach</span></code></pre>
<p>Ce mécanisme, certes très pratique, a toutefois un inconvénient : mal utilisé, il provoque le problème dit "N+1 requêtes" (<em>N+1 queries problem</em>).<br>
En effet, pour que l'exemple ci-dessus s'exécute, il faut 1 requête pour récupérer l'objet <code>$user</code>, puis 1 requête pour chaque objet <code>$topic</code>. Et comme on le voit dans l'exemple, il est tout à fait possible d'invoquer les relations d'une relation, et ce sans limite. Ce qui augmente d'autant le nombre de requêtes SQL !<br>
Pour éviter cela, il est possible, dès la récupération de l'objet <code>$user</code>, de demander à ce que soient en même temps chargées ses relations (et leurs relations, et…).</p>
<p>Le début de l'exemple ci-dessus devient alors :</p>
<pre><code class="php"><span class="c1">// UserController.php</span>
<span class="nv">$user</span> <span class="o">=</span> <span class="nx">User</span><span class="o">::</span><span class="na">where</span><span class="p">(</span><span class="s1">'id'</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span><span class="o">-></span><span class="na">with</span><span class="p">(</span><span class="s1">'topics'</span><span class="p">,</span> <span class="s1">'topics.comments'</span><span class="p">)</span><span class="o">-></span><span class="na">first</span><span class="p">();</span></code></pre>
<p>Le nombre de requêtes chute alors, puisqu'il en faut toujours une seule pour charger <code>$user</code> (ça ne change pas), mais aussi une seule pour charger tous les <code>topics</code> appartenant à <code>$user</code>, et une autre pour charger tous les <code>comments</code> de tous les <code>topics</code> appartenant à <code>$user</code>. On passe donc de M*N + 1 requêtes, à 3 !</p>
<p>Problème résolu, alors ? Pas tout à fait. Si la méthode <code>with()</code> résout bien le problème, encore faut-il l'utiliser !<br>
Et c'est là qu'intervient <a href="https://laravel-news.com/disable-eloquent-lazy-loading-during-development">la nouveauté en question</a>, consistant à désactiver le lazy-loading, pour forcer Laravel à lever une exception, et ainsi détecter les endroits dans le code ne recourant pas encore à la méthode <code>with()</code>.<br>
À noter que ce mécanisme de désactivation du lazy-loading est global. C'est donc la totalité du projet qui est susceptible de lever des exceptions. Il est donc préférable de faire ça en local d'abord (mais si vous êtes du genre à débugguer en production, allez-y !)</p>
<h3 id="toc-et-maintenant">Et maintenant ?</h3>
<h4 id="toc-laravel-9-lts">Laravel 9 LTS</h4>
<p>Comme dit plus haut, Laravel 9 LTS a été reporté. Mais des premières infos sur ce que contiendra cette version ont déjà été annoncées :<br>
- Support de PHP 8.0 minimum ;<br>
- Utilisation de Symfony 6.0, pour les briques logicielles qui en proviennent ;<br>
- <code>Anonymous Stub Migrations</code> : désormais, ce comportement sera par défaut (cf. plus haut, pour plus d'infos sur ce qu'est cette fonctionnalité) ;<br>
- …</p>
<h4 id="toc-laracon-2021">Laracon 2021</h4>
<p>Laracon, la conférence annuelle autour de Laravel, <a href="https://laracon.net/">aura lieu le 1er septembre 2021</a>.</p>
</div><div><a href="https://linuxfr.org/news/laravel-8-est-sorti.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/122771/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/laravel-8-est-sorti#comments">ouvrir dans le navigateur</a>
</p>
windu.2bAnonymeBenoît Sibaudyalpalm123https://linuxfr.org/nodes/122771/comments.atom