{"id":48633,"date":"2021-09-24T19:30:38","date_gmt":"2021-09-24T17:30:38","guid":{"rendered":"https:\/\/kinsta.com\/?p=100177"},"modified":"2023-08-22T09:59:11","modified_gmt":"2023-08-22T08:59:11","slug":"transpiler-php","status":"publish","type":"post","link":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/","title":{"rendered":"Le guide ultime pour transpiler le code PHP"},"content":{"rendered":"<p>Dans des circonstances id\u00e9ales, nous devrions utiliser <a href=\"https:\/\/kinsta.com\/fr\/changelog\/php-8\/\">PHP 8.0<\/a> (la derni\u00e8re version au moment d&rsquo;\u00e9crire ces lignes) pour tous nos sites et le mettre \u00e0 jour d\u00e8s qu&rsquo;une nouvelle version est publi\u00e9e. Cependant, les d\u00e9veloppeurs devront souvent travailler avec des versions ant\u00e9rieures de PHP, par exemple lorsqu&rsquo;ils cr\u00e9ent une extension publique pour <a href=\"https:\/\/kinsta.com\/fr\/blog\/qu-est-ce-que-wordpress\/\">WordPress<\/a> ou qu&rsquo;ils travaillent avec du code h\u00e9rit\u00e9 qui emp\u00eache la mise \u00e0 jour de l&rsquo;environnement du serveur web.<\/p>\n<p>Dans ces situations, nous pourrions abandonner l&rsquo;espoir d&rsquo;utiliser le dernier code PHP. Mais il existe une meilleure alternative : nous pouvons toujours \u00e9crire notre code source avec PHP 8.0 et le transpiler vers une version ant\u00e9rieure de PHP &#8211; m\u00eame vers PHP 7.1.<\/p>\n<p>Dans ce guide, nous allons vous apprendre tout ce que vous devez savoir sur la transpilation du code PHP.<\/p>\n<div><\/div><kinsta-auto-toc heading=\"Table of Contents\" exclude=\"last\" list-style=\"arrow\" selector=\"h2\" count-number=\"-1\"><\/kinsta-auto-toc>\n<h2>Qu&rsquo;est-ce que la transpilation ?<\/h2>\n<p>La transpilation convertit le code source d&rsquo;un <a href=\"https:\/\/kinsta.com\/fr\/blog\/meilleur-langage-programmation-apprendre\/\">langage de programmation<\/a> en un code source \u00e9quivalent du m\u00eame langage de programmation ou d&rsquo;un langage diff\u00e9rent.<\/p>\n<p>La transpilation n&rsquo;est pas un concept nouveau dans le d\u00e9veloppement web : les d\u00e9veloppeurs c\u00f4t\u00e9 client connaissent probablement <a href=\"https:\/\/babeljs.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">Babel<\/a>, un transpileur pour le code JavaScript.<\/p>\n<p>Babel convertit le code JavaScript de la version moderne ECMAScript 2015+ en une version h\u00e9rit\u00e9e compatible avec les anciens navigateurs. Par exemple, \u00e9tant donn\u00e9 une fonction fl\u00e8che ES2015 :<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map((n) =&gt; n * 2);\n<\/code><\/pre>\n<p>&#8230;Babel la convertira en sa version ES5 :<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map(function(n) {\n  return n * 2;\n});\n<\/code><\/pre>\n<h3>Qu&rsquo;est-ce que la transpilation PHP ?<\/h3>\n<p>Ce qui est potentiellement nouveau dans le d\u00e9veloppement web, c&rsquo;est la possibilit\u00e9 de transpiler le code c\u00f4t\u00e9 serveur, en particulier PHP.<\/p>\n<p>La transpilation PHP fonctionne de la m\u00eame mani\u00e8re que la transpilation JavaScript : le code source d&rsquo;une <a href=\"https:\/\/kinsta.com\/fr\/blog\/versions-php\/\">version moderne de PHP<\/a> est converti en un code \u00e9quivalent pour une version plus ancienne de PHP.<\/p>\n<p>En suivant le m\u00eame exemple que pr\u00e9c\u00e9demment, une fonction fl\u00e8che de PHP 7.4 :<\/p>\n<pre><code class=\"language-php\">$nums = array_map(fn($n) =&gt; $n * 2, [2, 4, 6]) ;\n<\/code><\/pre>\n<p>&#8230;peut \u00eatre transpil\u00e9e dans sa version \u00e9quivalente de PHP 7.3 :<\/p>\n<pre><code class=\"language-php\">$nums = array_map(\n  function ($n) {\n    return $n * 2;\n  },\n  [2, 4, 6]\n);\n<\/code><\/pre>\n<p>Les fonctions fl\u00e9ch\u00e9es peuvent \u00eatre transpil\u00e9es car elles sont du <a href=\"https:\/\/en.wikipedia.org\/wiki\/Syntactic_sugar\" target=\"_blank\" rel=\"noopener noreferrer\">syntaxic sugar<\/a>, c&rsquo;est-\u00e0-dire une nouvelle syntaxe pour produire un comportement existant.<\/p>\n<p>Cependant, il existe aussi de nouvelles fonctions qui cr\u00e9ent un nouveau comportement, et \u00e0 ce titre, il n&rsquo;y aura pas de code \u00e9quivalent pour les versions pr\u00e9c\u00e9dentes de PHP. C&rsquo;est le cas des <a href=\"https:\/\/kinsta.com\/fr\/blog\/php-8\/#union-types-2-0\">types d&rsquo;union<\/a>, introduits dans PHP 8.0 :<\/p>\n<pre><code class=\"language-php\">function someFunction(float|int $param): string|float|int|null\n{\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Dans ces situations, la transpilation peut toujours \u00eatre effectu\u00e9e tant que la nouvelle fonctionnalit\u00e9 est n\u00e9cessaire pour le d\u00e9veloppement mais pas pour la production. Dans ce cas, nous pouvons simplement supprimer compl\u00e8tement la fonctionnalit\u00e9 du code transpil\u00e9 sans cons\u00e9quences graves.<\/p>\n<p>Un exemple est celui des types d&rsquo;union. Cette fonctionnalit\u00e9 est utilis\u00e9e pour v\u00e9rifier qu&rsquo;il n&rsquo;y a pas d&rsquo;incompatibilit\u00e9 entre le type d&rsquo;entr\u00e9e et sa valeur fournie, ce qui permet d&rsquo;\u00e9viter les bogues. S&rsquo;il y a un conflit avec les types, il y aura une erreur d\u00e9j\u00e0 dans le d\u00e9veloppement, et nous devrions l&rsquo;attraper et la corriger avant que le code n&rsquo;atteigne la production.<\/p>\n<p>Nous pouvons donc nous permettre de supprimer la fonctionnalit\u00e9 du code pour la production :<\/p>\n<pre><code class=\"language-php\">function someFunction($param)\n{\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Si l&rsquo;erreur se produit quand m\u00eame en production, le message d&rsquo;erreur sera moins pr\u00e9cis que si nous avions des types d&rsquo;union. Cependant, cet inconv\u00e9nient potentiel est compens\u00e9 par la possibilit\u00e9 d&rsquo;utiliser les types d&rsquo;union en premier lieu.<\/p>\n\n<h2>Avantages de la transpilation du code PHP<\/h2>\n<p>La transpilation permet de coder une application en utilisant la derni\u00e8re version de PHP et de produire une version qui fonctionne \u00e9galement dans des environnements ex\u00e9cutant des versions plus anciennes de PHP.<\/p>\n<p>Cela peut \u00eatre particuli\u00e8rement utile pour les d\u00e9veloppeurs qui cr\u00e9ent des produits pour les anciens <a href=\"https:\/\/kinsta.com\/fr\/blog\/systeme-gestion-contenu\/\">syst\u00e8mes de gestion de contenu (CMS)<\/a>. WordPress, par exemple, prend toujours <a href=\"https:\/\/wordpress.org\/about\/requirements\/\" target=\"_blank\" rel=\"noopener noreferrer\">officiellement en charge PHP 5.6<\/a> (m\u00eame s&rsquo;il recommande PHP 7.4+). Le pourcentage de sites WordPress ex\u00e9cutant les versions 5.6 \u00e0 7.2 de PHP &#8211; qui sont toutes en fin de vie (End Of Life ou EOL), ce qui signifie qu&rsquo;elles ne re\u00e7oivent plus de mises \u00e0 jour de s\u00e9curit\u00e9 &#8211; s&rsquo;\u00e9l\u00e8ve \u00e0 34,8 %, et ceux fonctionnant avec une version de PHP autre que 8.0 \u00e0 99,5 % :<\/p>\n<figure style=\"width: 920px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/wp-stats.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/wp-stats.png\" alt=\"Statistiques d'utilisation de WordPress par version.\" width=\"920\" height=\"720\"><\/a><figcaption class=\"wp-caption-text\">Statistiques d&rsquo;utilisation de WordPress par version. Source de l&rsquo;image : <a href=\"https:\/\/wordpress.org\/about\/stats\/\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress<\/a><\/figcaption><\/figure>\n<p>Par cons\u00e9quent, les th\u00e8mes et extensions WordPress destin\u00e9s \u00e0 un public mondial seront tr\u00e8s probablement cod\u00e9s avec une ancienne version de PHP pour augmenter leur port\u00e9e possible. Gr\u00e2ce \u00e0 la transpilation, ils pourraient \u00eatre cod\u00e9s avec PHP 8.0, et \u00eatre tout de m\u00eame publi\u00e9s pour une ancienne version de PHP, ciblant ainsi le plus grand nombre d&rsquo;utilisateurs possible.<\/p>\n<p>En effet, toute application qui doit prendre en charge une version de PHP autre que la plus r\u00e9cente (m\u00eame dans la gamme des versions PHP actuellement prises en charge) peut en b\u00e9n\u00e9ficier.<\/p>\n<p>C&rsquo;est le cas de Drupal, qui <a href=\"https:\/\/www.drupal.org\/docs\/system-requirements\/php-requirements\" target=\"_blank\" rel=\"noopener noreferrer\">n\u00e9cessite PHP 7.3<\/a>. Gr\u00e2ce \u00e0 la transpilation, les d\u00e9veloppeurs peuvent cr\u00e9er des modules Drupal accessibles au public en utilisant PHP 8.0, et les publier avec PHP 7.3.<\/p>\n<p>Un autre exemple est la cr\u00e9ation de code personnalis\u00e9 pour les clients qui ne peuvent pas ex\u00e9cuter PHP 8.0 dans leur environnement pour une raison ou une autre. N\u00e9anmoins, gr\u00e2ce \u00e0 la transpilation, les d\u00e9veloppeurs peuvent toujours coder leurs produits livrables en utilisant PHP 8.0 et les ex\u00e9cuter sur ces anciens environnements.<\/p>\n<h3>Quand transpiler PHP<\/h3>\n<p>Le code PHP peut toujours \u00eatre transpil\u00e9, sauf s&rsquo;il contient une fonctionnalit\u00e9 PHP qui n&rsquo;a pas d&rsquo;\u00e9quivalent dans la version pr\u00e9c\u00e9dente de PHP.<\/p>\n<p>C&rsquo;est peut-\u00eatre le cas des <a href=\"https:\/\/kinsta.com\/fr\/blog\/php-8\/#attributes\">attributs<\/a>, introduits dans PHP 8.0 :<\/p>\n<pre><code class=\"language-php\">#[SomeAttr]\nfunction someFunc() {}\n\n#[AnotherAttr]\nclass SomeClass {}<\/code><\/pre>\n<p>Dans l&rsquo;exemple pr\u00e9c\u00e9dent utilisant les fonctions fl\u00e8ches, le code pouvait \u00eatre transpil\u00e9 car les fonctions fl\u00e8ches sont du syntaxic sugar. Les attributs, en revanche, cr\u00e9ent un comportement compl\u00e8tement nouveau. Ce comportement pourrait aussi \u00eatre reproduit avec PHP 7.4 et inf\u00e9rieur, mais seulement en le codant manuellement, c&rsquo;est-\u00e0-dire pas automatiquement sur la base d&rsquo;un outil ou d&rsquo;un processus (l&rsquo;IA pourrait fournir une solution, mais nous n&rsquo;en sommes pas encore l\u00e0).<\/p>\n<p>Les attributs destin\u00e9s \u00e0 \u00eatre utilis\u00e9s pour le d\u00e9veloppement, tels que <a href=\"https:\/\/wiki.php.net\/rfc\/deprecated_attribute\"><code>#[Deprecated]<\/code><\/a>peuvent \u00eatre supprim\u00e9s de la m\u00eame mani\u00e8re que les types d&rsquo;union. Mais les attributs qui modifient le comportement de l&rsquo;application en production ne peuvent pas \u00eatre supprim\u00e9s, et ils ne peuvent pas non plus \u00eatre directement transpil\u00e9s.<\/p>\n<p>\u00c0 ce jour, aucun transpilateur ne peut prendre du code avec des attributs PHP 8.0 et produire automatiquement son code \u00e9quivalent en PHP 7.4. Par cons\u00e9quent, si votre code PHP doit utiliser des attributs, il sera difficile, voire impossible, de le transpiler.<\/p>\n<h3>Fonctionnalit\u00e9s PHP qui peuvent \u00eatre transpil\u00e9es<\/h3>\n<p>Ce sont les fonctionnalit\u00e9s de PHP 7.1 et plus qui peuvent actuellement \u00eatre transpil\u00e9es. Si votre code n&rsquo;utilise que ces fonctionnalit\u00e9s, vous pouvez avoir la certitude que votre application transpil\u00e9e fonctionnera. Sinon, vous devrez \u00e9valuer si le code transpil\u00e9 produira des d\u00e9faillances.<\/p>\n<table>\n<thead>\n<tr>\n<th>Version de <span id=\"urn:enhancement-5f1c5957-f7d1-4eb1-8856-8ac2ae5e04f7\" class=\"textannotation\">PHP<\/span><\/th>\n<th>Caract\u00e9ristiques<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.1<\/td>\n<td>Tout<\/td>\n<\/tr>\n<tr>\n<td>7.2<\/td>\n<td>&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration72.new-features.php#migration72.new-features.object-type\" target=\"_blank\" rel=\"noopener noreferrer\">type <code>objet<\/code><\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration72.new-features.php#migration72.new-features.param-type-widening\" target=\"_blank\" rel=\"noopener noreferrer\">\u00e9largissement du type de param\u00e8tre<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/function.preg-match.php#refsect1-function.preg-match-parameters\" target=\"_blank\" rel=\"noopener noreferrer\">Drapeau <code>PREG_UNMATCHED_AS_NULL<\/code> dans <code>preg_match<\/code><\/a><\/td>\n<\/tr>\n<tr>\n<td>7.3<\/td>\n<td>&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.destruct-reference\" target=\"_blank\" rel=\"noopener noreferrer\">Affectations de r\u00e9f\u00e9rence dans la d\u00e9structuration des <code>list()<\/code><\/a> <em>(sauf \u00e0 l&rsquo;int\u00e9rieur de <code>foreach<\/code> &#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector\/issues\/4376\" target=\"_blank\" rel=\"noopener noreferrer\">#4376<\/a><\/em>)<br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.heredoc\" target=\"_blank\" rel=\"noopener noreferrer\">Syntaxe Heredoc et Nowdoc flexible<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.trailing-commas\" target=\"_blank\" rel=\"noopener noreferrer\">Virgules de fin dans les appels de fonctions<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.other-changes.php#migration73.other-changes.core.setcookie\" target=\"_blank\" rel=\"noopener noreferrer\"><code>set(raw)cookie<\/code> accepte l&rsquo;argument $option<\/a><\/td>\n<\/tr>\n<tr>\n<td>7.4<\/td>\n<td>&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.typed-properties\" target=\"_blank\" rel=\"noopener noreferrer\">Propri\u00e9t\u00e9s typ\u00e9es<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/functions.arrow.php\" target=\"_blank\" rel=\"noopener noreferrer\">Fonctions fl\u00e9ch\u00e9es<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.null-coalescing-assignment-operator\" target=\"_blank\" rel=\"noopener noreferrer\">Op\u00e9rateur d&rsquo;affectation \u00e0 coalescence nulle<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.unpack-inside-array\" target=\"_blank\" rel=\"noopener noreferrer\">D\u00e9ballage \u00e0 l&rsquo;int\u00e9rieur des tableaux<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.numeric-literal-separator\" target=\"_blank\" rel=\"noopener noreferrer\">S\u00e9parateur de litt\u00e9ral num\u00e9rique<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.standard.strip-tags\"><code>strip_tags()<\/code>avec un tableau de noms de balises<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.type-variance\" target=\"_blank\" rel=\"noopener noreferrer\">types de retour covariants et types de param\u00e8tres contravariants<\/a><\/td>\n<\/tr>\n<tr>\n<td>8.0<\/td>\n<td>&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/union-types\" target=\"_blank\" rel=\"noopener noreferrer\">Types d&rsquo;union<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/mixed-type\" target=\"_blank\" rel=\"noopener noreferrer\">pseudo-type <code>mixed<\/code><\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/static-return-type\" target=\"_blank\" rel=\"noopener noreferrer\">type de retour <code>static<\/code><\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/class-constant-on-objects\"><code>::class<\/code> constante magique sur les objets <\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/match-expression\"><code>match<\/code> expressions <\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/catch-exception-type\"><code>catch<\/code> exceptions uniquement par type <\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/null-safe-operator\" target=\"_blank\" rel=\"noopener noreferrer\">Op\u00e9rateur \u00e0 s\u00e9curit\u00e9 nulle<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/constructor-property-promotion\" target=\"_blank\" rel=\"noopener noreferrer\">Promotion des propri\u00e9t\u00e9s du constructeur de classe<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/trailing-comma-parameter-use-list\" target=\"_blank\" rel=\"noopener noreferrer\">Virgules de fin dans les listes de param\u00e8tres et les listes d&rsquo;<code>use<\/code> fermetures<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Transpilateurs PHP<\/h2>\n<p>Actuellement, il existe un outil pour transpiler le code PHP : <a href=\"https:\/\/github.com\/rectorphp\/rector\">Rector<\/a>.<\/p>\n<p>Rector est un outil de reconstruction de PHP, qui convertit le code PHP sur la base de r\u00e8gles programmables. On entre le code source et <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md\">l&rsquo;ensemble des r\u00e8gles<\/a> \u00e0 ex\u00e9cuter, et Rector transformera le code.<\/p>\n<p>Rector s&rsquo;utilise en ligne de commande et est install\u00e9 dans le projet via Composer. Une fois ex\u00e9cut\u00e9, Rector produira un \u00ab diff \u00bb (ajouts en vert, suppressions en rouge) du code avant et apr\u00e8s la conversion :<\/p>\n<figure style=\"width: 740px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-dry-run.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-dry-run.gif\" alt=\"Sortie \u00ab diff \u00bb de Rector\" width=\"740\" height=\"480\"><\/a><figcaption class=\"wp-caption-text\">Sortie \u00ab diff \u00bb de Rector<\/figcaption><\/figure>\n<h2>Quelle version de PHP transpiler<\/h2>\n<p>Pour transpiler du code entre plusieurs versions de PHP, il faut cr\u00e9er les r\u00e8gles correspondantes.<\/p>\n<p>Aujourd&rsquo;hui, la biblioth\u00e8que Rector comprend la plupart des r\u00e8gles pour transpiler du code dans la plage de PHP 8.0 \u00e0 7.1. Nous pouvons donc transpiler notre code PHP de mani\u00e8re fiable jusqu&rsquo;\u00e0 la version 7.1.<\/p>\n<p>Il existe \u00e9galement des <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp71\" target=\"_blank\" rel=\"noopener noreferrer\">r\u00e8gles pour transpiler de PHP 7.1 \u00e0 7.0<\/a> et <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp70\" target=\"_blank\" rel=\"noopener noreferrer\">de 7.0 \u00e0 5.6<\/a>, mais elles ne sont pas exhaustives. Des travaux sont en cours pour les compl\u00e9ter, afin que nous puissions \u00e9ventuellement transpiler du code PHP jusqu&rsquo;\u00e0 la version 5.6.<\/p>\n<h2>Transpiler vs r\u00e9troporter<\/h2>\n<p>Le r\u00e9troportage (backporting) est similaire \u00e0 la transpilation (transpiling), mais plus simple. Le r\u00e9troportage de code ne repose pas n\u00e9cessairement sur les nouvelles fonctionnalit\u00e9s d&rsquo;un langage. Au lieu de cela, la m\u00eame fonctionnalit\u00e9 peut \u00eatre fournie \u00e0 une ancienne version du langage simplement en copiant\/collant\/adaptant le code correspondant de la nouvelle version du langage.<\/p>\n<p>Par exemple, la fonction <code>str_contains<\/code> a \u00e9t\u00e9 introduite dans PHP 8.0. La m\u00eame fonction pour PHP 7.4 et les versions inf\u00e9rieures peut \u00eatre facilement mise en \u0153uvre comme ceci :<\/p>\n<pre><code class=\"language-php\">if (!defined('PHP_VERSION_ID') || (defined('PHP_VERSION_ID') && PHP_VERSION_ID &lt; 80000)) {\n  if (!function_exists('str_contains')) {\n    \/**\n     * Checks if a string contains another\n     *\n     * @param string $haystack The string to search in\n     * @param string $needle The string to search\n     * @return boolean Returns TRUE if the needle was found in haystack, FALSE otherwise.\n     *\/\n    function str_contains(string $haystack, string $needle): bool\n    {\n      return strpos($haystack, $needle) !== false;\n    }\n  }\n}<\/code><\/pre>\n<p>Comme le r\u00e9troportage est plus simple que la transpilation, nous devrions opter pour cette solution chaque fois que le r\u00e9troportage fait l&rsquo;affaire.<\/p>\n<p>En ce qui concerne la gamme entre PHP 8.0 et 7.1, nous pouvons utiliser les biblioth\u00e8ques polyfill de <a href=\"https:\/\/symfony.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Symfony<\/a>:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php71\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.1<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php72\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.2<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php73\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.3<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php74\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 7.4<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/symfony\/polyfill-php80\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyfill PHP 8.0<\/a><\/li>\n<\/ul>\n<p>Ces biblioth\u00e8ques r\u00e9troportent les fonctions, classes, constantes et interfaces suivantes :<\/p>\n<table style=\"height: 1173px\" width=\"335\">\n<thead>\n<tr>\n<th>Version de PHP<\/th>\n<th>Caract\u00e9ristiques<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.2<\/td>\n<td>Fonctions :<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/spl_object_id\" target=\"_blank\" rel=\"noopener noreferrer\"><code>spl_object_id<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/utf8_encode\" target=\"_blank\" rel=\"noopener noreferrer\"><code>utf8_encode<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/utf8_decode\" target=\"_blank\" rel=\"noopener noreferrer\"><code>utf8_decode<\/code><\/a><\/li>\n<\/ul>\n<p>Constantes :<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/reserved.constants#constant.php-float-dig\" target=\"_blank\" rel=\"noopener noreferrer\"><code>PHP_FLOAT_*<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/reserved.constants#constant.php-os-family\" target=\"_blank\" rel=\"noopener noreferrer\"><code>PHP_OS_FAMILY<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<tr>\n<td>7.3<\/td>\n<td>Fonctions :<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/array_key_first\" target=\"_blank\" rel=\"noopener noreferrer\"><code>array_key_first<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/array_key_last\" target=\"_blank\" rel=\"noopener noreferrer\"><code>array_key_last<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/function.hrtime\" target=\"_blank\" rel=\"noopener noreferrer\"><code>hrtime<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/is_countable\" target=\"_blank\" rel=\"noopener noreferrer\"><code>is_countable<\/code><\/a><\/li>\n<\/ul>\n<p>Exceptions :<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/JsonException\" target=\"_blank\" rel=\"noopener noreferrer\"><code>JsonException<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<tr>\n<td>7.4<\/td>\n<td>Fonctions :<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/get_mangled_object_vars\" target=\"_blank\" rel=\"noopener noreferrer\"><code>get_mangled_object_vars<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/mb_str_split\" target=\"_blank\" rel=\"noopener noreferrer\"><code>mb_str_split<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/password_algos\" target=\"_blank\" rel=\"noopener noreferrer\"><code>password_algos<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<tr>\n<td>8.0<\/td>\n<td>Interfaces :<\/p>\n<ul>\n<li><code>Stringable<\/code><\/li>\n<\/ul>\n<p>Classes :<\/p>\n<ul>\n<li><code>ValueError<\/code><\/li>\n<li><code>UnhandledMatchError<\/code><\/li>\n<\/ul>\n<p>Constantes :<\/p>\n<ul>\n<li><code>FILTER_VALIDATE_BOOL<\/code><\/li>\n<\/ul>\n<p>Fonctions :<\/p>\n<ul>\n<li><a href=\"https:\/\/php.net\/fdiv\" target=\"_blank\" rel=\"noopener noreferrer\"><code>fdiv<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/get_debug_type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>get_debug_type<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/preg_last_error_msg\" target=\"_blank\" rel=\"noopener noreferrer\"><code>preg_last_error_msg<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/str_contains\" target=\"_blank\" rel=\"noopener noreferrer\"><code>str_contains<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/str_starts_with\" target=\"_blank\" rel=\"noopener noreferrer\"><code>str_starts_with<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/str_ends_with\" target=\"_blank\" rel=\"noopener noreferrer\"><code>str_ends_with<\/code><\/a><\/li>\n<li><a href=\"https:\/\/php.net\/get_resource_id\" target=\"_blank\" rel=\"noopener noreferrer\"><code>get_resource_id<\/code><\/a><\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Exemples de PHP transpil\u00e9<\/h2>\n<p>Inspectons quelques exemples de code PHP transpil\u00e9, et quelques paquets qui sont enti\u00e8rement transpil\u00e9s.<\/p>\n<h3>Code PHP<\/h3>\n<p>L&rsquo;expression <code>match<\/code> a \u00e9t\u00e9 <a href=\"https:\/\/kinsta.com\/fr\/blog\/php-8\/#match-expression\">introduite dans PHP 8.0<\/a>. Ce code source :<\/p>\n<pre><code class=\"language-php\">function getFieldValue(string $fieldName): ?string\n{\n  return match($fieldName) {\n    'foo' =&gt; 'foofoo',\n    'bar' =&gt; 'barbar',\n    'baz' =&gt; 'bazbaz',\n    default =&gt; null,\n  };\n}\n<\/code><\/pre>\n<p>&#8230;sera transpil\u00e9 dans sa version \u00e9quivalente de PHP 7.4, en utilisant l&rsquo;op\u00e9rateur <code>switch<\/code>:<\/p>\n<pre><code class=\"language-php\">function getFieldValue(string $fieldName): ?string\n{\n  switch ($fieldName) {\n    case 'foo':\n      return 'foofoo';\n    case 'bar':\n      return 'barbar';\n    case 'baz':\n      return 'bazbaz';\n    default:\n      return null;\n  }\n}\n<\/code><\/pre>\n<p><a href=\"https:\/\/kinsta.com\/fr\/blog\/php-8\/#nullsafe-operator\">L&rsquo;op\u00e9rateur nullsafe<\/a> a \u00e9galement \u00e9t\u00e9 introduit dans PHP 8.0 :<\/p>\n<pre><code class=\"language-php\">public function getValue(TypeResolverInterface $typeResolver): ?string\n{\n  return $this-&gt;getResolver($typeResolver)?-&gt;getValue();\n}\n<\/code><\/pre>\n<p>Le code transpil\u00e9 doit d&rsquo;abord affecter la valeur de l&rsquo;op\u00e9ration \u00e0 une nouvelle variable, afin d&rsquo;\u00e9viter d&rsquo;ex\u00e9cuter l&rsquo;op\u00e9ration deux fois :<\/p>\n<pre><code class=\"language-php\">public function getValue(TypeResolverInterface $typeResolver): ?string\n{\n  return ($val = $this-&gt;getResolver($typeResolver)) ? $val-&gt;getValue() : null;\n}\n<\/code><\/pre>\n<p>La fonction de <a href=\"https:\/\/kinsta.com\/fr\/blog\/php-8\/#constructor-property-promotion\">promotion des propri\u00e9t\u00e9s des constructeurs<\/a>, \u00e9galement introduite dans PHP 8.0, permet aux d\u00e9veloppeurs d&rsquo;\u00e9crire moins de code :<\/p>\n<pre><code class=\"language-php\">class QueryResolver\n{\n  function __construct(protected QueryFormatter $queryFormatter)\n  {\n  }\n}\n<\/code><\/pre>\n<p>En le transpilant pour PHP 7.4, le morceau de code complet est produit :<\/p>\n<pre><code class=\"language-php\"> class QueryResolver\n {\n  protected QueryFormatter $queryFormatter;\n\n  function __construct(QueryFormatter $queryFormatter)\n  {\n    $this-&gt;queryFormatter = $queryFormatter;\n  }\n}\n<\/code><\/pre>\n<p>Le code transpil\u00e9 ci-dessus contient des <a href=\"https:\/\/kinsta.com\/fr\/blog\/php-7-4\/#typed-properties\">propri\u00e9t\u00e9s typ\u00e9es<\/a>, qui ont \u00e9t\u00e9 introduites dans PHP 7.4. En transpilant ce code en PHP 7.3, vous les remplacez par des commentaires (docblocks) :<\/p>\n<pre><code class=\"language-php\"> class QueryResolver\n {\n  \/**\n   * @var QueryFormatter\n   *\/\n  protected $queryFormatter;\n\n  function __construct(QueryFormatter $queryFormatter)\n  {\n    $this-&gt;queryFormatter = $queryFormatter;\n  }\n}\n<\/code><\/pre>\n<h3>Paquets PHP<\/h3>\n<p>Les biblioth\u00e8ques suivantes sont en cours de transpilation pour la production :<\/p>\n<table>\n<thead>\n<tr>\n<th>Biblioth\u00e8que\/description<\/th>\n<th>Code\/notes<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/getrector.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Rector<\/a><br \/>\nOutil de reconstruction de PHP qui rend la transpilation possible<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector-src\" target=\"_blank\" rel=\"noopener noreferrer\">Code source<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector\" target=\"_blank\" rel=\"noopener noreferrer\">Code transpil\u00e9<\/a><br \/>\n&#8211; <a href=\"https:\/\/getrector.org\/blog\/prefixed-rector-by-default\" target=\"_blank\" rel=\"noopener noreferrer\">Notes<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/tomasvotruba.com\/blog\/2017\/05\/03\/combine-power-of-php-code-sniffer-and-php-cs-fixer-in-3-lines\/\" target=\"_blank\" rel=\"noopener noreferrer\">Normes de codage faciles<\/a><br \/>\nOutil pour que le code PHP adh\u00e8re \u00e0 un ensemble de r\u00e8gles<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/symplify\/symplify\/tree\/main\/packages\/easy-coding-standard\" target=\"_blank\" rel=\"noopener noreferrer\">Code source<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/symplify\/easy-coding-standard\" target=\"_blank\" rel=\"noopener noreferrer\">Code transpil\u00e9<\/a><br \/>\n&#8211; <a href=\"https:\/\/tomasvotruba.com\/blog\/introducing-ecs-prefixed-and-downgraded-to-php-71\/\" target=\"_blank\" rel=\"noopener noreferrer\">Notes<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/graphql-api.com\" target=\"_blank\" rel=\"noopener noreferrer\">API GraphQL pour WordPress<\/a><br \/>\nExtension fournissant un serveur GraphQL pour WordPress<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/leoloso\/PoP\/tree\/master\/layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\" target=\"_blank\" rel=\"noopener noreferrer\">Code source<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp-dist\/\" target=\"_blank\" rel=\"noopener noreferrer\">Code transpil\u00e9<\/a><br \/>\n&#8211; <a href=\"https:\/\/graphql-api.com\/blog\/the-plugin-is-now-transpiled-from-php-80-to-71\/\" target=\"_blank\" rel=\"noopener noreferrer\">Notes<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Avantages et inconv\u00e9nients de la transpilation PHP<\/h2>\n<p>L&rsquo;avantage de transpiler PHP a d\u00e9j\u00e0 \u00e9t\u00e9 d\u00e9crit : cela permet au code source d&rsquo;utiliser PHP 8.0 (c&rsquo;est-\u00e0-dire la derni\u00e8re version de PHP), qui sera transform\u00e9 en une version inf\u00e9rieure de PHP pour la production afin de fonctionner dans une application ou un environnement h\u00e9rit\u00e9.<\/p>\n<p>Cela nous permet effectivement de devenir de meilleurs d\u00e9veloppeurs, en produisant un code de meilleure qualit\u00e9. En effet, notre code source peut utiliser les types d&rsquo;union de PHP 8.0, les propri\u00e9t\u00e9s typ\u00e9es de PHP 7.4 et les diff\u00e9rents types et pseudo-types ajout\u00e9s \u00e0 chaque nouvelle version de PHP(<code>mixed<\/code> de PHP 8.0, <code>object<\/code> de PHP 7.2), entre autres fonctionnalit\u00e9s modernes de PHP.<\/p>\n<p>Gr\u00e2ce \u00e0 ces fonctionnalit\u00e9s, nous pouvons mieux attraper les bogues pendant le d\u00e9veloppement et \u00e9crire du code plus facile \u00e0 lire.<\/p>\n<p>Maintenant, voyons les inconv\u00e9nients.<\/p>\n<h3>Il doit \u00eatre cod\u00e9 et entretenu<\/h3>\n<p>Rector peut transpiler du code automatiquement, mais le processus n\u00e9cessitera probablement une saisie manuelle pour le faire fonctionner avec notre configuration sp\u00e9cifique.<\/p>\n<h3>Les biblioth\u00e8ques tierces doivent aussi \u00eatre transpil\u00e9es<\/h3>\n<p>Cela devient un probl\u00e8me lorsque leur transpilation produit des erreurs car nous devons alors nous plonger dans leur code source pour trouver la raison possible. Si le probl\u00e8me peut \u00eatre r\u00e9solu et que le projet est open source, nous devrons envoyer une \u00ab pull request \u00bb. Si la biblioth\u00e8que n&rsquo;est pas open source, nous risquons de nous heurter \u00e0 un obstacle.<\/p>\n<h3>Rector ne nous informe pas lorsque le code ne peut pas \u00eatre transpil\u00e9<\/h3>\n<p>Si le code source contient des attributs PHP 8.0 ou toute autre fonctionnalit\u00e9 qui ne peut pas \u00eatre transpil\u00e9e, nous ne pouvons pas continuer. Cependant, Rector ne v\u00e9rifie pas cette condition, nous devons donc le faire manuellement. Ce n&rsquo;est peut-\u00eatre pas un gros probl\u00e8me concernant notre propre code source puisque nous le connaissons d\u00e9j\u00e0, mais cela pourrait devenir un obstacle concernant les d\u00e9pendances tierces.<\/p>\n<h3>Les informations de d\u00e9bogage utilisent le code transpil\u00e9, pas le code source<\/h3>\n<p>Lorsque l&rsquo;application produit un message d&rsquo;erreur avec une trace de pile en production, le num\u00e9ro de ligne pointe vers le code transpil\u00e9. Nous devons reconvertir le code transpil\u00e9 en code original pour trouver le num\u00e9ro de ligne correspondant dans le code source.<\/p>\n<h3>Le code transpil\u00e9 doit aussi \u00eatre pr\u00e9fix\u00e9<\/h3>\n<p>Notre projet transpil\u00e9 et une autre biblioth\u00e8que \u00e9galement install\u00e9e dans l&rsquo;environnement de production pourraient utiliser la m\u00eame d\u00e9pendance tierce. Cette d\u00e9pendance tierce sera transpil\u00e9e pour notre projet et conservera son code source original pour l&rsquo;autre biblioth\u00e8que. Par cons\u00e9quent, la version transpil\u00e9e doit \u00eatre pr\u00e9fix\u00e9e via <a href=\"https:\/\/github.com\/humbug\/php-scoper\" target=\"_blank\" rel=\"noopener noreferrer\">PHP-Scoper<\/a>, <a href=\"https:\/\/github.com\/BrianHenryIE\/strauss\" target=\"_blank\" rel=\"noopener noreferrer\">Strauss<\/a> ou un autre outil pour \u00e9viter les conflits potentiels.<\/p>\n<h3>La transpilation doit avoir lieu pendant l&rsquo;int\u00e9gration continue (IC)<\/h3>\n<p>Comme le code transpil\u00e9 remplacera naturellement le code source, nous ne devons pas ex\u00e9cuter le processus de transpilation sur nos ordinateurs de d\u00e9veloppement, car nous risquerions de cr\u00e9er des effets secondaires. Ex\u00e9cuter le processus pendant une ex\u00e9cution de l&rsquo;IC est plus appropri\u00e9 (plus d&rsquo;informations \u00e0 ce sujet ci-dessous).<\/p>\n<h2>Comment transpiler PHP<\/h2>\n<p>Tout d&rsquo;abord, nous devons installer Rector dans notre projet de d\u00e9veloppement :<\/p>\n<pre><code class=\"language-bash\">composer require rector\/rector --dev\n<\/code><\/pre>\n<p>Nous cr\u00e9ons ensuite un fichier de configuration <code>rector.php<\/code> dans le r\u00e9pertoire racine du projet contenant les ensembles de r\u00e8gles n\u00e9cessaires. Pour r\u00e9trograder le code de PHP 8.0 \u00e0 7.1, nous utilisons cette configuration :<\/p>\n<pre><code class=\"language-php\">use Rector\\Set\\ValueObject\\DowngradeSetList;\nuse Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator;\n\nreturn static function (ContainerConfigurator $containerConfigurator): void {\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_80);\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_74);\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_73);\n    $containerConfigurator-&gt;import(DowngradeSetList::PHP_72);\n};\n<\/code><\/pre>\n<p>Pour nous assurer que le processus s&rsquo;ex\u00e9cute comme pr\u00e9vu, nous pouvons ex\u00e9cuter la commande <code>process<\/code> de Rector en mode sec, en passant le ou les emplacements \u00e0 traiter (dans ce cas, tous les fichiers du dossier <code>src\/<\/code>) :<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src --dry-run\n<\/code><\/pre>\n<p>Pour effectuer la transpilation, nous ex\u00e9cutons la commande <code>process<\/code> de Rector, qui modifiera les fichiers dans leur emplacement actuel :<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src\n<\/code><\/pre>\n<p>Remarque : si nous ex\u00e9cutons <code>rector process<\/code> sur nos ordinateurs de d\u00e9veloppement, le code source sera converti sur place, sous <code>src\/<\/code>. Cependant, nous voulons produire le code converti dans un emplacement diff\u00e9rent pour ne pas \u00e9craser le code source lors de la r\u00e9trogradation du code. C&rsquo;est pour cette raison que l&rsquo;ex\u00e9cution du processus convient le mieux pendant l&rsquo;int\u00e9gration continue.<\/p>\n<h2>Optimiser le processus de transpilation<\/h2>\n<p>Pour g\u00e9n\u00e9rer un livrable transpil\u00e9 pour la production, seul le code destin\u00e9 \u00e0 la production doit \u00eatre converti ; le code n\u00e9cessaire uniquement au d\u00e9veloppement peut \u00eatre ignor\u00e9. Cela signifie que nous pouvons \u00e9viter de transpiler tous les tests (pour notre projet et ses d\u00e9pendances) et toutes les d\u00e9pendances pour le d\u00e9veloppement.<\/p>\n<p>Concernant les tests, nous savons d\u00e9j\u00e0 o\u00f9 se trouvent ceux de notre projet &#8211; par exemple, dans le r\u00e9pertoire <code>tests\/<\/code>. Nous devons \u00e9galement d\u00e9couvrir o\u00f9 se trouvent ceux des d\u00e9pendances &#8211; par exemple, dans leurs sous-r\u00e9pertoires <code>tests\/<\/code>, <code>test\/<\/code> et <code>Test\/<\/code> (pour diff\u00e9rentes biblioth\u00e8ques). Ensuite, nous disons \u00e0 Rector de ne pas traiter ces r\u00e9pertoires :<\/p>\n<pre><code class=\"language-php\">return static function (ContainerConfigurator $containerConfigurator): void {\n  \/\/ ...\n\n  $parameters-&gt;set(Option::SKIP, [\n    \/\/ Skip tests\n    '*\/tests\/*',\n    '*\/test\/*',\n    '*\/Test\/*',\n  ]);\n};\n<\/code><\/pre>\n<p>Concernant les d\u00e9pendances, Composer sait lesquelles sont pour le d\u00e9veloppement (celles sous l&rsquo;entr\u00e9e <code>require-dev<\/code> dans <code>composer.json<\/code>) et lesquelles sont pour la production (celles sous l&rsquo;entr\u00e9e <code>require<\/code>). Pour r\u00e9cup\u00e9rer aupr\u00e8s de Composer les chemins de toutes les d\u00e9pendances pour la production, nous ex\u00e9cutons :<\/p>\n<pre><code class=\"language-bash\">composer info --path --no-dev\n<\/code><\/pre>\n<p>Cette commande produira une liste de d\u00e9pendances avec leur nom et leur chemin, comme ceci :<\/p>\n<pre><code>brain\/cortex                     \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/brain\/cortex\ncomposer\/installers              \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/composer\/installers\ncomposer\/semver                  \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/composer\/semver\nguzzlehttp\/guzzle                \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/guzzlehttp\/guzzle\nleague\/pipeline                  \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/league\/pipeline\n<\/code><\/pre>\n<p>Nous pouvons extraire tous les chemins et les introduire dans la commande Rector, qui traitera ensuite le r\u00e9pertoire <code>src\/<\/code> de notre projet plus les r\u00e9pertoires contenant toutes les d\u00e9pendances pour la production :<\/p>\n<pre><code class=\"language-bash\">$ paths=\"$(composer info --path --no-dev | cut -d' ' -f2- | sed 's\/ \/\/g' | tr '\\n' ' ')\"\n$ vendor\/bin\/rector process src $paths\n<\/code><\/pre>\n<p>Une autre am\u00e9lioration peut emp\u00eacher Rector de traiter les d\u00e9pendances qui utilisent d\u00e9j\u00e0 la version PHP cible. Si une biblioth\u00e8que a \u00e9t\u00e9 cod\u00e9e avec PHP 7.1 (ou toute version inf\u00e9rieure), il n&rsquo;est pas n\u00e9cessaire de la transpiler vers PHP 7.1.<\/p>\n<p>Pour cela, nous pouvons obtenir la liste des biblioth\u00e8ques n\u00e9cessitant PHP 7.2 et plus et ne traiter que celles-l\u00e0. Nous obtiendrons les noms de toutes ces biblioth\u00e8ques via la commande <code>why-not<\/code> de Composer, comme ceci :<\/p>\n<pre><code class=\"language-bash\">composer why-not php \"7.1.*\" | grep -o \"\\S*\\\/\\S*\"\n<\/code><\/pre>\n<p>Comme cette commande ne fonctionne pas avec le drapeau <code>--no-dev<\/code>, pour inclure uniquement les d\u00e9pendances pour la production, nous devons d&rsquo;abord supprimer les d\u00e9pendances pour le d\u00e9veloppement et r\u00e9g\u00e9n\u00e9rer l&rsquo;autoloader, ex\u00e9cuter la commande, puis les ajouter \u00e0 nouveau :<\/p>\n<pre><code class=\"language-bash\">$ composer install --no-dev\n$ packages=$(composer why-not php \"7.1.*\" | grep -o \"\\S*\\\/\\S*\")\n$ composer install\n<\/code><\/pre>\n<p>La commande <code>info --path<\/code> de Composer r\u00e9cup\u00e8re le chemin d&rsquo;acc\u00e8s d&rsquo;un paquet, avec ce format :<\/p>\n<pre><code class=\"language-bash\"># Executing this command\n$ composer info psr\/cache --path   \n# Produces this response:\npsr\/cache \/Users\/leo\/GitHub\/leoloso\/PoP\/vendor\/psr\/cache\n<\/code><\/pre>\n<p>Nous ex\u00e9cutons cette commande pour tous les \u00e9l\u00e9ments de notre liste afin d&rsquo;obtenir tous les chemins d&rsquo;acc\u00e8s \u00e0 transpiler :<\/p>\n<pre><code class=\"language-bash\">for package in $packages\ndo\n  path=$(composer info $package --path | cut -d' ' -f2-)\n  paths=\"$paths $path\"\ndone\n<\/code><\/pre>\n<p>Enfin, nous fournissons cette liste \u00e0 Rector (ainsi que le r\u00e9pertoire <code>src\/<\/code> du projet) :<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src $paths\n<\/code><\/pre>\n<h2>Pi\u00e8ges \u00e0 \u00e9viter lorsque vous transpilez du code<\/h2>\n<p>Transpiler du code peut \u00eatre consid\u00e9r\u00e9 comme un art, n\u00e9cessitant souvent des ajustements sp\u00e9cifiques au projet. Voyons quelques probl\u00e8mes que nous pouvons rencontrer.<\/p>\n<h3>Les r\u00e8gles encha\u00een\u00e9es ne sont pas toujours trait\u00e9es<\/h3>\n<p>Une r\u00e8gle encha\u00een\u00e9e, c&rsquo;est lorsqu&rsquo;une r\u00e8gle doit convertir le code produit par une r\u00e8gle pr\u00e9c\u00e9dente. Par exemple, la biblioth\u00e8que <code>symfony\/cache<\/code> contient <a href=\"https:\/\/github.com\/symfony\/cache\/blob\/be5707f\/CacheItem.php#L115\" target=\"_blank\" rel=\"noopener noreferrer\">ce code<\/a>:<\/p>\n<pre><code class=\"language-php\">final class CacheItem implements ItemInterface\n{\n  public function tag($tags): ItemInterface\n  {\n    \/\/ ...\n    return $this;\n  }\n}\n<\/code><\/pre>\n<p>Lors de la transpilation de PHP 7.4 \u00e0 7.3, la fonction <code>tag<\/code> doit subir deux modifications :<\/p>\n<ul>\n<li>Le type de retour <code>ItemInterface<\/code> doit d&rsquo;abord \u00eatre converti en <code>self<\/code>, en raison de la r\u00e8gle <a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeCovariantReturnTypeRector.php\"><code>DowngradeCovariantReturnTypeRector<\/code><\/a><\/li>\n<li>Le type de retour <code>self<\/code> doit ensuite \u00eatre supprim\u00e9, conform\u00e9ment \u00e0 la r\u00e8gle <a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeSelfTypeDeclarationRector.php\"><code>DowngradeSelfTypeDeclarationRector<\/code><\/a><\/li>\n<\/ul>\n<p>Le r\u00e9sultat final devrait \u00eatre celui-ci :<\/p>\n<pre><code class=\"language-php\">final class CacheItem implements ItemInterface\n{\n  public function tag($tags)\n  {\n    \/\/ ...\n    return $this;\n  }\n}\n<\/code><\/pre>\n<p>Cependant, Rector ne produit que l&rsquo;\u00e9tape interm\u00e9diaire :<\/p>\n<pre><code class=\"language-php\">final class CacheItem implements ItemInterface\n{\n  public function tag($tags): self\n  {\n    \/\/ ...\n    return $this;\n  }\n}\n<\/code><\/pre>\n<p>Le probl\u00e8me est que <a href=\"https:\/\/github.com\/rectorphp\/rector\/issues\/5962\" target=\"_blank\" rel=\"noopener noreferrer\">Rector ne peut pas toujours contr\u00f4ler l&rsquo;ordre dans lequel les r\u00e8gles sont appliqu\u00e9es<\/a>.<\/p>\n<p>La solution consiste \u00e0 identifier les r\u00e8gles encha\u00een\u00e9es qui n&rsquo;ont pas \u00e9t\u00e9 trait\u00e9es et \u00e0 lancer une nouvelle ex\u00e9cution de Rector pour les appliquer.<\/p>\n<p>Pour identifier les r\u00e8gles encha\u00een\u00e9es, nous ex\u00e9cutons Rector deux fois sur le code source, comme ceci :<\/p>\n<pre><code class=\"language-bash\">$ vendor\/bin\/rector process src\n$ vendor\/bin\/rector process src --dry-run\n<\/code><\/pre>\n<p>La premi\u00e8re fois, nous ex\u00e9cutons Rector comme pr\u00e9vu, pour ex\u00e9cuter la transpilation. La deuxi\u00e8me fois, nous utilisons le drapeau <code>--dry-run<\/code> pour d\u00e9couvrir s&rsquo;il y a encore des modifications \u00e0 apporter. S&rsquo;il y en a, la commande sortira avec un code d&rsquo;erreur et la sortie \u00ab diff \u00bb indiquera quelle(s) r\u00e8gle(s) peut (peuvent) encore \u00eatre appliqu\u00e9e(s). Cela signifierait que la premi\u00e8re ex\u00e9cution n&rsquo;\u00e9tait pas compl\u00e8te, certaines r\u00e8gles encha\u00een\u00e9es n&rsquo;ayant pas \u00e9t\u00e9 trait\u00e9es.<\/p>\n<figure style=\"width: 901px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-run-dry.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/kinsta.com\/wp-content\/uploads\/2021\/07\/rector-process-run-dry.png\" alt=\"Ex\u00e9cution de Rector avec le drapeau --dry-run\" width=\"901\" height=\"903\"><\/a><figcaption class=\"wp-caption-text\">Ex\u00e9cution de Rector avec le drapeau &#8211;dry-run<\/figcaption><\/figure>\n<p>Une fois que nous avons identifi\u00e9 la (ou les) r\u00e8gle(s) cha\u00een\u00e9e(s) non appliqu\u00e9e(s), nous pouvons alors cr\u00e9er un autre fichier de configuration Rector &#8211; par exemple, <code>rector-chained-rule.php<\/code> ex\u00e9cutera la r\u00e8gle manquante. Au lieu de traiter un ensemble complet de r\u00e8gles pour tous les fichiers sous <code>src\/<\/code>, cette fois, nous pouvons ex\u00e9cuter la r\u00e8gle sp\u00e9cifique manquante sur le fichier sp\u00e9cifique o\u00f9 elle doit \u00eatre appliqu\u00e9e :<\/p>\n<pre><code class=\"language-php\">\/\/ rector-chained-rule.php\nuse Rector\\Core\\Configuration\\Option;\nuse Rector\\DowngradePhp74\\Rector\\ClassMethod\\DowngradeSelfTypeDeclarationRector;\nuse Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator;\n\nreturn static function (ContainerConfigurator $containerConfigurator): void {\n  $services = $containerConfigurator-&gt;services();\n  $services-&gt;set(DowngradeSelfTypeDeclarationRector::class);\n\n  $parameters = $containerConfigurator-&gt;parameters();\n  $parameters-&gt;set(Option::PATHS, [\n    __DIR__ . '\/vendor\/symfony\/cache\/CacheItem.php',\n  ]);\n};\n<\/code><\/pre>\n<p>Enfin, nous indiquons \u00e0 Rector lors de son deuxi\u00e8me passage d&rsquo;utiliser le nouveau fichier de configuration via l&rsquo;entr\u00e9e <code>--config<\/code>:<\/p>\n<pre><code class=\"language-bash\"># First pass with all modifications\n$ vendor\/bin\/rector process src\n\n# Second pass to fix a specific problem\n$ vendor\/bin\/rector process --config=rector-chained-rule.php\n<\/code><\/pre>\n<h3>Les d\u00e9pendances de Composer peuvent \u00eatre incoh\u00e9rentes<\/h3>\n<p>Les biblioth\u00e8ques peuvent d\u00e9clarer qu&rsquo;une d\u00e9pendance est destin\u00e9e au d\u00e9veloppement (c&rsquo;est-\u00e0-dire sous <code>require-dev<\/code> dans <code>composer.json<\/code>), et pourtant, r\u00e9f\u00e9rencer du code provenant d&rsquo;elles pour la production (comme sur certains fichiers sous <code>src\/<\/code>, et non <code>tests\/<\/code>).<\/p>\n<p>En g\u00e9n\u00e9ral, ce n&rsquo;est pas un probl\u00e8me car ce code ne sera pas charg\u00e9 en production et il n&rsquo;y aura donc jamais d&rsquo;erreur sur l&rsquo;application. Cependant, lorsque Rector traite le code source et ses d\u00e9pendances, il valide que tout le code r\u00e9f\u00e9renc\u00e9 peut \u00eatre charg\u00e9. Rector lancera une erreur si un fichier fait r\u00e9f\u00e9rence \u00e0 un morceau de code d&rsquo;une biblioth\u00e8que non install\u00e9e (parce qu&rsquo;il a \u00e9t\u00e9 d\u00e9clar\u00e9 n\u00e9cessaire pour le d\u00e9veloppement uniquement).<\/p>\n<p>Par exemple, la classe <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/8f03a1f\/src\/Symfony\/Component\/Cache\/Messenger\/EarlyExpirationHandler.php\"><code>EarlyExpirationHandler<\/code><\/a> du composant Cache de Symfony impl\u00e9mente l&rsquo;interface <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/191cb52\/src\/Symfony\/Component\/Messenger\/Handler\/MessageHandlerInterface.php\"><code>MessageHandlerInterface<\/code><\/a> du composant Messenger :<\/p>\n<pre><code class=\"language-php\">class EarlyExpirationHandler implements MessageHandlerInterface\n{\n    \/\/...\n}\n<\/code><\/pre>\n<p>Cependant, <code>symfony\/cache<\/code> d\u00e9clare que <code>symfony\/messenger<\/code> est une <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/6fe82d8\/src\/Symfony\/Component\/Cache\/composer.json#L43\" target=\"_blank\" rel=\"noopener noreferrer\">d\u00e9pendance pour le d\u00e9veloppement<\/a>. Ensuite, lorsque vous ex\u00e9cutez Rector sur un projet qui d\u00e9pend de <code>symfony\/cache<\/code>, il lancera une erreur :<\/p>\n<pre><code>[ERROR] Could not process \"vendor\/symfony\/cache\/Messenger\/EarlyExpirationHandler.php\" file, due to:             \n  \"Analyze error: \"Class Symfony\\Component\\Messenger\\Handler\\MessageHandlerInterface not found.\". Include your files in \"$parameters-&gt;set(Option::AUTOLOAD_PATHS, [...]);\" in \"rector.php\" config.\n  See https:\/\/github.com\/rectorphp\/rector#configuration\".   \n<\/code><\/pre>\n<p>Il existe trois solutions \u00e0 ce probl\u00e8me :<\/p>\n<ol>\n<li>Dans la configuration de Rector, ignorez le traitement du fichier qui fait r\u00e9f\u00e9rence \u00e0 ce morceau de code :<\/li>\n<\/ol>\n<pre><code class=\"language-php\">return static function (ContainerConfigurator $containerConfigurator): void {\n  \/\/ ...\n\n  $parameters-&gt;set(Option::SKIP, [\n    __DIR__ . '\/vendor\/symfony\/cache\/Messenger\/EarlyExpirationHandler.php',\n  ]);\n};<\/code><\/pre>\n<ol start=\"2\">\n<li>T\u00e9l\u00e9chargez la biblioth\u00e8que manquante et ajoutez son chemin pour qu&rsquo;elle soit charg\u00e9e automatiquement par Rector :<\/li>\n<\/ol>\n<pre><code class=\"language-php\">return static function (ContainerConfigurator $containerConfigurator): void {\n  \/\/ ...\n\n  $parameters-&gt;set(Option::AUTOLOAD_PATHS, [\n    __DIR__ . '\/vendor\/symfony\/messenger',\n  ]);\n};\n<\/code><\/pre>\n<ol start=\"3\">\n<li>Fa\u00eetes en sorte que votre projet d\u00e9pende de la biblioth\u00e8que manquante pour la production :<\/li>\n<\/ol>\n<pre><code class=\"language-bash\">composer require symfony\/messenger\n<\/code><\/pre>\n<h2>Transpilation et int\u00e9gration continue<\/h2>\n<p>Comme mentionn\u00e9 pr\u00e9c\u00e9demment, dans nos ordinateurs de d\u00e9veloppement, nous devons utiliser le drapeau <code>--dry-run<\/code> lorsque nous ex\u00e9cutons Rector, sinon le code source sera remplac\u00e9 par le code transpil\u00e9. Pour cette raison, il est plus appropri\u00e9 d&rsquo;ex\u00e9cuter le processus de transpilation r\u00e9el pendant l&rsquo;int\u00e9gration continue (IC), o\u00f9 nous pouvons faire tourner des lanceurs temporaires pour ex\u00e9cuter le processus.<\/p>\n<p>Un moment id\u00e9al pour ex\u00e9cuter le processus de transpilation est la g\u00e9n\u00e9ration de la version de notre projet. Par exemple, le <a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp\/blob\/3f9fd52ddf318b9fbb33653de8acfec8dc4b4665\/.github\/workflows\/main.yml\" target=\"_blank\" rel=\"noopener noreferrer\">code ci-dessous<\/a> est un flux de travail pour <a href=\"https:\/\/github.com\/features\/actions\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Actions<\/a>, qui cr\u00e9e la version d&rsquo;une extension WordPress :<\/p>\n<pre><code class=\"language-yml\">name: Generate Installable Plugin and Upload as Release Asset\non:\n  release:\n    types: [published]\njobs:\n  build:\n    name: Build, Downgrade and Upload Release\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v2\n      - name: Downgrade code for production (to PHP 7.1)\n        run: |\n          composer install\n          vendor\/bin\/rector process\n          sed -i 's\/Requires PHP: 7.4\/Requires PHP: 7.1\/' graphql-api.php\n      - name: Build project for production\n        run: |\n          composer install --no-dev --optimize-autoloader\n          mkdir build\n      - name: Create artifact\n        uses: montudor\/action-zip@v0.1.0\n        with:\n          args: zip -X -r build\/graphql-api.zip . -x *.git* node_modules\/\\* .* \"*\/\\.*\" CODE_OF_CONDUCT.md CONTRIBUTING.md ISSUE_TEMPLATE.md PULL_REQUEST_TEMPLATE.md rector.php *.dist composer.* dev-helpers** build**\n      - name: Upload artifact\n        uses: actions\/upload-artifact@v2\n        with:\n            name: graphql-api\n            path: build\/graphql-api.zip\n      - name: Upload to release\n        uses: JasonEtco\/upload-to-release@master\n        with:\n          args: build\/graphql-api.zip application\/zip\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n<p>Ce flux de travail contient une proc\u00e9dure standard pour <a href=\"https:\/\/leoloso.com\/posts\/github-action-to-release-wp-plugin\/\" target=\"_blank\" rel=\"noopener noreferrer\">publier une extension WordPress via GitHub Actions<\/a>. Le nouvel ajout, qui consiste \u00e0 transpiler le code du plugin de PHP 7.4 \u00e0 7.1, se produit \u00e0 cette \u00e9tape :<\/p>\n<pre><code class=\"language-yml\">      - name: Downgrade code for production (to PHP 7.1)\n        run: |\n          vendor\/bin\/rector process\n          sed -i 's\/Requires PHP: 7.4\/Requires PHP: 7.1\/' graphql-api.php\n<\/code><\/pre>\n<p>Pris ensemble, ce flux de travail effectue maintenant les \u00e9tapes suivantes :<\/p>\n<ol>\n<li>V\u00e9rifier le code source d&rsquo;une extension WordPress dans son d\u00e9p\u00f4t, \u00e9crit avec PHP 7.4<\/li>\n<li>Installer ses d\u00e9pendances Composer<\/li>\n<li>Transpiler son code de PHP 7.4 \u00e0 7.1<\/li>\n<li>Modifier l&rsquo;entr\u00e9e \u00ab N\u00e9cessite PHP \u00bb dans l&rsquo;en-t\u00eate du fichier principal de l&rsquo;extension de <code>\u00ab 7.4 \u00bb<\/code> \u00e0 <code>\u00ab 7.1 \u00bb<\/code><\/li>\n<li>Supprimer les d\u00e9pendances n\u00e9cessaires au d\u00e9veloppement<\/li>\n<li>Cr\u00e9er le fichier .zip de l&rsquo;extension, en excluant tous les fichiers inutiles<\/li>\n<li>T\u00e9l\u00e9versez le fichier .zip en tant que ressource de la version (et, en plus, en tant qu&rsquo;artefact sur l&rsquo;action GitHub)<\/li>\n<\/ol>\n<h2>Tester le code transpil\u00e9<\/h2>\n<p>Une fois que le code a \u00e9t\u00e9 transpil\u00e9 en PHP 7.1, comment savoir s&rsquo;il fonctionne bien ? Ou, en d&rsquo;autres termes, comment savons-nous qu&rsquo;il a \u00e9t\u00e9 enti\u00e8rement converti et qu&rsquo;aucun vestige de versions sup\u00e9rieures du code PHP n&rsquo;a \u00e9t\u00e9 laiss\u00e9 derri\u00e8re lui ?<\/p>\n<p>Comme pour la transpilation du code, nous pouvons mettre en \u0153uvre la solution dans le cadre d&rsquo;un processus d&rsquo;int\u00e9gration continue. L&rsquo;id\u00e9e est de configurer l&rsquo;environnement du lanceur avec PHP 7.1 et d&rsquo;ex\u00e9cuter un linter sur le code transpil\u00e9. Si un morceau de code n&rsquo;est pas compatible avec PHP 7.1 (comme une propri\u00e9t\u00e9 typ\u00e9e de PHP 7.4 qui n&rsquo;a pas \u00e9t\u00e9 convertie), le linter lancera une erreur.<\/p>\n<p>Un linter pour PHP qui fonctionne bien est <a href=\"https:\/\/github.com\/php-parallel-lint\/PHP-Parallel-Lint\" target=\"_blank\" rel=\"noopener noreferrer\">PHP Parallel Lint<\/a>. Nous pouvons installer cette biblioth\u00e8que en tant que d\u00e9pendance pour le d\u00e9veloppement dans notre projet, ou demander au processus IC de l&rsquo;installer en tant que projet Composer autonome :<\/p>\n<pre><code class=\"language-bash\">composer create-project php-parallel-lint\/php-parallel-lint\n<\/code><\/pre>\n<p>Lorsque le code contient PHP 7.2 et plus, PHP Parallel Lint affiche une erreur comme <a href=\"https:\/\/github.com\/leoloso\/PoP\/runs\/2751846434?check_suite_focus=true\" target=\"_blank\" rel=\"noopener noreferrer\">celle-ci<\/a>:<\/p>\n<pre><code>Run php-parallel-lint\/parallel-lint layers\/ vendor\/ --exclude vendor\/symfony\/polyfill-ctype\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-grapheme\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-idn\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-normalizer\/bootstrap80.php --exclude vendor\/symfony\/polyfill-mbstring\/bootstrap80.php\nPHP 7.1.33 | 10 parallel jobs\n............................................................   60\/2870 (2 %)\n............................................................  120\/2870 (4 %)\n...\n............................................................  660\/2870 (22 %)\n.............X..............................................  720\/2870 (25 %)\n............................................................  780\/2870 (27 %)\n...\n............................................................ 2820\/2870 (98 %)\n..................................................           2870\/2870 (100 %)\n\n\nChecked 2870 files in 15.4 seconds\nSyntax error found in 1 file\n\n------------------------------------------------------------\nParse error: layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\/graphql-api.php:55\n    53|     '0.8.0',\n    54|     \\__('GraphQL API for WordPress', 'graphql-api'),\n  &gt; 55| ))) {\n    56|     $plugin-&gt;setup();\n    57| }\nUnexpected ')' in layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\/graphql-api.php on line 55\nError: Process completed with exit code 1.\n<\/code><\/pre>\n<p>Ajoutons le linter dans le flux de travail de notre CI. Les \u00e9tapes \u00e0 ex\u00e9cuter pour transpiler le code de PHP 8.0 \u00e0 7.1 et le tester sont les suivantes :<\/p>\n<ol>\n<li>V\u00e9rifier le code source<\/li>\n<li>Fare en sorte que l&rsquo;environnement ex\u00e9cute PHP 8.0, afin que Rector puisse interpr\u00e9ter le code source<\/li>\n<li>Transpiler le code en PHP 7.1<\/li>\n<li>Installer l&rsquo;outil PHP linter<\/li>\n<li>Passer la version PHP de l&rsquo;environnement \u00e0 7.1<\/li>\n<li>Ex\u00e9cuter l&rsquo;outil linter sur le code transpil\u00e9<\/li>\n<\/ol>\n<p>Ce <a href=\"https:\/\/github.com\/leoloso\/PoP\/blob\/b93d3e35cb59e0281b45899fd82231c3d8cbbe25\/.github\/workflows\/downgrade_php_tests.yml\" target=\"_blank\" rel=\"noopener noreferrer\">flux de travail GitHub Action<\/a> fait le travail :<\/p>\n<pre><code class=\"language-yaml\">name: Downgrade PHP tests\njobs:\n  main:\n    name: Downgrade code to PHP 7.1 via Rector, and execute tests\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v2\n\n      - name: Set-up PHP\n        uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 8.0\n          coverage: none\n\n      - name: Local packages - Downgrade PHP code via Rector\n        run: |\n          composer install\n          vendor\/bin\/rector process\n\n      # Prepare for testing on PHP 7.1\n      - name: Install PHP Parallel Lint\n        run: composer create-project php-parallel-lint\/php-parallel-lint --ansi\n\n      - name: Switch to PHP 7.1\n        uses: shivammathur\/setup-php@v2\n        with:\n          php-version: 7.1\n          coverage: none\n\n      # Lint the transpiled code\n      - name: Run PHP Parallel Lint on PHP 7.1\n        run: php-parallel-lint\/parallel-lint src\/ vendor\/ --exclude vendor\/symfony\/polyfill-ctype\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-grapheme\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-idn\/bootstrap80.php --exclude vendor\/symfony\/polyfill-intl-normalizer\/bootstrap80.php --exclude vendor\/symfony\/polyfill-mbstring\/bootstrap80.php\n<\/code><\/pre>\n<p>Remarquez que plusieurs fichiers <code>bootstrap80.php<\/code> des biblioth\u00e8ques polyfill de Symfony (qui n&rsquo;ont pas besoin d&rsquo;\u00eatre transpil\u00e9s) doivent \u00eatre exclus du linter. Ces fichiers contiennent PHP 8.0, le linter enverrait donc des erreurs lors de leur traitement. Cependant, l&rsquo;exclusion de ces fichiers est s\u00fbre car ils ne seront charg\u00e9s en production que si <a href=\"https:\/\/github.com\/symfony\/polyfill-mbstring\/blob\/9ad2f3c\/bootstrap.php#L14-L16\" target=\"_blank\" rel=\"noopener noreferrer\">PHP 8.0 ou plus est ex\u00e9cut\u00e9<\/a>:<\/p>\n<pre><code class=\"language-php\">if (\\PHP_VERSION_ID &gt;= 80000) {\n  return require __DIR__.'\/bootstrap80.php';\n}\n<\/code><\/pre>\n\n<h2>R\u00e9sum\u00e9<\/h2>\n<p>Cet article nous a appris \u00e0 transpiler notre code PHP, ce qui nous permet d&rsquo;utiliser PHP 8.0 dans le code source et de cr\u00e9er une version qui fonctionne sur PHP 7.1. La transpilation se fait via <a href=\"https:\/\/github.com\/rectorphp\/rector\" target=\"_blank\" rel=\"noopener noreferrer\">Rector<\/a>, un outil de reconstruction de PHP.<\/p>\n<p>Transpiler notre code fait de nous de meilleurs d\u00e9veloppeurs puisque nous pouvons mieux attraper les bogues en cours de d\u00e9veloppement et produire un code naturellement plus facile \u00e0 lire et \u00e0 comprendre.<\/p>\n<p>Transpiler nous permet aussi de d\u00e9coupler notre code des exigences PHP sp\u00e9cifiques du CMS. Nous pouvons d\u00e9sormais le faire si nous souhaitons utiliser la derni\u00e8re version de PHP pour cr\u00e9er une extension WordPress ou un module Drupal accessible au public sans restreindre s\u00e9v\u00e8rement notre base d&rsquo;utilisateurs.<\/p>\n<p><em>Vous avez encore des questions sur la transpilation PHP ? Fa\u00eetes-nous en part dans la section des commentaires !<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans des circonstances id\u00e9ales, nous devrions utiliser PHP 8.0 (la derni\u00e8re version au moment d&rsquo;\u00e9crire ces lignes) pour tous nos sites et le mettre \u00e0 jour &#8230;<\/p>\n","protected":false},"author":196,"featured_media":48639,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[31,253],"topic":[987,999],"class_list":["post-48633","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-php","tag-programming","topic-apprendre-php","topic-fonction-php"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v24.6 (Yoast SEO v24.6) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Le guide ultime pour transpiler le code PHP<\/title>\n<meta name=\"description\" content=\"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Le guide ultime pour transpiler le code PHP\" \/>\n<meta property=\"og:description\" content=\"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstafrance\/\" \/>\n<meta property=\"article:published_time\" content=\"2021-09-24T17:30:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-08-22T08:59:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg\" \/>\n\t<meta property=\"og:image:width\" content=\"1460\" \/>\n\t<meta property=\"og:image:height\" content=\"730\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Leonardo Losoviz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@kinsta_fr\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"23 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Le guide ultime pour transpiler le code PHP\",\"datePublished\":\"2021-09-24T17:30:38+00:00\",\"dateModified\":\"2023-08-22T08:59:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/\"},\"wordCount\":4580,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg\",\"keywords\":[\"php\",\"programming\"],\"articleSection\":[\"D\u00e9veloppement web\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/\",\"url\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/\",\"name\":\"Le guide ultime pour transpiler le code PHP\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg\",\"datePublished\":\"2021-09-24T17:30:38+00:00\",\"dateModified\":\"2023-08-22T08:59:11+00:00\",\"description\":\"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg\",\"width\":1460,\"height\":730,\"caption\":\"Le guide ultime pour transpiler le code PHP\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/fr\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Apprendre PHP\",\"item\":\"https:\/\/kinsta.com\/fr\/sujets\/apprendre-php\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Le guide ultime pour transpiler le code PHP\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/fr\/#website\",\"url\":\"https:\/\/kinsta.com\/fr\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/fr\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/fr\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstafrance\/\",\"https:\/\/x.com\/kinsta_fr\",\"https:\/\/www.instagram.com\/kinstahosting\/\",\"https:\/\/www.linkedin.com\/company\/kinsta\/\",\"https:\/\/www.pinterest.com\/kinstahosting\/\",\"https:\/\/www.youtube.com\/c\/Kinsta\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g\",\"caption\":\"Leonardo Losoviz\"},\"description\":\"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.\",\"sameAs\":[\"https:\/\/leoloso.com\",\"https:\/\/x.com\/losoviz\",\"https:\/\/www.youtube.com\/@GatoGraphQL\"],\"url\":\"https:\/\/kinsta.com\/fr\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Le guide ultime pour transpiler le code PHP","description":"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/","og_locale":"fr_FR","og_type":"article","og_title":"Le guide ultime pour transpiler le code PHP","og_description":"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.","og_url":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstafrance\/","article_published_time":"2021-09-24T17:30:38+00:00","article_modified_time":"2023-08-22T08:59:11+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.","twitter_image":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg","twitter_creator":"@losoviz","twitter_site":"@kinsta_fr","twitter_misc":{"\u00c9crit par":"Leonardo Losoviz","Dur\u00e9e de lecture estim\u00e9e":"23 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Le guide ultime pour transpiler le code PHP","datePublished":"2021-09-24T17:30:38+00:00","dateModified":"2023-08-22T08:59:11+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/"},"wordCount":4580,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/fr\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg","keywords":["php","programming"],"articleSection":["D\u00e9veloppement web"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/","url":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/","name":"Le guide ultime pour transpiler le code PHP","isPartOf":{"@id":"https:\/\/kinsta.com\/fr\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg","datePublished":"2021-09-24T17:30:38+00:00","dateModified":"2023-08-22T08:59:11+00:00","description":"Vous en avez assez de travailler avec plusieurs versions de PHP ? Apprenez \u00e0 \u00e9crire du code source avec PHP 8.0, et \u00e0 le transpiler dans les versions pr\u00e9c\u00e9dentes de PHP.","breadcrumb":{"@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#primaryimage","url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg","contentUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2021\/09\/transpiler-php.jpeg","width":1460,"height":730,"caption":"Le guide ultime pour transpiler le code PHP"},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/fr\/blog\/transpiler-php\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/fr\/"},{"@type":"ListItem","position":2,"name":"Apprendre PHP","item":"https:\/\/kinsta.com\/fr\/sujets\/apprendre-php\/"},{"@type":"ListItem","position":3,"name":"Le guide ultime pour transpiler le code PHP"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/fr\/#website","url":"https:\/\/kinsta.com\/fr\/","name":"Kinsta\u00ae","description":"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es","publisher":{"@id":"https:\/\/kinsta.com\/fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/fr\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/fr\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstafrance\/","https:\/\/x.com\/kinsta_fr","https:\/\/www.instagram.com\/kinstahosting\/","https:\/\/www.linkedin.com\/company\/kinsta\/","https:\/\/www.pinterest.com\/kinstahosting\/","https:\/\/www.youtube.com\/c\/Kinsta"]},{"@type":"Person","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b28085726ee66e49f08be16ad668efd5?s=96&d=mm&r=g","caption":"Leonardo Losoviz"},"description":"Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com\/losoviz.","sameAs":["https:\/\/leoloso.com","https:\/\/x.com\/losoviz","https:\/\/www.youtube.com\/@GatoGraphQL"],"url":"https:\/\/kinsta.com\/fr\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/48633","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/comments?post=48633"}],"version-history":[{"count":9,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/48633\/revisions"}],"predecessor-version":[{"id":48643,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/48633\/revisions\/48643"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/translations\/en"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/translations\/es"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/translations\/it"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/translations\/fr"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/translations\/pt"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/translations\/de"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/translations\/nl"},{"href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/48633\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/media\/48639"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/media?parent=48633"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/tags?post=48633"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/topic?post=48633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}