{"id":43724,"date":"2021-10-05T10:04:40","date_gmt":"2021-10-05T08:04:40","guid":{"rendered":"https:\/\/kinsta.com\/?p=100177"},"modified":"2023-07-27T11:41:21","modified_gmt":"2023-07-27T10:41:21","slug":"transpilierung-php","status":"publish","type":"post","link":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/","title":{"rendered":"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code"},"content":{"rendered":"<p>Im Idealfall sollten wir <a href=\"https:\/\/kinsta.com\/de\/changelog\/php-8\/\">PHP 8.0<\/a> (die neueste Version zum Zeitpunkt der Erstellung dieses Artikels) f\u00fcr all unsere Webseiten verwenden und es aktualisieren, sobald eine neue Version ver\u00f6ffentlicht wird. Allerdings m\u00fcssen Entwickler oft mit fr\u00fcheren PHP-Versionen arbeiten, z. B. wenn sie ein \u00f6ffentliches Plugin f\u00fcr <a href=\"https:\/\/kinsta.com\/de\/blog\/was-ist-wordpress\/\">WordPress<\/a> erstellen oder mit veraltetem Code arbeiten, der eine Aktualisierung der Webserver-Umgebung verhindert.<\/p>\n<p>In diesen Situationen k\u00f6nnten wir die Hoffnung aufgeben, den neuesten PHP-Code zu verwenden. Aber es gibt eine bessere Alternative: Wir k\u00f6nnen unseren Quellcode immer noch mit PHP 8.0 schreiben und ihn in eine fr\u00fchere PHP-Version transpilieren &#8211; sogar in PHP 7.1.<\/p>\n<p>In diesem Leitfaden erf\u00e4hrst du alles, was du \u00fcber das Transpilieren von PHP-Code wissen musst.<\/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>Was ist Transpilieren?<\/h2>\n<p>Beim Transpilieren wird der Quellcode einer <a href=\"https:\/\/kinsta.com\/de\/blog\/beste-programmiersprache-lernen-sollte\/\">Programmiersprache<\/a> in einen gleichwertigen Quellcode der gleichen oder einer anderen Programmiersprache umgewandelt.<\/p>\n<p>Transpilieren ist kein neues Konzept in der Webentwicklung: Client-seitige Entwickler\/innen werden wahrscheinlich mit <a href=\"https:\/\/babeljs.io\/\">Babel<\/a>, einem Transpiler f\u00fcr JavaScript-Code, vertraut sein.<\/p>\n<p>Babel wandelt JavaScript-Code von der modernen ECMAScript 2015+ Version in eine Legacy-Version um, die mit \u00e4lteren Browsern kompatibel ist. Nehmen wir zum Beispiel eine ES2015-Pfeilfunktion:<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map((n) =&gt; n * 2);\n<\/code><\/pre>\n<p>&#8230;wandelt Babel es in seine ES5-Version um:<\/p>\n<pre><code class=\"language-js\">[2, 4, 6].map(function(n) {\n  return n * 2;\n});\n<\/code><\/pre>\n<h3>Was ist Transpiling PHP?<\/h3>\n<p>Eine potenzielle Neuheit in der Webentwicklung ist die M\u00f6glichkeit, serverseitigen Code, insbesondere PHP, zu transpilieren.<\/p>\n<p>Das Transpilieren von PHP funktioniert auf die gleiche Weise wie das Transpilieren von JavaScript: Der Quellcode einer <a href=\"https:\/\/kinsta.com\/de\/blog\/php-versionen\/\">modernen PHP-Version<\/a> wird in einen entsprechenden Code f\u00fcr eine \u00e4ltere PHP-Version umgewandelt.<\/p>\n<p>Nach dem gleichen Beispiel wie zuvor, eine Pfeilfunktion aus 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;kann in die entsprechende PHP 7.3 Version transpiliert werden:<\/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>Pfeilfunktionen k\u00f6nnen transpiliert werden, weil sie <a href=\"https:\/\/en.wikipedia.org\/wiki\/Syntactic_sugar\">syntaktischer Zucker<\/a> sind, d.h. eine neue Syntax, um ein bestehendes Verhalten zu erzeugen. Das ist die niedrig h\u00e4ngende Frucht.<\/p>\n<p>Es gibt jedoch auch neue Funktionen, die ein neues Verhalten erzeugen, und als solche gibt es keinen entsprechenden Code f\u00fcr fr\u00fchere PHP-Versionen. Das ist der Fall bei den <a href=\"https:\/\/kinsta.com\/de\/blog\/php-8\/#union-types-2-0\">Union-Typen<\/a>, die in PHP 8.0 eingef\u00fchrt wurden:<\/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 diesen Situationen kann die Transpilierung trotzdem durchgef\u00fchrt werden, solange das neue Feature f\u00fcr die Entwicklung, aber nicht f\u00fcr die Produktion ben\u00f6tigt wird. Dann k\u00f6nnen wir das Feature einfach ganz aus dem transpilierten Code entfernen, ohne dass es ernsthafte Konsequenzen hat.<\/p>\n<p>Ein solches Beispiel sind Union-Typen. Diese Funktion wird verwendet, um zu pr\u00fcfen, dass es keine Unstimmigkeiten zwischen dem Eingabetyp und dem bereitgestellten Wert gibt, was hilft, Fehler zu vermeiden. Wenn es einen Konflikt mit den Typen gibt, entsteht bereits in der Entwicklung ein Fehler, und wir sollten ihn abfangen und beheben, bevor der Code in die Produktion gelangt.<\/p>\n<p>Daher k\u00f6nnen wir es uns leisten, die Funktion aus dem Code f\u00fcr die Produktion zu entfernen:<\/p>\n<pre><code class=\"language-php\">function someFunction($param)\n{\n  \/\/ ...\n}\n<\/code><\/pre>\n<p>Wenn der Fehler in der Produktion trotzdem auftritt, wird die Fehlermeldung weniger pr\u00e4zise sein als bei Union-Typen. Dieser potenzielle Nachteil wird jedoch dadurch aufgewogen, dass wir \u00fcberhaupt Union-Typen verwenden k\u00f6nnen.<\/p>\n\n<h2>Vorteile der Transpilierung von PHP-Code<\/h2>\n<p>Mit Transpiling kann man eine Anwendung mit der neuesten PHP-Version programmieren und eine Version erstellen, die auch in Umgebungen mit \u00e4lteren PHP-Versionen funktioniert.<\/p>\n<p>Das kann besonders f\u00fcr Entwickler\/innen n\u00fctzlich sein, die Produkte f\u00fcr \u00e4ltere <a href=\"https:\/\/kinsta.com\/de\/blog\/content-management-system\/\">Content-Management-Systeme (CMS)<\/a> entwickeln. WordPress zum Beispiel unterst\u00fctzt <a href=\"https:\/\/wordpress.org\/about\/requirements\/\">offiziell immer noch PHP 5.6<\/a> (auch wenn es PHP 7.4+ empfiehlt). Der Prozentsatz der WordPress Seiten, die mit den PHP-Versionen 5.6 bis 7.2 laufen &#8211; die alle End-of-Life (EOL) sind, d.h. sie erhalten keine Sicherheitsupdates mehr &#8211; liegt bei beachtlichen 34,8 % und der Anteil der Seiten, die mit einer anderen PHP-Version als 8.0 laufen, bei satten 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=\"WordPress-Nutzungsstatistiken nach Version\" width=\"920\" height=\"720\"><\/a><figcaption class=\"wp-caption-text\">WordPress-Nutzungsstatistiken nach Version. Bildquelle: <a href=\"https:\/\/wordpress.org\/about\/stats\/\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress<\/a><\/figcaption><\/figure>\n<p>Daher werden WordPress-Themes und Plugins, die sich an ein globales Publikum richten, h\u00f6chstwahrscheinlich mit einer alten PHP-Version programmiert, um ihre m\u00f6gliche Reichweite zu erh\u00f6hen. Dank der Transpilierung k\u00f6nnen sie mit PHP 8.0 programmiert und trotzdem f\u00fcr eine \u00e4ltere PHP-Version ver\u00f6ffentlicht werden, um so viele Nutzer\/innen wie m\u00f6glich anzusprechen.<\/p>\n<p>Jede Anwendung, die eine andere als die neueste PHP-Version unterst\u00fctzen muss (selbst innerhalb der aktuell unterst\u00fctzten PHP-Versionen), kann davon profitieren.<\/p>\n<p>Dies ist bei Drupal der Fall, das <a href=\"https:\/\/www.drupal.org\/docs\/system-requirements\/php-requirements\">PHP 7.3 ben\u00f6tigt<\/a>. Dank der Transpilierung k\u00f6nnen Entwickler \u00f6ffentlich verf\u00fcgbare Drupal-Module mit PHP 8.0 erstellen und sie mit PHP 7.3 ver\u00f6ffentlichen.<\/p>\n<p>Ein weiteres Beispiel ist die Erstellung von benutzerdefiniertem Code f\u00fcr Kunden, die aus dem einen oder anderen Grund kein PHP 8.0 in ihrer Umgebung einsetzen k\u00f6nnen. Dank der Transpilierung k\u00f6nnen die Entwickler\/innen ihre Produkte trotzdem mit PHP 8.0 programmieren und sie in diesen Legacy-Umgebungen einsetzen.<\/p>\n<h3>transpilieren<\/h3>\n<p>PHP-Code kann immer transpiliert werden, es sei denn, er enth\u00e4lt eine PHP-Funktion, f\u00fcr die es in der vorherigen PHP-Version keine Entsprechung gibt.<\/p>\n<p>Das ist zum Beispiel bei den <a href=\"https:\/\/kinsta.com\/de\/blog\/php-8\/#attributes\">Attributen<\/a> der Fall, die in PHP 8.0 eingef\u00fchrt wurden:<\/p>\n<pre><code class=\"language-php\">#[SomeAttr]\nfunction someFunc() {}\n\n#[AnotherAttr]\nclass SomeClass {}\n<\/code><\/pre>\n<p>Im vorherigen Beispiel mit den Pfeilfunktionen konnte der Code transpiliert werden, weil Pfeilfunktionen syntaktischer Zucker sind. Attribute hingegen schaffen ein v\u00f6llig neues Verhalten. Dieses Verhalten k\u00f6nnte auch mit PHP 7.4 und niedriger reproduziert werden, aber nur durch manuelle Programmierung, d.h. nicht automatisch mit Hilfe eines Tools oder Prozesses (KI k\u00f6nnte eine L\u00f6sung bieten, aber so weit sind wir noch nicht).<\/p>\n<p>Attribute, die f\u00fcr die Entwicklung bestimmt sind, wie z. B. <code><a href=\"https:\/\/wiki.php.net\/rfc\/deprecated_attribute\">#[Deprecated]<\/a><\/code>, k\u00f6nnen auf dieselbe Weise entfernt werden wie Union-Typen. Aber Attribute, die das Verhalten der Anwendung in der Produktion ver\u00e4ndern, k\u00f6nnen nicht entfernt werden, und sie k\u00f6nnen auch nicht direkt transpiliert werden.<\/p>\n<p>Bis heute kann kein Transpiler Code mit PHP 8.0-Attributen automatisch in entsprechenden PHP 7.4-Code umwandeln. Wenn dein PHP-Code also Attribute verwenden muss, wird es schwierig oder unm\u00f6glich sein, ihn zu transpilieren.<\/p>\n<h3>PHP-Funktionen, die transpiliert werden k\u00f6nnen<\/h3>\n<p>Dies sind die Funktionen von PHP 7.1 und h\u00f6her, die derzeit transpiliert werden k\u00f6nnen. Wenn dein Code nur diese Funktionen verwendet, kannst du sicher sein, dass deine transpilierte Anwendung funktionieren wird. Andernfalls musst du pr\u00fcfen, ob der transpilierte Code zu Fehlern f\u00fchren wird.<\/p>\n<table>\n<thead>\n<tr>\n<th>PHP Version<\/th>\n<th>Features<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.1<\/td>\n<td>Alles<\/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>-Typ<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration72.new-features.php#migration72.new-features.param-type-widening\">Parameter-Typ-Erweiterung<\/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\">Referenzzuweisungen in <code>list()<\/code> \/ Array-Destrukturierung<\/a> (<em>au\u00dfer innerhalb von <code>foreach<\/code> \u2014\u00a0<a href=\"https:\/\/github.com\/rectorphp\/rector\/issues\/4376\">#4376<\/a><\/em>)<br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.heredoc\">Flexible Heredoc- und Nowdoc-Syntax<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.new-features.php#migration73.new-features.core.trailing-commas\">Nachgestellte Kommas in Funktionsaufrufen<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration73.other-changes.php#migration73.other-changes.core.setcookie\"><code>set(raw)cookie<\/code> akzeptiert das 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\">Typisierte Eigenschaften<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/functions.arrow.php\">Pfeilfunktionen<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.null-coalescing-assignment-operator\">Null verschmelzende Zuweisungsoperatoren<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.unpack-inside-array\">Entpacken innerhalb von Arrays<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.numeric-literal-separator\">Numerisches Literal-Trennzeichen<\/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> mit Array von Tag-Namen<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.php.net\/manual\/en\/migration74.new-features.php#migration74.new-features.core.type-variance\">kovariante R\u00fcckgabetypen und kontravariante Parametertypen<\/a><\/td>\n<\/tr>\n<tr>\n<td>8.0<\/td>\n<td>&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/union-types\">Union-Typen<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/mixed-type\"><code>mixed<\/code> Pseudo-Typ<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/static-return-type\"><code>static<\/code> R\u00fcckgabetyp<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/class-constant-on-objects\"><code>::class<\/code> magische Konstante f\u00fcr Objekte<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/match-expression\"><code>match<\/code> Ausdr\u00fccke<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/catch-exception-type\"><code>catch<\/code> Ausnahmen nur nach Typ<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/null-safe-operator\">Null-sicherer Operator<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/constructor-property-promotion\">Bef\u00f6rderung von Klassenkonstruktoreigenschaften<\/a><br \/>\n&#8211; <a href=\"https:\/\/php.watch\/versions\/8.0\/trailing-comma-parameter-use-list\">Anh\u00e4ngende Kommas in Parameterlisten und <code>use<\/code> Listen f\u00fcr Closures<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>PHP-Transpilierer<\/h2>\n<p>Derzeit gibt es nur ein Tool zum Transpilieren von PHP-Code: <a href=\"https:\/\/github.com\/rectorphp\/rector\">Rector<\/a>.<\/p>\n<p>Rector ist ein Tool zur PHP-Rekonstruktion, das PHP-Code anhand von programmierbaren Regeln umwandelt. Wir geben den Quellcode und die <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md\">Regeln ein, die ausgef\u00fchrt<\/a> werden sollen, und Rector wandelt den Code um.<\/p>\n<p>Rector wird \u00fcber die Kommandozeile bedient und \u00fcber Composer in das Projekt installiert. Nach der Ausf\u00fchrung gibt Rector einen &#8222;Diff&#8220; (Hinzuf\u00fcgungen in gr\u00fcn, Entfernungen in rot) des Codes vor und nach der Conversion aus:<\/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\" Ausgabe von Rector\" width=\"740\" height=\"480\"><\/a><figcaption class=\"wp-caption-text\">&#8222;diff&#8220; Ausgabe von Rector<\/figcaption><\/figure>\n<h2>Welche Version von PHP soll transpiliert werden?<\/h2>\n<p>Um Code zwischen verschiedenen PHP-Versionen zu transpilieren, m\u00fcssen die entsprechenden Regeln erstellt werden.<\/p>\n<p>Heute enth\u00e4lt die Rector-Bibliothek die meisten Regeln f\u00fcr das Transpilieren von Code im Bereich von PHP 8.0 bis 7.1. So k\u00f6nnen wir unseren PHP-Code zuverl\u00e4ssig bis zur Version 7.1 transpilieren.<\/p>\n<p>Es gibt auch <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp71\">Regeln f\u00fcr die Transpilierung von PHP 7.1 nach 7.0<\/a> und <a href=\"https:\/\/github.com\/rectorphp\/rector\/blob\/main\/docs\/rector_rules_overview.md#downgradephp70\">von 7.0 nach 5.6<\/a>, aber diese sind nicht vollst\u00e4ndig. Wir arbeiten daran, sie zu vervollst\u00e4ndigen, so dass wir PHP-Code eventuell bis zur Version 5.6 transpilieren k\u00f6nnen.<\/p>\n<h2>Transpilieren vs. Backporting<\/h2>\n<p>Das Backporting ist \u00e4hnlich wie das Transpilieren, aber einfacher. Bei der R\u00fcckportierung von Code werden nicht unbedingt neue Funktionen einer Sprache verwendet. Stattdessen kann dieselbe Funktion in einer \u00e4lteren Version der Sprache bereitgestellt werden, indem der entsprechende Code aus der neuen Version der Sprache einfach kopiert\/eingepasst\/angepasst wird.<\/p>\n<p>Zum Beispiel wurde die Funktion <code>str_contains<\/code> in PHP 8.0 eingef\u00fchrt. Die gleiche Funktion f\u00fcr PHP 7.4 und darunter kann einfach wie folgt implementiert werden:<\/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>Da die R\u00fcckportierung einfacher ist als die Transpilierung, sollten wir uns f\u00fcr diese L\u00f6sung entscheiden, wenn die R\u00fcckportierung die Aufgabe erf\u00fcllt.<\/p>\n<p>Was den Bereich zwischen PHP 8.0 und 7.1 angeht, k\u00f6nnen wir die Polyfill-Bibliotheken von <a href=\"https:\/\/symfony.com\/\">Symfony<\/a> verwenden:<\/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>Diese Bibliotheken backportieren die folgenden Funktionen, Klassen, Konstanten und Schnittstellen:<\/p>\n<table style=\"height: 1173px\" width=\"335\">\n<thead>\n<tr>\n<th>PHP Version<\/th>\n<th>Features<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>7.2<\/td>\n<td>Funktionen:<\/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>Konstanten:<\/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>Funktionen:<\/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>Ausnahmen:<\/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>Funktionen:<\/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>Schnittstellen:<\/p>\n<ul>\n<li><code>Stringable<\/code><\/li>\n<\/ul>\n<p>Klasse:<\/p>\n<ul>\n<li><code>ValueError<\/code><\/li>\n<li><code>UnhandledMatchError<\/code><\/li>\n<\/ul>\n<p>Konstanten:<\/p>\n<ul>\n<li><code>FILTER_VALIDATE_BOOL<\/code><\/li>\n<\/ul>\n<p>Funktionen:<\/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>Beispiele f\u00fcr transpiliertes PHP<\/h2>\n<p>Sehen wir uns einige Beispiele f\u00fcr transpilierten PHP-Code und einige Pakete an, die vollst\u00e4ndig transpiliert wurden.<\/p>\n<h3>PHP-Code<\/h3>\n<p>Der <code>match<\/code>-Ausdruck wurde in <a href=\"https:\/\/kinsta.com\/de\/blog\/php-8\/#match-expression\">PHP 8.0 eingef\u00fchrt<\/a>. Dieser Quellcode:<\/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;wird mit Hilfe des <code>switch<\/code>-Operators in die entsprechende PHP 7.4-Version transpiliert:<\/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>Der <a href=\"https:\/\/kinsta.com\/de\/blog\/php-8\/#nullsafe-operator\">nullsafe-Operator<\/a> wurde ebenfalls in PHP 8.0 eingef\u00fchrt:<\/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>Der umgesetzte Code muss den Wert der Operation zun\u00e4chst einer neuen Variablen zuweisen, um zu vermeiden, dass die Operation zweimal ausgef\u00fchrt wird:<\/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>Die Funktion zur <a href=\"https:\/\/kinsta.com\/de\/blog\/php-8\/#constructor-property-promotion\">F\u00f6rderung von Konstruktoreigenschaften<\/a>, die ebenfalls in PHP 8.0 eingef\u00fchrt wurde, erm\u00f6glicht es Entwicklern, weniger Code zu schreiben:<\/p>\n<pre><code class=\"language-php\">class QueryResolver\n{\n  function __construct(protected QueryFormatter $queryFormatter)\n  {\n  }\n}\n<\/code><\/pre>\n<p>Beim Transpilieren f\u00fcr PHP 7.4 wird der gesamte Code erzeugt:<\/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>Der oben transpilierte Code enth\u00e4lt <a href=\"https:\/\/kinsta.com\/de\/blog\/php-7-4\/#typed-properties\">typisierte Eigenschaften<\/a>, die in PHP 7.4 eingef\u00fchrt wurden. Durch die \u00dcbertragung des Codes auf PHP 7.3 werden sie durch docblocks ersetzt:<\/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-Pakete<\/h3>\n<p>Die folgenden Bibliotheken werden f\u00fcr die Produktion umgesetzt:<\/p>\n<table>\n<thead>\n<tr>\n<th>Library\/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 \/>\nPHP-Rekonstruktionswerkzeug, das die Transpilierung erm\u00f6glicht<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector-src\">Quellcode<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/rectorphp\/rector\">Transpilierter Code<\/a><br \/>\n&#8211; <a href=\"https:\/\/getrector.org\/blog\/prefixed-rector-by-default\">Anmerkungen<\/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\/\">Einfache Kodierungsstandards<\/a><br \/>\nWerkzeug, um PHP-Code an eine Reihe von Regeln zu binden<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/symplify\/symplify\/tree\/main\/packages\/easy-coding-standard\">Quellcode<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/symplify\/easy-coding-standard\">Transpilierter Code<\/a><br \/>\n&#8211; <a href=\"https:\/\/tomasvotruba.com\/blog\/introducing-ecs-prefixed-and-downgraded-to-php-71\/\">Anmerkungen<\/a><\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/graphql-api.com\" target=\"_blank\" rel=\"noopener noreferrer\">GraphQL API f\u00fcr WordPress<\/a><br \/>\nPlugin zur Bereitstellung eines GraphQL-Servers f\u00fcr WordPress<\/td>\n<td>&#8211; <a href=\"https:\/\/github.com\/leoloso\/PoP\/tree\/master\/layers\/GraphQLAPIForWP\/plugins\/graphql-api-for-wp\">Quellcode<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp-dist\/\">Transpilierter Code<\/a><br \/>\n&#8211; <a href=\"https:\/\/graphql-api.com\/blog\/the-plugin-is-now-transpiled-from-php-80-to-71\/\">Anmerkungen<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Vor- und Nachteile der Transpilierung von PHP<\/h2>\n<p>Der Vorteil des Transpilierens von PHP wurde bereits beschrieben: Es erm\u00f6glicht die Verwendung von PHP 8.0 (d. h. der neuesten Version von PHP) im Quellcode, der dann in eine niedrigere Version von PHP f\u00fcr die Produktion umgewandelt wird, um in einer Legacy-Anwendung oder -Umgebung zu laufen.<\/p>\n<p>Auf diese Weise werden wir zu besseren Entwicklern, die Code mit h\u00f6herer Qualit\u00e4t produzieren. Das liegt daran, dass unser Quellcode die Union-Typen von PHP 8.0, die typisierten Eigenschaften von PHP 7.4 und die verschiedenen Typen und Pseudo-Typen, die jeder neuen PHP-Version hinzugef\u00fcgt wurden (<code>mixed<\/code> von PHP 8.0, <code>object<\/code> von PHP 7.2), neben anderen modernen Funktionen von PHP nutzen kann.<\/p>\n<p>Mithilfe dieser Funktionen k\u00f6nnen wir Fehler w\u00e4hrend der Entwicklung besser erkennen und Code schreiben, der leichter zu lesen ist.<\/p>\n<p>Werfen wir nun einen Blick auf die Nachteile.<\/p>\n<h3>Es muss kodiert und gewartet werden<\/h3>\n<p>Rector kann Code automatisch transpilieren, aber der Prozess wird wahrscheinlich einige manuelle Eingaben erfordern, damit er mit unserem spezifischen Setup funktioniert.<\/p>\n<h3>Bibliotheken von Drittanbietern m\u00fcssen ebenfalls transpiliert werden<\/h3>\n<p>Dies wird immer dann zum Problem, wenn beim Transpilieren Fehler auftreten, da wir dann in den Quellcode eindringen m\u00fcssen, um die m\u00f6gliche Ursache herauszufinden. Wenn das Problem behoben werden kann und das Projekt quelloffen ist, m\u00fcssen wir einen Pull-Request einreichen. Wenn die Bibliothek nicht quelloffen ist, k\u00f6nnten wir auf ein Hindernis sto\u00dfen.<\/p>\n<h3>Rector informiert uns nicht, wenn der Code nicht transpiliert werden kann<\/h3>\n<p>Wenn der Quellcode PHP 8.0-Attribute oder ein anderes Feature enth\u00e4lt, das nicht transpiliert werden kann, k\u00f6nnen wir nicht fortfahren. Rector pr\u00fcft diese Bedingung jedoch nicht, so dass wir dies manuell tun m\u00fcssen. Bei unserem eigenen Quellcode ist das vielleicht kein gro\u00dfes Problem, da wir damit bereits vertraut sind, aber bei Abh\u00e4ngigkeiten von Drittanbietern k\u00f6nnte es zu einem Hindernis werden.<\/p>\n<h3>Debugging-Informationen verwenden den umgesetzten Code, nicht den Quellcode<\/h3>\n<p>Wenn die Anwendung in der Produktion eine Fehlermeldung mit einem Stack-Trace erzeugt, verweist die Zeilennummer auf den transpilierten Code. Um die entsprechende Zeilennummer im Quellcode zu finden, m\u00fcssen wir vom transpilierten in den Originalcode zur\u00fcckkonvertieren.<\/p>\n<h3>Der transpilierte Code muss auch vorangestellt werden<\/h3>\n<p>Unser transpiliertes Projekt und eine andere Bibliothek, die ebenfalls in der Produktionsumgebung installiert ist, k\u00f6nnten dieselbe Drittanbieter-Abh\u00e4ngigkeit verwenden. Diese Drittanbieter-Abh\u00e4ngigkeit wird f\u00fcr unser Projekt transpiliert, w\u00e4hrend der urspr\u00fcngliche Quellcode f\u00fcr die andere Bibliothek beibehalten wird. Daher muss die transpilierte Version mit <a href=\"https:\/\/github.com\/humbug\/php-scoper\">PHP-Scoper<\/a>, <a href=\"https:\/\/github.com\/BrianHenryIE\/strauss\">Strauss<\/a> oder einem anderen Tool vorangestellt werden, um m\u00f6gliche Konflikte zu vermeiden.<\/p>\n<h3>Die Transpilierung muss w\u00e4hrend der kontinuierlichen Integration (CI) erfolgen<\/h3>\n<p>Da der transpilierte Code nat\u00fcrlich den Quellcode \u00fcberschreibt, sollten wir den Transpilierungsprozess nicht auf unseren Entwicklungscomputern ausf\u00fchren, da sonst die Gefahr von Seiteneffekten besteht. Es ist besser, den Prozess w\u00e4hrend eines CI-Laufs auszuf\u00fchren (mehr dazu weiter unten).<\/p>\n<h2>Wie man PHP transpiliert<\/h2>\n<p>Zun\u00e4chst m\u00fcssen wir Rector in unserem Entwicklungsprojekt installieren:<\/p>\n<pre><code class=\"language-bash\">composer require rector\/rector --dev\n<\/code><\/pre>\n<p>Anschlie\u00dfend erstellen wir eine Konfigurationsdatei <code>rector.php<\/code> im Stammverzeichnis des Projekts, die die erforderlichen Regels\u00e4tze enth\u00e4lt. Um den Code von PHP 8.0 auf 7.1 herunterzustufen, verwenden wir diese 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>Um sicherzustellen, dass der Prozess wie erwartet ausgef\u00fchrt wird, k\u00f6nnen wir den <code>process<\/code> Befehl\u00a0von Rector im Trockenmodus ausf\u00fchren und die zu verarbeitende(n) Datei(en) \u00fcbergeben (in diesem Fall alle Dateien im Ordner <code>src\/<\/code>):<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src --dry-run\n<\/code><\/pre>\n<p>Um die Transpilierung durchzuf\u00fchren, f\u00fchren wir den <code>process<\/code> Befehl von Rector aus, der die Dateien an ihrem bisherigen Speicherort ver\u00e4ndert:<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src\n<\/code><\/pre>\n<p>Bitte beachte: Wenn wir den <code>rector process<\/code> in unseren Entwicklungscomputern laufen lassen, wird der Quellcode an Ort und Stelle konvertiert, unter <code>src\/<\/code>. Wir wollen jedoch den konvertierten Code an einem anderen Ort erzeugen, um den Quellcode beim Downgrade nicht zu \u00fcberschreiben. Aus diesem Grund ist es am besten, den Prozess w\u00e4hrend der kontinuierlichen Integration auszuf\u00fchren.<\/p>\n<h2>Optimieren des Transpilierungsprozesses<\/h2>\n<p>Um ein transpiliertes Deliverable f\u00fcr die Produktion zu erstellen, muss nur der Code f\u00fcr die Produktion konvertiert werden; Code, der nur f\u00fcr die Entwicklung ben\u00f6tigt wird, kann \u00fcbersprungen werden. Das bedeutet, dass wir es vermeiden k\u00f6nnen, alle Tests (sowohl f\u00fcr unser Projekt als auch f\u00fcr seine Abh\u00e4ngigkeiten) und alle Abh\u00e4ngigkeiten f\u00fcr die Entwicklung zu transpilieren.<\/p>\n<p>Was die Tests betrifft, so wissen wir bereits, wo sich die Tests f\u00fcr unser Projekt befinden &#8211; zum Beispiel im Ordner <code>tests\/<\/code>. Wir m\u00fcssen auch herausfinden, wo sich die Tests f\u00fcr die Abh\u00e4ngigkeiten befinden &#8211; zum Beispiel in den Unterordnern <code>tests\/<\/code>, <code>test\/<\/code> und <code>Test\/<\/code> (f\u00fcr verschiedene Bibliotheken). Dann weisen wir Rector an, die Bearbeitung dieser Ordner zu \u00fcberspringen:<\/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>Was die Abh\u00e4ngigkeiten angeht, wei\u00df Composer, welche f\u00fcr die Entwicklung (unter dem Eintrag <code>require-dev<\/code> in <code>composer.json<\/code>) und welche f\u00fcr die Produktion (unter dem Eintrag <code>require<\/code>) sind.<\/p>\n<p>Um vom Composer die Pfade aller Abh\u00e4ngigkeiten f\u00fcr die Produktion zu erhalten, f\u00fchren wir aus:<\/p>\n<pre><code class=\"language-bash\">composer info --path --no-dev\n<\/code><\/pre>\n<p>Mit diesem Befehl wird eine Liste der Abh\u00e4ngigkeiten mit Namen und Pfad erstellt, etwa so:<\/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>Wir k\u00f6nnen alle Pfade extrahieren und in den Rector-Befehl eingeben, der dann den <code>src\/<\/code>-Ordner unseres Projekts sowie die Ordner mit allen Abh\u00e4ngigkeiten f\u00fcr die Produktion verarbeitet:<\/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>Eine weitere Verbesserung kann verhindern, dass Rector die Abh\u00e4ngigkeiten verarbeitet, die bereits die Ziel-PHP-Version verwenden. Wenn eine Bibliothek mit PHP 7.1 (oder einer niedrigeren Version) programmiert wurde, muss sie nicht nach PHP 7.1 transpiliert werden.<\/p>\n<p>Um dies zu erreichen, k\u00f6nnen wir die Liste der Bibliotheken erhalten, die PHP 7.2 und h\u00f6her ben\u00f6tigen, und nur diese verarbeiten. Die Namen all dieser Bibliotheken erhalten wir \u00fcber den <code>why-not<\/code>-Befehl von Composer, etwa so:<\/p>\n<pre><code class=\"language-bash\">composer why-not php \"7.1.*\" | grep -o \"\\S*\\\/\\S*\"\n<\/code><\/pre>\n<p>Da dieser Befehl nicht mit dem <code>--no-dev<\/code> Flag funktioniert, m\u00fcssen wir, um nur die Abh\u00e4ngigkeiten f\u00fcr die Produktion einzubeziehen, zuerst die Abh\u00e4ngigkeiten f\u00fcr die Entwicklung entfernen und den Autoloader neu generieren, den Befehl ausf\u00fchren und sie dann wieder hinzuf\u00fcgen:<\/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>Der Befehl <code>info --path<\/code> von Composer gibt den Pfad eines Pakets in diesem Format aus:<\/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>Wir f\u00fchren diesen Befehl f\u00fcr alle Eintr\u00e4ge in unserer Liste aus, um alle Pfade zum Umsetzen zu erhalten:<\/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>Schlie\u00dflich stellen wir diese Liste dem Rector zur Verf\u00fcgung (sowie den Ordner <code>src\/<\/code> des Projekts):<\/p>\n<pre><code class=\"language-bash\">vendor\/bin\/rector process src $paths\n<\/code><\/pre>\n<h2>Zu vermeidende Fallstricke beim Transpilieren von Code<\/h2>\n<p>Das Transpilieren von Code ist eine Kunst, die oft projektspezifische Anpassungen erfordert. Sehen wir uns ein paar Probleme an, auf die wir sto\u00dfen k\u00f6nnen.<\/p>\n<h3>Verkettete Regeln werden nicht immer abgearbeitet<\/h3>\n<p>Von einer verketteten Regel spricht man, wenn eine Regel den Code umwandeln muss, der von einer vorherigen Regel erzeugt wurde.<\/p>\n<p>Zum Beispiel enth\u00e4lt die Bibliothek <code>symfony\/cache<\/code> <a href=\"https:\/\/github.com\/symfony\/cache\/blob\/be5707f\/CacheItem.php#L115\">diesen 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>Beim Transpilieren von PHP 7.4 nach 7.3 m\u00fcssen zwei \u00c4nderungen am Funktions-<code>tag<\/code> vorgenommen werden:<\/p>\n<ul>\n<li>Der R\u00fcckgabetyp <code>ItemInterface<\/code> muss aufgrund der Regel <code><a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeCovariantReturnTypeRector.php\">DowngradeCovariantReturnTypeRector<\/a><\/code> zun\u00e4chst in <code>self<\/code> umgewandelt werden.<\/li>\n<li>Der R\u00fcckgabetyp <code>self<\/code> muss dann aufgrund der Regel <a href=\"https:\/\/github.com\/rectorphp\/rector-src\/blob\/f451b0b\/rules\/DowngradePhp74\/Rector\/ClassMethod\/DowngradeSelfTypeDeclarationRector.php\"><code>DowngradeSelfTypeDeclarationRecto<\/code>r<\/a> entfernt werden.<\/li>\n<\/ul>\n<p>Das Endergebnis sollte dieses hier sein:<\/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>Der Rector gibt jedoch nur die Zwischenstufe aus:<\/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>Das Problem ist, dass <a href=\"https:\/\/github.com\/rectorphp\/rector\/issues\/5962\">Rector nicht immer kontrollieren kann, in welcher Reihenfolge die Regeln angewendet<\/a> werden.<\/p>\n<p>Die L\u00f6sung besteht darin, herauszufinden, welche verketteten Regeln unbearbeitet geblieben sind, und einen neuen Rector-Lauf auszuf\u00fchren, um sie anzuwenden.<\/p>\n<p>Um die verketteten Regeln zu identifizieren, f\u00fchren wir Rector zweimal auf dem Quellcode aus, etwa so:<\/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>Beim ersten Mal f\u00fchren wir Rector wie erwartet aus, um die Transpilierung durchzuf\u00fchren. Beim zweiten Mal verwenden wir das Flag <code>--dry-run<\/code>, um festzustellen, ob noch \u00c4nderungen vorgenommen werden m\u00fcssen. Wenn ja, wird der Befehl mit einem Fehlercode beendet und die &#8222;diff&#8220;-Ausgabe zeigt an, welche Regel(n) noch angewendet werden k\u00f6nnen. Das w\u00fcrde bedeuten, dass der erste Durchlauf nicht vollst\u00e4ndig war und einige verkettete Regeln nicht verarbeitet wurden.<\/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 mit dem Flag -dry-run ausf\u00fchren\" width=\"901\" height=\"903\"><\/a><figcaption class=\"wp-caption-text\">Rector mit dem Flag -dry-run ausf\u00fchren<\/figcaption><\/figure>\n<p>\u00a0<\/p>\n<p>Sobald wir die nicht angewendete(n) verkettete(n) Regel(n) identifiziert haben, k\u00f6nnen wir eine weitere Rector-Konfigurationsdatei erstellen &#8211; zum Beispiel <code>rector-chained-rule.php<\/code>, die die fehlende Regel ausf\u00fchrt. Anstatt einen ganzen Satz von Regeln f\u00fcr alle Dateien unter <code>src\/<\/code> auszuf\u00fchren, k\u00f6nnen wir die fehlende Regel auf die Datei anwenden, auf die sie angewendet werden soll:<\/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>Schlie\u00dflich weisen wir Rector im zweiten Durchgang an, die neue Konfigurationsdatei \u00fcber input <code>--config<\/code> zu verwenden:<\/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-Abh\u00e4ngigkeiten k\u00f6nnen inkonsistent sein<\/h3>\n<p>Bibliotheken k\u00f6nnen eine Abh\u00e4ngigkeit als f\u00fcr die Entwicklung bestimmt deklarieren (z. B. unter <code>require-dev<\/code> in der <code>composer.json<\/code>), aber dennoch Code von ihnen f\u00fcr die Produktion referenzieren (z. B. in einigen Dateien unter <code>src\/<\/code>, nicht unter <code>tests\/<\/code>).<\/p>\n<p>Normalerweise ist das kein Problem, denn dieser Code wird in der Produktion nicht geladen, so dass es in der Anwendung nie zu einem Fehler kommt. Wenn Rector jedoch den Quellcode und seine Abh\u00e4ngigkeiten verarbeitet, pr\u00fcft er, ob der gesamte referenzierte Code geladen werden kann. Rector gibt einen Fehler aus, wenn eine Datei auf einen Teil des Codes einer nicht installierten Bibliothek verweist (weil er nur f\u00fcr die Entwicklung ben\u00f6tigt wird).<\/p>\n<p>Zum Beispiel implementiert die Klasse <code><a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/8f03a1f\/src\/Symfony\/Component\/Cache\/Messenger\/EarlyExpirationHandler.php\">EarlyExpirationHandler<\/a><\/code> aus der Cache-Komponente von Symfony die Schnittstelle <code><a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/191cb52\/src\/Symfony\/Component\/Messenger\/Handler\/MessageHandlerInterface.php\">MessageHandlerInterface<\/a><\/code> aus der Messenger-Komponente:<\/p>\n<pre><code class=\"language-php\">class EarlyExpirationHandler implements MessageHandlerInterface\n{\n    \/\/...\n}\n<\/code><\/pre>\n<p>Allerdings deklariert <code>symfony\/cache<\/code> <code>symfony\/messenger<\/code> als eine <a href=\"https:\/\/github.com\/symfony\/symfony\/blob\/6fe82d8\/src\/Symfony\/Component\/Cache\/composer.json#L43\">Abh\u00e4ngigkeit f\u00fcr die Entwicklung<\/a>. Wenn du Rector auf einem Projekt ausf\u00fchrst, das von <code>symfony\/cache<\/code> abh\u00e4ngt, wird ein Fehler ausgegeben:<\/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>Es gibt drei L\u00f6sungen f\u00fcr dieses Problem:<\/p>\n<ol>\n<li>\u00dcberspringe in der Rector-Konfiguration die Verarbeitung der Datei, die auf dieses St\u00fcck Code verweist:<\/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>Lade die fehlende Bibliothek herunter und f\u00fcge ihren Pfad hinzu, damit sie von Rector automatisch geladen wird:<\/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>Lass dein Projekt von der fehlenden Bibliothek f\u00fcr die Produktion abh\u00e4ngen:<\/li>\n<\/ol>\n<pre><code class=\"language-bash\">composer require symfony\/messenger\n<\/code><\/pre>\n<h2>Transpilieren und kontinuierliche Integration<\/h2>\n<p>Wie bereits erw\u00e4hnt, m\u00fcssen wir auf unseren Entwicklungscomputern das Flag <code>--dry-run<\/code> verwenden, wenn wir Rector ausf\u00fchren, da sonst der Quellcode durch den transpilierten Code \u00fcberschrieben wird. Aus diesem Grund ist es besser, den eigentlichen Transpilierungsprozess w\u00e4hrend der kontinuierlichen Integration (CI) auszuf\u00fchren, bei der wir tempor\u00e4re Runner zur Ausf\u00fchrung des Prozesses starten k\u00f6nnen.<\/p>\n<p>Ein idealer Zeitpunkt f\u00fcr die Durchf\u00fchrung des Transpilierens ist die Erstellung des Releases f\u00fcr unser Projekt. Der <a href=\"https:\/\/github.com\/GraphQLAPI\/graphql-api-for-wp\/blob\/3f9fd52ddf318b9fbb33653de8acfec8dc4b4665\/.github\/workflows\/main.yml\">folgende Code<\/a> ist zum Beispiel ein Workflow f\u00fcr <a href=\"https:\/\/github.com\/features\/actions\">GitHub Actions<\/a>, der die Ver\u00f6ffentlichung eines WordPress-Plugins erstellt:<\/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>Dieser Arbeitsablauf enth\u00e4lt eine Standardprozedur, um ein <a href=\"https:\/\/leoloso.com\/posts\/github-action-to-release-wp-plugin\/\">WordPress-Plugin \u00fcber GitHub Actions<\/a> zu ver\u00f6ffentlichen. Der neue Zusatz, den Code des Plugins von PHP 7.4 auf 7.1 zu transpilieren, geschieht in diesem Schritt:<\/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>Zusammengenommen f\u00fchrt dieser Arbeitsablauf nun die folgenden Schritte durch:<\/p>\n<ol>\n<li>Checkt den Quellcode f\u00fcr ein WordPress-Plugin aus dem Repository aus, das mit PHP 7.4 geschrieben wurde<\/li>\n<li>Installiert die Composer-Abh\u00e4ngigkeiten<\/li>\n<li>Transponiert den Code von PHP 7.4 auf 7.1<\/li>\n<li>\u00c4ndert den Eintrag &#8222;Requires PHP&#8220; im Header der Hauptdatei des Plugins von <code>\"7.4\"<\/code> auf <code>\"7.1\"<\/code>.<\/li>\n<li>Entfernt die f\u00fcr die Entwicklung ben\u00f6tigten Abh\u00e4ngigkeiten<\/li>\n<li>Erstellt die .zip-Datei des Plugins und schlie\u00dft dabei alle nicht ben\u00f6tigten Dateien aus<\/li>\n<li>L\u00e4dt die .zip-Datei als Release-Asset hoch (und zus\u00e4tzlich als Artefakt in die GitHub-Aktion)<\/li>\n<\/ol>\n<h2>Testen des transponierten Codes<\/h2>\n<p>Wenn der Code nach PHP 7.1 transpiliert wurde, woher wissen wir dann, dass er gut funktioniert? Oder anders gesagt, woher wissen wir, dass er gr\u00fcndlich umgewandelt wurde und keine Reste von h\u00f6heren PHP-Versionen zur\u00fcckgeblieben sind?<\/p>\n<p>\u00c4hnlich wie beim Transpilieren des Codes k\u00f6nnen wir die L\u00f6sung innerhalb eines CI-Prozesses implementieren. Die Idee ist, die Umgebung des Runners mit PHP 7.1 einzurichten und einen Linter auf den transpilierten Code anzuwenden. Wenn ein Teil des Codes nicht mit PHP 7.1 kompatibel ist (z. B. eine typisierte Eigenschaft aus PHP 7.4, die nicht umgewandelt wurde), gibt der Linter einen Fehler aus.<\/p>\n<p>Ein Linter f\u00fcr PHP, der gut funktioniert, ist <a href=\"https:\/\/github.com\/php-parallel-lint\/PHP-Parallel-Lint\">PHP Parallel Lint<\/a>. Wir k\u00f6nnen diese Bibliothek als Abh\u00e4ngigkeit f\u00fcr die Entwicklung in unserem Projekt installieren oder sie durch den CI-Prozess als eigenst\u00e4ndiges Composer-Projekt installieren lassen:<\/p>\n<pre><code class=\"language-bash\">composer create-project php-parallel-lint\/php-parallel-lint\n<\/code><\/pre>\n<p>Wenn der Code PHP 7.2 und h\u00f6her enth\u00e4lt, wird PHP Parallel Lint einen Fehler <a href=\"https:\/\/github.com\/leoloso\/PoP\/runs\/2751846434?check_suite_focus=true\">wie diesen<\/a> ausgeben:<\/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>F\u00fcgen wir den Linter in den Workflow unseres CIs ein. Die Schritte, die wir ausf\u00fchren m\u00fcssen, um den Code von PHP 8.0 nach 7.1 zu transpilieren und zu testen, sind:<\/p>\n<ol>\n<li>Schau dir den Quellcode an<\/li>\n<li>Lasse die Umgebung PHP 8.0 laufen, damit Rector den Quellcode interpretieren kann<\/li>\n<li>Transpiliere den Code nach PHP 7.1<\/li>\n<li>Installiere das PHP-Linter-Tool<\/li>\n<li>\u00c4ndere die PHP-Version in der Umgebung auf 7.1<\/li>\n<li>F\u00fchre den Linter mit dem transpilierten Code aus<\/li>\n<\/ol>\n<p>Dieser <a href=\"https:\/\/github.com\/leoloso\/PoP\/blob\/b93d3e35cb59e0281b45899fd82231c3d8cbbe25\/.github\/workflows\/downgrade_php_tests.yml\">GitHub Action-Workflow<\/a> erledigt diese Aufgabe:<\/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>Bitte beachte, dass mehrere bootstrap80.php-Dateien aus den Polyfill-Bibliotheken von Symfony (die nicht transpiliert werden m\u00fcssen) vom Linter ausgeschlossen werden m\u00fcssen. Diese Dateien enthalten PHP 8.0, so dass der Linter bei der Verarbeitung dieser Dateien Fehler ausl\u00f6sen w\u00fcrde. Der Ausschluss dieser Dateien ist jedoch sicher, da sie in der Produktion nur geladen werden, wenn <a href=\"https:\/\/github.com\/symfony\/polyfill-mbstring\/blob\/9ad2f3c\/bootstrap.php#L14-L16\">PHP 8.0 oder h\u00f6her l\u00e4uft<\/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>Zusammenfassung<\/h2>\n<p>In diesem Artikel haben wir gelernt, wie wir unseren PHP-Code transpilieren k\u00f6nnen. So k\u00f6nnen wir PHP 8.0 im Quellcode verwenden und eine Version erstellen, die mit PHP 7.1 funktioniert. Die Transpilierung erfolgt mit <a href=\"https:\/\/github.com\/rectorphp\/rector\">Rector<\/a>, einem Tool zur PHP-Rekonstruktion.<\/p>\n<p>Das Transpilieren unseres Codes macht uns zu besseren Entwicklern, da wir Fehler in der Entwicklung besser erkennen und Code produzieren k\u00f6nnen, der nat\u00fcrlich leichter zu lesen und zu verstehen ist.<\/p>\n<p>Das Transpilieren erm\u00f6glicht es uns auch, unseren Code mit spezifischen PHP-Anforderungen vom CMS zu entkoppeln. Das k\u00f6nnen wir jetzt tun, wenn wir die neueste Version von PHP verwenden wollen, um ein \u00f6ffentlich zug\u00e4ngliches WordPress-Plugin oder Drupal-Modul zu erstellen, ohne unsere Nutzerbasis stark einzuschr\u00e4nken.<\/p>\n<p><em>Hast du noch Fragen zum Transpilieren von PHP? Lass es uns im Kommentarbereich wissen!<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im Idealfall sollten wir PHP 8.0 (die neueste Version zum Zeitpunkt der Erstellung dieses Artikels) f\u00fcr all unsere Webseiten verwenden und es aktualisieren, sobald eine neue &#8230;<\/p>\n","protected":false},"author":196,"featured_media":43725,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[38,411],"topic":[959,970],"class_list":["post-43724","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-php","tag-programming","topic-php-lernen","topic-php-funktion"],"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>Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code<\/title>\n<meta name=\"description\" content=\"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.\" \/>\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\/de\/blog\/transpilierung-php\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code\" \/>\n<meta property=\"og:description\" content=\"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/\" \/>\n<meta property=\"article:published_time\" content=\"2021-10-05T08:04:40+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-07-27T10:41:21+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-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=\"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg\" \/>\n<meta name=\"twitter:creator\" content=\"@losoviz\" \/>\n<meta name=\"twitter:site\" content=\"@Kinsta_DE\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leonardo Losoviz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"21\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/\"},\"author\":{\"name\":\"Leonardo Losoviz\",\"@id\":\"https:\/\/kinsta.com\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\"},\"headline\":\"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code\",\"datePublished\":\"2021-10-05T08:04:40+00:00\",\"dateModified\":\"2023-07-27T10:41:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/\"},\"wordCount\":3938,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/de\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg\",\"keywords\":[\"php\",\"programming\"],\"articleSection\":[\"Webentwicklung\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/\",\"url\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/\",\"name\":\"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/de\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg\",\"datePublished\":\"2021-10-05T08:04:40+00:00\",\"dateModified\":\"2023-07-27T10:41:21+00:00\",\"description\":\"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg\",\"width\":1460,\"height\":730,\"caption\":\"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PHP lernen\",\"item\":\"https:\/\/kinsta.com\/de\/thema\/php-lernen\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/de\/#website\",\"url\":\"https:\/\/kinsta.com\/de\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Schnelle, sichere und hochwertige Hosting-L\u00f6sungen\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/de\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/de\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/de\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/de\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/\",\"https:\/\/x.com\/Kinsta_DE\",\"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\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238\",\"name\":\"Leonardo Losoviz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/kinsta.com\/de\/#\/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\/de\/blog\/author\/leonardolosoviz\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code","description":"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.","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\/de\/blog\/transpilierung-php\/","og_locale":"de_DE","og_type":"article","og_title":"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code","og_description":"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.","og_url":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/","article_published_time":"2021-10-05T08:04:40+00:00","article_modified_time":"2023-07-27T10:41:21+00:00","og_image":[{"width":1460,"height":730,"url":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg","type":"image\/jpeg"}],"author":"Leonardo Losoviz","twitter_card":"summary_large_image","twitter_description":"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.","twitter_image":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg","twitter_creator":"@losoviz","twitter_site":"@Kinsta_DE","twitter_misc":{"Verfasst von":"Leonardo Losoviz","Gesch\u00e4tzte Lesezeit":"21\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/"},"author":{"name":"Leonardo Losoviz","@id":"https:\/\/kinsta.com\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238"},"headline":"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code","datePublished":"2021-10-05T08:04:40+00:00","dateModified":"2023-07-27T10:41:21+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/"},"wordCount":3938,"commentCount":0,"publisher":{"@id":"https:\/\/kinsta.com\/de\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg","keywords":["php","programming"],"articleSection":["Webentwicklung"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/","url":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/","name":"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code","isPartOf":{"@id":"https:\/\/kinsta.com\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg","datePublished":"2021-10-05T08:04:40+00:00","dateModified":"2023-07-27T10:41:21+00:00","description":"Bist du es leid, mit verschiedenen PHP-Versionen zu arbeiten? Lerne, wie du mit PHP 8.0 Quellcode schreibst und ihn in fr\u00fchere PHP-Versionen transpilierst.","breadcrumb":{"@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#primaryimage","url":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg","contentUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2021\/10\/transpilierung-php.jpeg","width":1460,"height":730,"caption":"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code"},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/de\/blog\/transpilierung-php\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/de\/"},{"@type":"ListItem","position":2,"name":"PHP lernen","item":"https:\/\/kinsta.com\/de\/thema\/php-lernen\/"},{"@type":"ListItem","position":3,"name":"Der ultimative Leitfaden f\u00fcr die Transpilierung von PHP-Code"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/de\/#website","url":"https:\/\/kinsta.com\/de\/","name":"Kinsta\u00ae","description":"Schnelle, sichere und hochwertige Hosting-L\u00f6sungen","publisher":{"@id":"https:\/\/kinsta.com\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/de\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/de\/wp-content\/uploads\/sites\/5\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Kinsta-Deutschland-207459890108303\/","https:\/\/x.com\/Kinsta_DE","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\/de\/#\/schema\/person\/c382de1885cc21b079ec1e71d7faf238","name":"Leonardo Losoviz","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/kinsta.com\/de\/#\/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\/de\/blog\/author\/leonardolosoviz\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts\/43724","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/users\/196"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/comments?post=43724"}],"version-history":[{"count":5,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts\/43724\/revisions"}],"predecessor-version":[{"id":43741,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/posts\/43724\/revisions\/43741"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/translations\/en"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/translations\/es"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/translations\/it"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/translations\/fr"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/translations\/pt"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/translations\/de"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/translations\/nl"},{"href":"https:\/\/kinsta.com\/de\/wp-json\/kinsta\/v1\/posts\/43724\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/media\/43725"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/media?parent=43724"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/tags?post=43724"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/de\/wp-json\/wp\/v2\/topic?post=43724"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}