{"id":40183,"date":"2021-10-06T14:18:40","date_gmt":"2021-10-06T12:18:40","guid":{"rendered":"https:\/\/kinsta.com\/?p=100177"},"modified":"2023-06-07T09:01:48","modified_gmt":"2023-06-07T07:01:48","slug":"transpilen-php","status":"publish","type":"post","link":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/","title":{"rendered":"De ultieme handleiding voor het transpilen van PHP code"},"content":{"rendered":"<p>In ideale omstandigheden zouden we voor al onze sites <a href=\"https:\/\/kinsta.com\/nl\/changelog\/php-8\/\">PHP 8.0<\/a> (de laatste versie op het moment van schrijven) gebruiken en deze updaten zodra er een nieuwe versie uitkomt. In de praktijk werken developers echter vaak uit noodzaak met oudere PHP versies, zoals wanneer je een openbare plugin voor <a href=\"https:\/\/kinsta.com\/nl\/blog\/wat-is-wordpress\/\">WordPress<\/a> maakt of werkt met verouderde code die het upgraden van de webserveromgeving belemmert.<\/p>\n<p>In deze situaties kunnen we al snel de hoop opgeven om de meest recente PHP versie te gebruiken. Maar er is een beter alternatief: we kunnen onze broncode schrijven met PHP 8.0 en deze transpilen naar een eerdere PHP versie &#8211; zelfs naar PHP 7.1.<\/p>\n<p>In deze gids leren we je alles wat je moet weten over het transpilen van PHP code.<\/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>Wat is transpilen?<\/h2>\n<p>Door te transpilen converteer je de broncode van een <a href=\"https:\/\/kinsta.com\/nl\/blog\/beste-programmeertaal-om-te-leren\/\">programmeertaal<\/a> naar equivalente broncode van dezelfde of een andere programmeertaal.<\/p>\n<p>Transpilen is zeker geen nieuw concept binnen webontwikkeling: client-side developers zijn waarschijnlijk wel bekend met <a href=\"https:\/\/babeljs.io\/\">Babel<\/a>, een transpiler voor JavaScript code.<\/p>\n<p>Babel converteert JavaScript code van de moderne ECMAScript 2015+ versie naar een oudere versie die compatibel is met oudere browsers. Als de tool een ES2015 arrow functie krijgt:<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map((n) =&gt; n * 2);\n<\/code><\/pre>\n<p>&#8230; dan converteert Babel het naar een ES5 versie:<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map(function(n) {\n  return n * 2;\n});\n<\/code><\/pre>\n<h3>Wat is het transpilen van PHP?<\/h3>\n<p>Wat potentieel nieuw is binnen webontwikkeling: de mogelijkheid om server-side code te transpilen, en dan in het bijzonder PHP.<\/p>\n<p>Het transpilen van PHP werkt op dezelfde manier als het transpilen van JavaScript: broncode van een <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-versies\/\">moderne PHP versie<\/a> wordt geconverteerd naar een equivalente code voor een oudere PHP versie.<\/p>\n<p>In de lijn van hetzelfde voorbeeld als hierboven, een arrow functie van 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; kan worden getranspileerd naar de equivalente PHP 7.3 versie:<\/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>Arrow functies kunnen worden getranspileerd omdat ze <a href=\"https:\/\/en.wikipedia.org\/wiki\/Syntactic_sugar\">syntactic sugar<\/a> zijn, dat wil zeggen een nieuwe syntax om een bestaand gedrag te produceren. Dit is het laaghangende fruit.<\/p>\n<p>Maar er zijn natuurlijk ook nieuwe features die nieuw gedrag cre\u00ebren, waarvoor dus geen equivalente code bestaat in eerdere versies van PHP. Dat is het geval met <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-8\/#union-types-2-0\">union types<\/a>, die werden ge\u00efntroduceerd in 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>In deze situaties is transpilen nog steeds mogelijk, zolang de nieuwe feature nodig is voor ontwikkeling, maar niet voor productie. We kunnen in dat geval de feature helemaal uit de getranspileerde code verwijderen zonder ernstige gevolgen.<\/p>\n<p>Een voorbeeld hiervan zijn union types. Deze feature wordt gebruikt om te controleren of er geen mismatch is tussen het input type en de opgegeven waarde, wat bugs helpt voorkomen. Als er een conflict is met types, dan zie je fout al in de ontwikkelingsfase. Dit betekent dat we, voordat de code de productie-omgeving bereikt, de fout kunnen vinden en oplossen.<\/p>\n<p>Dat is dan ook de reden dat we het ons kunnen veroorloven om de feature uit de &#8220;productiecode&#8221; kunnen verwijderen:<\/p>\n<pre><code class=\"language-php\">function someFunction($param)\n{\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Als de fout nog steeds optreedt in productie, dan zal de gegenereerde foutmelding minder nauwkeurig zijn dan wanneer we union types hadden. Dit potenti\u00eble nadeel wordt echter gecompenseerd door het feit dat we \u00fcberhaupt union types kunnen gebruiken.<\/p>\n\n<h2>Voordelen van het transpilen van PHP code<\/h2>\n<p>Transpiling stelt iemand in staat om een applicatie te coderen met de nieuwste versie van PHP en een release te produceren die ook werkt in omgevingen met oudere versies van PHP.<\/p>\n<p>Dit kan met name handig zijn voor ontwikkelaars die producten maken voor oudere <a href=\"https:\/\/kinsta.com\/blog\/content-management-system\/\">Content Management Systems (CMS)<\/a>. WordPress ondersteunt <a href=\"https:\/\/wordpress.org\/about\/requirements\/\">officieel nog steeds PHP 5.6<\/a> (hoewel het PHP 7.4+ aanbeveelt). Het percentage van WordPress sites met daarop PHP versies 5.6 tot 7.2 &#8211; die allemaal End-Of-Life (EOL) zijn, wat betekent dat ze geen beveiligingsupdates meer ontvangen &#8211; staat op een aanzienlijke 34,8%. Sterker nog, maar liefst 99,5% van de sites draait op een versie van eerder dan PHP 8.0:<\/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=\"WordPress gebruiksstatistieken per versie\" width=\"920\" height=\"720\"><\/a><figcaption class=\"wp-caption-text\">WordPress gebruiksstatistieken per versie. Bron: <a href=\"https:\/\/wordpress.org\/about\/stats\/\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress<\/a><\/figcaption><\/figure>\n<p>Als gevolg daarvan zijn WordPress thema&#8217;s en plugins die openbaar te verkrijgen zijn en een wereldwijd publiek bedienen, waarschijnlijk gecodeerd met een oudere versie van PHP om hun bereik te vergroten. Dankzij transpiling kunnen deze worden gecodeerd met PHP 8.0, en toch worden uitgebracht voor een oudere PHP versie, waardoor zoveel mogelijk gebruikers worden getarget.<\/p>\n<p>Elke toepassing die een andere PHP versie dan de meest recente moet ondersteunen (zelfs binnen het bereik van de momenteel ondersteunde PHP versies) kan dus profiteren.<\/p>\n<p>Dit is het geval met Drupal, waarvoor <a href=\"https:\/\/www.drupal.org\/docs\/system-requirements\/php-requirements\">PHP versie 7.3 vereist is<\/a>. Dankzij transpiling kunnen developers openbaar beschikbare Drupal modules maken met PHP 8.0 en deze releasen met PHP 7.3.<\/p>\n<p>Een ander voorbeeld is het maken van aangepaste code voor klanten die om de een of andere reden geen PHP 8.0 in hun omgeving kunnen draaien. Desalniettemin kunnen ontwikkelaars dankzij transpiling hun deliverables nog steeds coderen met PHP 8.0 en ze in die oudere omgevingen uitvoeren.<\/p>\n<h3>Wanneer PHP transpilen?<\/h3>\n<p>PHP code kan altijd worden getranspileerd, tenzij het een PHP feature bevat die geen equivalent heeft in de vorige PHP versie.<\/p>\n<p>Dat is mogelijk het geval met <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-8\/#attributes\">attributes<\/a> die werden ge\u00efntroduceerd in PHP 8.0:<\/p>\n<pre><code class=\"language-php\">#[SomeAttr]\nfunction someFunc() {}\n\n#[AnotherAttr]\nclass SomeClass {}\n<\/code><\/pre>\n<p>In het eerdere voorbeeld met daarin arrow functies, kon de code worden getranspileerd omdat arrow functies syntactic sugar zijn. Attributes daarentegen cre\u00ebren geheel nieuw gedrag. Dit gedrag zou kunnen worden gereproduceerd binnen PHP 7.4 en lager, maar alleen door het handmatig te coderen, dus niet op basis van een tool of proces (AI zou een oplossing kunnen bieden, maar we zijn nog niet zover).<\/p>\n<p>Attributes die bedoeld zijn voor gebruik tijdens de ontwikkelingsfase, zoals <code><a href=\"https:\/\/wiki.php.net\/rfc\/deprecated_attribute\">#[Deprecated]<\/a><\/code>, kunnen op dezelfde manier worden verwijderd als union types. Maar attributes die het gedrag van de toepassing in productie wijzigen, kunnen niet worden verwijderd, en kunnen ook niet rechtstreeks worden getranspileerd.<\/p>\n<p>Vandaag de dag is er nog geen transpiler die code met PHP 8.0 attributes kan pakken om automatisch de equivalente PHP 7.4 code te produceren. Als je PHP code dus attributes moet gebruiken, wordt het transpilen erg lastig, zo niet onmogelijk.<\/p>\n<h3>PHP features die kunnen worden getranspileerd<\/h3>\n<p>Dit zijn de features van PHP 7.1 en hoger die kunnen worden getranspileerd. Als je code alleen deze features gebruikt, dan kan je er zeker van zijn dat de getranspileerde applicatie zal werken. In alle andere gevallen moet je zelf kijken of de getranspileerde code geen foutmeldingen geeft.<\/p>\n<table>\n<thead>\n<tr>\n<th>PHP Versie<\/th>\n<th>Features<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.1<\/td>\n<td>Everything<\/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\"><code>object<\/code> type<\/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\">parameter type widening<\/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\"><code>PREG_UNMATCHED_AS_NULL<\/code> flag in <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\">Reference assignments in <code>list()<\/code> \/ array destructuring<\/a> (<em>behalve binnen <code>foreach<\/code> \u2014 <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\">Flexible Heredoc en Nowdoc syntax<\/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\">Trailing commas in functions calls<\/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> accepteert $option argument<\/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\">Typed properties<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/functions.arrow.php\" target=\"_blank\" rel=\"noopener noreferrer\">Arrow functies<\/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\">Null coalescing assignment operator<\/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\">Unpacking binnen arrays<\/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\">Numeric literal separator<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.standard.strip-tags\" target=\"_blank\" rel=\"noopener noreferrer\"><code>strip_tags()<\/code> met array van tag names<\/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\">covariant return types en contravariant param types<\/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\">Union types<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/mixed-type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>mixed<\/code> pseudo type<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/static-return-type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>static<\/code> return type<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/class-constant-on-objects\" target=\"_blank\" rel=\"noopener noreferrer\"><code>::class<\/code> magic constant bijj objects<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/match-expression\" target=\"_blank\" rel=\"noopener noreferrer\"><code>match<\/code> expressions<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/catch-exception-type\" target=\"_blank\" rel=\"noopener noreferrer\"><code>catch<\/code> exceptions allen per type<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/null-safe-operator\" target=\"_blank\" rel=\"noopener noreferrer\">Null-safe operator<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/constructor-property-promotion\" target=\"_blank\" rel=\"noopener noreferrer\">Class constructor property promotion<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/trailing-comma-parameter-use-list\" target=\"_blank\" rel=\"noopener noreferrer\">Trailing commas in parameter lists en closure <code>use<\/code> lists<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>PHP transpilers<\/h2>\n<p>Momenteel is er \u00e9\u00e9n tool voor het transpilen van PHP code: <a href=\"https:\/\/github.com\/rectorphp\/rector\">Rector<\/a>.<\/p>\n<p>Rector is een PHP reconstructortool, die PHP code converteert op basis van programmeerbare regels. We voeren de broncode in en de <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md\">reeks regels<\/a> die moeten worden uitgevoerd, en Rector zal de code transformeren.<\/p>\n<p>Rector wordt bediend via de opdrachtregel en wordt via Composer in het project ge\u00efnstalleerd. Bij uitvoering zal Rector een &#8220;diff&#8221; (toevoegingen in groen, verwijderingen in rood) van de code geven (van voor en na de conversie):<\/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=\"\"diff\" output van Rector\" width=\"740\" height=\"480\"><\/a><figcaption class=\"wp-caption-text\">&#8220;diff&#8221; output van Rector<\/figcaption><\/figure>\n<h2>Naar welke PHP versie je moet transpilen<\/h2>\n<p>Om code naar andere PHP versies te transpilen, moeten de bijbehorende regels worden gemaakt.<\/p>\n<p>Tegenwoordig bevat de Rector bibliotheek de meeste regels die je nodig hebt voor het transpilen van code binnen het bereik van PHP 8.0 tot 7.1. We kunnen dus met redelijk vertrouwen PHP code transpilen tot versie\u00a0 7.1.<\/p>\n<p>Ook zijn er <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp71\">regels voor het transpilen van PHP 7.1 tot 7.1<\/a> en van <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp70\">7.0 tot 5.6<\/a>, maar deze regels zijn niet uitputtend. Er wordt aan gewerkt om ze te voltooien, zodat we PHP code uiteindelijk naar versie 5.6 kunnen transpilen.<\/p>\n<h2>Transpilen vs backporten<\/h2>\n<p>Backporting is vergelijkbaar met transpilen, maar simpeler. Het backporten van code is niet noodzakelijk afhankelijk van nieuwe features van een taal. In plaats daarvan kan dezelfde functionaliteit worden geleverd aan een oudere versie van de taal door simpelweg de corresponderende code uit de nieuwe versie van de taal te kopi\u00ebren\/plakken\/aan te passen.<\/p>\n<p>De functie <code>str_contains<\/code> werd bijvoorbeeld ge\u00efntroduceerd in PHP 8.0. Dezelfde functie kan eenvoudig als volgt in PHP 7.4 en lager worden ge\u00efmplementeerd:<\/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}\n<\/code><\/pre>\n<p>Omdat backporten eenvoudiger is dan transpilen, is het slim om voor backporten te kiezen als dit de klus kan klaren.<\/p>\n<p>Wat betreft alles wat tussen PHP 8.0 en 7.1 komt, kunnen we de polyfill libraries van <a href=\"https:\/\/symfony.com\/\">Symfony<\/a> gebruiken:<\/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>Met deze libraries kan je de volgende functies, classes, constants en interfaces backporten:<\/p>\n<table style=\"height: 1173px\" width=\"335\">\n<thead>\n<tr>\n<th>PHP Versie<\/th>\n<th>Features<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.2<\/td>\n<td>Functies:<\/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>Constants:<\/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>Functies:<\/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>Functies:<\/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>Constants:<\/p>\n<ul>\n<li><code>FILTER_VALIDATE_BOOL<\/code><\/li>\n<\/ul>\n<p>Functies:<\/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>Voorbeelden van getranspileerde PHP<\/h2>\n<p>Laten we een paar voorbeelden van getranspileerde PHP code bekijken, en een paar pakketten die volledig worden getranspileerd.<\/p>\n<h3>PHP code<\/h3>\n<p>De <code>match<\/code> expressie werd <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-8\/#match-expression\">ge\u00efntroduceerd in PHP 8.0<\/a>. Deze broncode:<\/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;wordt getranspileerd naar de equivalente PHP 7.4 versie met behulp van de <code>switch<\/code> operator:<\/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>Ook de <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-8\/#nullsafe-operator\">nullsafe operator<\/a> werd ge\u00efntroduceerd in 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>De getranspileerde code moet eerst de waarde van de operation aan een nieuwe variabele toewijzen, om te voorkomen dat de operation twee keer wordt uitgevoerd:<\/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>De <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-8\/#constructor-property-promotion\">constructor property promotion<\/a> feature, die ook werd ge\u00efntroduceerd in PHP 8.0, stelt developers in staat om minder code te schrijven:<\/p>\n<pre><code class=\"language-php\">class QueryResolver\n{\n  function __construct(protected QueryFormatter $queryFormatter)\n  {\n  }\n}\n<\/code><\/pre>\n<p>Bij het transpilen voor PHP 7.4, wordt het volledige stuk code geproduceerd:<\/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>De getranspileerde code hierboven bevat <a href=\"https:\/\/kinsta.com\/nl\/blog\/php-7-4\/#typed-properties\">typed properties<\/a>, die zijn ge\u00efntroduceerd in PHP 7.4. Door die code naar PHP 7.3 te transpilen, worden ze vervangen door 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>PHP packages<\/h3>\n<p>The following libraries are being transpiled for production:<\/p>\n<table>\n<thead>\n<tr>\n<th>Library\/<strong>beschrijving<\/strong><\/th>\n<th>Code\/<strong>opmerkingen<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/getrector.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Rector<\/a><br \/>\nPHP reconstructortool die transpilen mogelijk maakt<\/td>\n<td>\u2013\u00a0<a href=\"https:\/\/github.com\/rectorphp\/rector-src\">Broncode<\/a><br \/>\n\u2013\u00a0<a href=\"https:\/\/github.com\/rectorphp\/rector\">Getranspileerde code<\/a><br \/>\n\u2013\u00a0<a href=\"https:\/\/getrector.org\/blog\/prefixed-rector-by-default\">Opmerkingen<\/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\">Easy Coding Standards<\/a><br \/>\nTool om PHP code aan een reeks regels te laten voldoen<\/td>\n<td>\u2013\u00a0<a href=\"https:\/\/github.com\/symplify\/symplify\/tree\/main\/packages\/easy-coding-standard\">Broncode<\/a><br \/>\n\u2013\u00a0<a href=\"https:\/\/github.com\/symplify\/easy-coding-standard\">Getranspileerde code<\/a><br \/>\n\u2013\u00a0<a href=\"https:\/\/tomasvotruba.com\/blog\/introducing-ecs-prefixed-and-downgraded-to-php-71\/\">Opmerkingen<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/graphql-api.com\" target=\"_blank\" rel=\"noopener noreferrer\">GraphQL API for WordPress<\/a><br \/>\nPlugin die een GraphQL server voor WordPress levert<\/td>\n<td>\u2013\u00a0<a href=\"https:\/\/github.com\/leoloso\/PoP\/tree\/master\/layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\">Broncode<\/a><br \/>\n\u2013\u00a0<a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp-dist\/\">Getranspileerde code<\/a><br \/>\n\u2013\u00a0<a href=\"https:\/\/graphql-api.com\/blog\/the-plugin-is-now-transpiled-from-php-80-to-71\/\">Opmerkingen<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Voor- en nadelen van het transpilen van PHP<\/h2>\n<p>Het voordeel van het transpilen van PHP hebben we al kort beschreven: je kan in je broncode PHP 8.0 gebruiken (of de nieuwste versie van PHP) die zal worden getransformeerd naar een lagere versie van PHP zodat deze kan worden uitgevoerd in een oudere applicatie of omgeving.<\/p>\n<p>Hiermee worden we betere ontwikkelaars en kunnen we onze code met een hogere kwaliteit produceren. Dit komt omdat onze broncode de union types van PHP 8.0, de typed properties van PHP 7.4 en de verschillende types en pseudo-types die aan elke nieuwe versie van PHP worden toegevoegd (<code>mixed<\/code> van PHP 8.0, <code>object<\/code> van PHP 7.2), kan gebruiken, naast andere moderne features van PHP.<\/p>\n<p>Door deze features te gebruiken, kunnen we tijdens de ontwikkelingsfase bugs sneller vinden en code schrijven die makkelijker te lezen is.<\/p>\n<p>Laten we nu eens kijken naar de nadelen.<\/p>\n<h3>Het moet worden gecodeerd en onderhouden<\/h3>\n<p>Rector kan code automatisch transpilen, maar er is zeker nog wel wat handmatige input vereist om het te laten werken met jouw specifieke setup.<\/p>\n<h3>Externe libraries moeten ook worden getranspileerd<\/h3>\n<p>Dit wordt een probleem wanneer het transpilen ervan fouten oplevert, omdat we dan in hun broncode moeten duiken om de mogelijke reden te achterhalen. Als het probleem kan worden opgelost en het project open source is, moeten we een pull-verzoek indienen. Als de library niet open source is, dan hebben we wellicht een probleem.<\/p>\n<h3>Rector informeert ons niet wanneer code niet kan worden getranspileerd<\/h3>\n<p>Als de broncode PHP 8.0 attributes bevat of een andere feature die niet kan worden getranspileerd, dan kunnen we niet verder gaan. Rector zal deze voorwaarde echter niet zelf checken, dus moeten we dit zelf doen. Dit is misschien geen groot probleem als het om onze eigen broncode gaat, omdat we het door en door kennen, maar het kan wel een probleem vormen met betrekking tot externe dependencies.<\/p>\n<h3>Debug-informatie gebruikt de getranspileerde code en niet de broncode<\/h3>\n<p>Wanneer de applicatie in productie een foutmelding geeft met een stacktrace, dan wijst het regelnummer naar de getranspileerde code. We moeten het dus terug converteren van getranspileerde naar originele code om het corresponderende regelnummer in de broncode te vinden.<\/p>\n<h3>De getranspileerde code moet ook worden geprefixed<\/h3>\n<p>Ons getranspileerde project en de andere libraries die ook in de productieomgeving zijn ge\u00efnstalleerd, kunnen dezelfde externe dependencies gebruiken. Deze externe dependency wordt getranspileerd voor ons project en behoudt de originele broncode voor de andere library. Daarom moet de getranspileerde versie worden geprefixed via <a href=\"https:\/\/github.com\/humbug\/php-scoper\">PHP-Scoper<\/a>, <a href=\"https:\/\/github.com\/BrianHenryIE\/strauss\">Strauss<\/a> of een andere tool om mogelijke conflicten te voorkomen.<\/p>\n<h3>Transpiling moet plaatsvinden tijdens continue integratie (CI)<\/h3>\n<p>Omdat de getranspileerde code uiteraard de broncode overschrijft, moeten we het transpilingproces niet uitvoeren op onze ontwikkelcomputers, anders lopen we het risico dat er bijwerkingen ontstaan. Het proces tijdens een CI run uitvoeren is geschikter (hierover hieronder meer).<\/p>\n<h2>Zo transpile je PHP<\/h2>\n<p>Eerst moeten we Rector installeren in ons project voor ontwikkeling:<\/p>\n<pre><code class=\"language-bash\">composer require rector\/rector --dev\n<\/code><\/pre>\n<p>Vervolgens cre\u00ebren we een <code>rector.php<\/code> configuratiebestand in de rootmap van het project met daarin de vereiste set regels. Om code te downgraden van PHP 8.0 tot 7.1, gebruiken we deze config:<\/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>Om ervoor te zorgen dat het proces wordt uitgevoerd zoals we willen, kunnen we het <code>process<\/code> commando van Rector in dry mode uitvoeren, waarbij we de te verwerken locatie(s) doorgeven (in dit geval alle bestanden onder de map <code>src\/<\/code>):<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src --dry-run\n<\/code><\/pre>\n<p>Om het transpilen uit te voeren, voeren we het <code>process<\/code> commando van Rector uit, dat de bestanden op hun bestaande locatie zal wijzigen:<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src\n<\/code><\/pre>\n<p>Let op: als we het <code>rector process<\/code> uitvoeren op onze ontwikkelcomputers, wordt de broncode op de huidige plaats geconverteerd, onder <code>src\/<\/code>. We willen de geconverteerde code echter op een andere locatie produceren om de broncode niet te overschrijven bij het downgraden van de code. Om deze reden is het uitvoeren van het proces het meest geschikt tijdens continue integratie.<\/p>\n<h2>Optimalisatie van het transpileproces<\/h2>\n<p>Om een getranspileerde deliverable voor productie te genereren, hoeft alleen de code voor productie te worden geconverteerd; code die alleen nodig is voor ontwikkeling, kan worden overgeslagen. Dat betekent dat we kunnen voorkomen dat alle tests (voor zowel ons project als de bijbehorende dependencies) en alle dependencies voor ontwikkeling moeten worden getranspileerd.<\/p>\n<p>Wat betreft tests, we weten al waar degenen voor ons project zich bevinden &#8211; bijvoorbeeld in de map <code>tests\/<\/code>. We moeten ook uitzoeken waar die voor onze dependencies zijn \u2014 bijvoorbeeld in hun submappen <code>tests\/<\/code>, <code>test\/<\/code> en <code>Test\/<\/code> (voor verschillende libraries). Vervolgens moeten we Rector vertellen om deze mappen over te slaan tijdens de verwerking:<\/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>Met betrekking tot dependencies weet Composer welke voor ontwikkeling zijn (met entry <code>require-dev<\/code> in <code>componist.json<\/code>) en welke voor productie zijn (met entry <code>require<\/code>).<\/p>\n<p>Om met Composer alle paden met alle dependencies voor productie te halen, voeren we uit:<\/p>\n<pre><code class=\"language-bash\">composer info --path --no-dev\n<\/code><\/pre>\n<p>Dit commando produceert een lijst met dependencies met hun naam en pad, zoals hieronder:<\/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>We kunnen alle paden extracten en ze invoeren in het Rector commando, dat vervolgens de <code>src\/<\/code> map van ons project zal verwerken plus die mappen die alle dependencies voor productie bevatten:<\/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>Er is een verbetering mogelijk die kan voorkomen dat Rector dependencies verwerkt die de doel PHP versie al gebruikt. Als een library is gecodeerd met PHP 7.1 (of een versie eronder), dan hoeft deze niet naar PHP 7.1 worden getranspileerd.<\/p>\n<p>Om dit te bereiken, moeten we een lijst met libraries krijgen die PHP 7.2 en hoger nodig hebben, en vervolgens alleen die libraries verwerken. De namen van deze libraries krijgen we via Composer&#8217;s <code>why-not<\/code> commando, zoals hieronder:<\/p>\n<pre><code class=\"language-bash\">composer why-not php \"7.1.*\" | grep -o \"\\S*\\\/\\S*\"\n<\/code><\/pre>\n<p>Omdat dit commando niet werkt met de <code>--no-dev<\/code> flag, waarmee we alleen dependencies voor productie zouden krijgen, moeten we eerst de dependencies voor ontwikkeling verwijden en de autoloader opnieuw genereren, vervolgens het commando uitvoeren en ze weer toevoegen:<\/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>Composer&#8217;s <code>info --path<\/code> commando haalt het pad voor een package op volgens dit 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>We voeren deze opdracht uit voor alle items in onze lijst om alle paden te krijgen die we willen transpilen:<\/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>Ten slotte geven we deze lijst aan Rector (plus de <code>src\/<\/code> map van het project):<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src $paths\n<\/code><\/pre>\n<h2>Te vermijden valkuilen bij het transpilen van code<\/h2>\n<p>Het transpilen van code kan als een kunst worden beschouwd, waarvoor vaak aanpassingen nodig zijn die specifiek zijn voor het project. Laten we eens kijken naar een paar problemen die we kunnen tegenkomen.<\/p>\n<h3>Chained rules worden niet altijd verwerkt<\/h3>\n<p>Een chained rule is wanneer een rule code moet converteren die door een eerdere rule is geproduceerd.<\/p>\n<p>Library <code>symfony\/cache<\/code> bevat bijvoorbeeld <a href=\"https:\/\/github.com\/symfony\/cache\/blob\/be5707f\/CacheItem.php#L115\">deze 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>Bij het transpilen van PHP 7.4 naar 7.3, moet de functie <code>tag<\/code> twee wijzigingen ondergaan:<\/p>\n<ul>\n<li>Het return type <code>ItemInterface<\/code> moet eerst worden geconverteerd naar <code>self<\/code>, vanwege rule <code><a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeCovariantReturnTypeRector.php\">DowngradeCovariantReturnTypeRector<\/a><\/code><\/li>\n<li>De return type <code>self<\/code> moet vervolgens worden verwijderd, vanwege rule <code><a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeSelfTypeDeclarationRector.php\">DowngradeSelfTypeDeclarationRector<\/a><\/code><\/li>\n<\/ul>\n<p>Het eindresultaat zou als volgt moeten zijn:<\/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>Rector geeft als output echter alleen de tussenfase:<\/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>Het probleem is dat <a href=\"https:\/\/github.com\/rectorphp\/rector\/issues\/5962\">Rector niet altijd controle heeft over de volgende waarin de regels worden toegepast<\/a>.<\/p>\n<p>De oplossing is om te identificeren welke chained rules onverwerkt zijn gelaten en een nieuwe Rector run uit te voeren om ze toe te passen.<\/p>\n<p>Om de chained rules te identificeren, draaien we Rector twee keer op de broncode, zoals hieronder:<\/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>De eerste keer draaien we Rector zoals verwacht, om de transpiling uit te voeren. De tweede keer gebruiken we de <code>--dry-run<\/code> flag om te kijken of er nog wijzigingen moeten worden aangebracht. Als dat het geval is, wordt het commando afgesloten met een foutcode, en geeft de uitvoer &#8220;diff&#8221; aan welke regel(s) nog kunnen worden toegepast. Dat zou betekenen dat de eerste run niet voltooid was en dat een of andere chained rule niet werd verwerkt.<\/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=\"Rector uitvoeren met --dry-run flag\" width=\"901\" height=\"903\"><\/a><figcaption class=\"wp-caption-text\">Rector uitvoeren met &#8211;dry-run flag<\/figcaption><\/figure>\n<p>Zodra we de niet-verwerkte chained rule(s) hebben ge\u00efdentificeerd, kunnen we een ander Rector configuratiebestand maken \u2014 <code>rector-chained-rule.php<\/code> kan bijvoorbeeld de ontbrekende rule uitvoeren. In plaats van een volledige set regels voor alle bestanden onder <code>src\/<\/code> te verwerken, kunnen we deze keer de specifieke ontbrekende rule uitvoeren binnen het specifieke bestand waar het moet worden toegepast:<\/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>En slotte vertellen we Rector in de tweede ronde om het nieuwe config bestand te gebruiken via input <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>Composer depencies kunnen inconsistent zijn<\/h3>\n<p>Libraries kunnen declaren dat een dependency bedoeld is voor ontwikkeling (bijv door <code>require-dev<\/code> in <code>composer.json<\/code>), maar toch voor productie naar een deel van de code ervan verwijzen (zoals sommige bestanden onder <code>src\/<\/code>, niet <code>tests\/<\/code>).<\/p>\n<p>Meestal is dit geen probleem omdat die code vaak niet in productie wordt geladen, dus er zal nooit een fout in de applicatie zijn. Wanneer Rector echter de broncode en zijn dependencies verwerkt, controleert het of alle code waarnaar wordt verwezen ook kan worden geladen. Rector zal een foutmelding geven als een bestand verwijst naar een stukje code uit een niet-ge\u00efnstalleerde library (omdat er werd gedeclared dat deze alleen nodig was voor ontwikkeling).<\/p>\n<p><code><a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/8f03a1f\/src\/Symfony\/Component\/Cache\/Messenger\/EarlyExpirationHandler.php\">EarlyExpirationHandler<\/a><\/code> van Symfony&#8217;s Cache component implementeert bijvoorbeeld interface <code><a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/191cb52\/src\/Symfony\/Component\/Messenger\/Handler\/MessageHandlerInterface.php\">MessageHandlerInterface<\/a><\/code> van de Messenger component:<\/p>\n<pre><code class=\"language-php\">class EarlyExpirationHandler implements MessageHandlerInterface\n{\n    \/\/...\n}\n<\/code><\/pre>\n<p>Maar <code>symfony\/cache<\/code> declaret <code>symfony\/messenger<\/code> als een <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/6fe82d8\/src\/Symfony\/Component\/Cache\/composer.json#L43\">dependency voor ontwikkeling<\/a>. Als Rector vervolgens wordt uitgevoerd op een project dat afhankelijk is van <code>symfony\/cache<\/code>, dan zal deze een foutmelding geven:<\/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>Er zijn drie oplossingen voor dit probleem:<\/p>\n<ol>\n<li>Sla in de Rector config de verwerking over van het bestand waarnaar dat stukje code verwijst:<\/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};\n<\/code><\/pre>\n<ol start=\"2\">\n<li>Download de ontbrekende libary en voeg het pad toe dat door Rector automatisch moet worden geladen:<\/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>Laat je project voor productie ook afhankelijk van de ontbrekende library:<\/li>\n<\/ol>\n<pre><code class=\"language-bash\">composer require symfony\/messenger\n<\/code><\/pre>\n<h2>Transpilen en continue integratie<\/h2>\n<p>Zoals eerder vermeld, moeten we in onze ontwikkelingscomputers de <code>--dry-run<\/code> flag gebruiken wanneer Rector wordt uitgevoerd, anders wordt de broncode overschreven door de getranspileerde code. Om deze reden is het geschikter om het daadwerkelijke transpiling-proces uit te voeren tijdens continue integratie (CI), waarbij we tijdelijke runners kunnen laten draaien om het proces uit te voeren.<\/p>\n<p>Een ideaal moment om het transpiling-proces uit te voeren is bij het genereren van de release voor ons project. De <a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp\/blob\/3f9fd52ddf318b9fbb33653de8acfec8dc4b4665\/.github\/workflows\/main.yml\">onderstaande code<\/a> is bijvoorbeeld een workflow voor <a href=\"https:\/\/github.com\/features\/actions\">Github Actions<\/a>, die de release van een WordPress plugin cre\u00ebert:<\/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>Deze workflow bevat een standaard procedure om een <a href=\"https:\/\/leoloso.com\/posts\/github-action-to-release-wp-plugin\/\">WordPress plugin te releasen via GitHub Actions<\/a>. De nieuwe toevoeging, om de code van de plugin te transpilen van PHP 7.4 naar 7.1, gebeurt in deze stap:<\/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>Alles bij elkaar voert deze workflow nu de volgende stappen uit:<\/p>\n<ol>\n<li>Bekijkt de broncode voor een WordPress plugin uit de repository, geschreven met PHP 7.4<\/li>\n<li>Installeert de Composer dependencies<\/li>\n<li>Transpilet de code van PHP 7.4 naar 7.1<\/li>\n<li>Wijzigt de entry &#8220;Requires PHP&#8221; in de header van het hoofdbestand van de plugin van <code>\"7.4\"<\/code> naar <code>\"7.1\"<\/code><\/li>\n<li>Verwijdert de dependencies die nodig zijn voor ontwikkel<\/li>\n<li>Maakt het .zip bestand van de plugin, met uitzondering van alle onnodige bestanden<\/li>\n<li>Uploadt het .zip bestand als een release asset (en bovendien als een artifact naar de GitHub Action)<\/li>\n<\/ol>\n<h2>Testen van getranspileerde code<\/h2>\n<p>Als de code eenmaal naar PHP 7.1, is getranspileerd, hoe weten we dan of deze goed werkt? Of, met andere woorden, hoe weten we dat het secuur is geconverteerd en dat er geen overblijfselen van nieuwere versies van PHP code zijn achtergebleven?<\/p>\n<p>Net als bij het transpilen van de code zelf, kunnen we de oplossing toevoegen aan een CI proces. Het idee is om de omgeving van de runner op te zetten met PHP 7.1 en een linter uit te voeren op de getranspileerde code. Als een stukje code niet compatibel is met PHP 7.1 (zoals een typed property van PHP 7.4 die niet is geconverteerd), geeft de linter een foutmelding.<\/p>\n<p>Een linter voor PHP die goed werkt, is <a href=\"https:\/\/github.com\/php-parallel-lint\/PHP-Parallel-Lint\">PHP Parallel Lint<\/a>. We kunnen deze library installeren als een dependency voor ontwikkeling binnen ons project, of dit tijdens het CI proces doen als een stand-alone Composer project:<\/p>\n<pre><code class=\"language-bash\">composer create-project php-parallel-lint\/php-parallel-lint\n<\/code><\/pre>\n<p>Telkens wanneer de code PHP 7.2 en hoger bevat, geeft PHP Parallel Lint een foutmelding <a href=\"https:\/\/github.com\/leoloso\/PoP\/runs\/2751846434?check_suite_focus=true\">zoals deze<\/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>Laten we linter toevoegen aan onze CDI workflow. De stappen die nodig zijn om de code te transpilen van PHP 8.0 naar 7.1 en alles te testen, zijn:<\/p>\n<ol>\n<li>Broncode bekijken<\/li>\n<li>De omgeving op PHP 8.0 laten draaien, zodat Rector de broncode kan interpreteren<\/li>\n<li>De code transpilen naar PHP 7.1<\/li>\n<li>De PHP lintertool installeren<\/li>\n<li>De PHP versie van de omgeving overschakelen naar 7.1<\/li>\n<li>Linter uitvoeren op de getranspileerde code<\/li>\n<\/ol>\n<p>Deze <a href=\"https:\/\/github.com\/leoloso\/PoP\/blob\/b93d3e35cb59e0281b45899fd82231c3d8cbbe25\/.github\/workflows\/downgrade_php_tests.yml\">GitHub Action workflow<\/a> klaart de klus:<\/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>Houd er rekening mee dat verschillende <code>bootstrap80.php<\/code> bestanden uit Symfony&#8217;s polyfill libraries (die niet hoeven te worden getranspileerd) van de linter moeten worden uitgesloten. Deze bestanden bevatten PHP 8.0, dus de linter zal fouten melden bij het verwerken ervan. Het uitsluiten van deze bestanden is echter veilig omdat ze alleen in productie worden geladen <a href=\"https:\/\/github.com\/symfony\/polyfill-mbstring\/blob\/9ad2f3c\/bootstrap.php#L14-L16\">als PHP 8.0 of hoger wordt uitgevoerd<\/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>Samenvatting<\/h2>\n<p>Dit artikel heeft je geleerd hoe we onze PHP code kunnen transpilen, waardoor we PHP 8.0 in de broncode kunnen gebruiken en een release kunnen maken die werkt op PHP 7.1. Transpiling doe je via <a href=\"https:\/\/github.com\/rectorphp\/rector\">Rector<\/a>, een PHP reconstructortool.<\/p>\n<p>Het transpilen van onze code maakt van ons betere ontwikkelaars, omdat we in de ontwikkelingsfase bugs sneller kunnen vinden en omdat we code kunnen produceren die gemakkelijker te lezen en begrijpen is.<\/p>\n<p>Transpiling stelt ons ook in staat om onze code te ontkoppelen met de specifieke PHP vereisten van het CMS. We kunnen hiermee de nieuwste versie van PHP gebruiken om een openbaar beschikbare WordPress plugin of Drupal module te maken, zonder dat we hiermee het aantal potenti\u00eble klanten aanzienlijk beperken.<\/p>\n<p><em>Heb je nog vragen over het transpilen van PHP? Laat het ons weten in de reacties hieronder!<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In ideale omstandigheden zouden we voor al onze sites PHP 8.0 (de laatste versie op het moment van schrijven) gebruiken en deze updaten zodra er een &#8230;<\/p>\n","protected":false},"author":196,"featured_media":40184,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[32,378],"topic":[865,856],"class_list":["post-40183","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-php","tag-programming","topic-php-functie","topic-php-leren"],"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>De ultieme handleiding voor het transpilen van PHP code<\/title>\n<meta name=\"description\" content=\"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.\" \/>\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\/nl\/blog\/transpilen-php\/\" \/>\n<meta property=\"og:locale\" content=\"nl_NL\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"De ultieme handleiding voor het transpilen van PHP code\" \/>\n<meta property=\"og:description\" content=\"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/\" \/>\n<meta property=\"article:published_time\" content=\"2021-10-06T12:18:40+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-06-07T07:01:48+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-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=\"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@Kinsta_NL\" \/>\n<meta name=\"twitter:label1\" content=\"Geschreven door\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Geschatte leestijd\" \/>\n\t<meta name=\"twitter:data2\" content=\"22 minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"De ultieme handleiding voor het transpilen van PHP code\",\"datePublished\":\"2021-10-06T12:18:40+00:00\",\"dateModified\":\"2023-06-07T07:01:48+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/\"},\"wordCount\":3888,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/nl\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg\",\"keywords\":[\"php\",\"programming\"],\"articleSection\":[\"Webdevelopment\"],\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/\",\"url\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/\",\"name\":\"De ultieme handleiding voor het transpilen van PHP code\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/nl\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg\",\"datePublished\":\"2021-10-06T12:18:40+00:00\",\"dateModified\":\"2023-06-07T07:01:48+00:00\",\"description\":\"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#breadcrumb\"},\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg\",\"width\":1460,\"height\":730,\"caption\":\"De ultieme handleiding voor het transpilen van PHP code\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/nl\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PHP leren\",\"item\":\"https:\/\/kinsta.com\/nl\/onderwerpen\/php-leren\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"De ultieme handleiding voor het transpilen van PHP code\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/nl\/#website\",\"url\":\"https:\/\/kinsta.com\/nl\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Snelle, veilige, premium hostingoplossingen\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/nl\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/nl\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"nl-NL\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/nl\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/nl\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinsta.com\/nl\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/nl\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/\",\"https:\/\/x.com\/Kinsta_NL\",\"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\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\/\/kinsta.com\/nl\/#\/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\/nl\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"De ultieme handleiding voor het transpilen van PHP code","description":"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.","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\/nl\/blog\/transpilen-php\/","og_locale":"nl_NL","og_type":"article","og_title":"De ultieme handleiding voor het transpilen van PHP code","og_description":"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.","og_url":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/","article_published_time":"2021-10-06T12:18:40+00:00","article_modified_time":"2023-06-07T07:01:48+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.","twitter_image":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg","twitter_creator":"@losoviz","twitter_site":"@Kinsta_NL","twitter_misc":{"Geschreven door":"Leonardo Losoviz","Geschatte leestijd":"22 minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"De ultieme handleiding voor het transpilen van PHP code","datePublished":"2021-10-06T12:18:40+00:00","dateModified":"2023-06-07T07:01:48+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/"},"wordCount":3888,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/nl\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg","keywords":["php","programming"],"articleSection":["Webdevelopment"],"inLanguage":"nl-NL","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/","url":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/","name":"De ultieme handleiding voor het transpilen van PHP code","isPartOf":{"@id":"https:\/\/kinsta.com\/nl\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg","datePublished":"2021-10-06T12:18:40+00:00","dateModified":"2023-06-07T07:01:48+00:00","description":"Zat van het werken met meerdere PHP versies? Leer hoe je je broncode in PHP 8.0 schrijft en deze transpilet naar vorige PHP versies.","breadcrumb":{"@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#breadcrumb"},"inLanguage":"nl-NL","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/"]}]},{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#primaryimage","url":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg","contentUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2021\/10\/transpiling-php.jpeg","width":1460,"height":730,"caption":"De ultieme handleiding voor het transpilen van PHP code"},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/nl\/blog\/transpilen-php\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/nl\/"},{"@type":"ListItem","position":2,"name":"PHP leren","item":"https:\/\/kinsta.com\/nl\/onderwerpen\/php-leren\/"},{"@type":"ListItem","position":3,"name":"De ultieme handleiding voor het transpilen van PHP code"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/nl\/#website","url":"https:\/\/kinsta.com\/nl\/","name":"Kinsta\u00ae","description":"Snelle, veilige, premium hostingoplossingen","publisher":{"@id":"https:\/\/kinsta.com\/nl\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/nl\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"nl-NL"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/nl\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/nl\/","logo":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinsta.com\/nl\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/nl\/wp-content\/uploads\/sites\/7\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/nl\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Kinsta-Nederland-476213452787823\/","https:\/\/x.com\/Kinsta_NL","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\/nl\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/kinsta.com\/nl\/#\/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\/nl\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts\/40183","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/comments?post=40183"}],"version-history":[{"count":3,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts\/40183\/revisions"}],"predecessor-version":[{"id":40187,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/posts\/40183\/revisions\/40187"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/translations\/en"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/translations\/es"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/translations\/it"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/translations\/fr"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/translations\/pt"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/translations\/de"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/translations\/nl"},{"href":"https:\/\/kinsta.com\/nl\/wp-json\/kinsta\/v1\/posts\/40183\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/media\/40184"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/media?parent=40183"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/tags?post=40183"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/nl\/wp-json\/wp\/v2\/topic?post=40183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}