Wordpress

Den Ultimate Guide fir PHP Code ze transpiléieren

An idealen Ëmstänn sollte mir PHP 8.0 benotzen (déi lescht Versioun vum Schreiwen) fir all eis Siten an aktualiséieren soubal eng nei Versioun verëffentlecht gëtt. Wéi och ëmmer, Entwéckler mussen dacks mat fréiere PHP Versiounen schaffen, sou wéi wann Dir en ëffentleche Plugin fir WordPress erstellt oder mat Legacy Code schafft, wat d'Upgrade vum Webserver Ëmfeld behënnert.

An dëse Situatioune kënne mir d'Hoffnung opginn fir de leschten PHP Code ze benotzen. Awer et gëtt eng besser Alternativ: mir kënnen nach ëmmer eise Quellcode mat PHP 8.0 schreiwen an en op eng fréier PHP Versioun transpiléieren - souguer op PHP 7.1.

An dësem Guide léiere mir Iech alles wat Dir wësse musst iwwer PHP Code transpiléieren.

Wat ass Transpiléieren?

Transpiling konvertéiert Quellcode vun enger Programméierungssprooch an en gläichwäertege Quellcode vun der selwechter oder enger anerer Programméierungssprooch.

Transpiléieren ass keen neit Konzept an der Webentwécklung: Client-Säit Entwéckler wäerten zimlech wahrscheinlech mat Babel vertraut sinn, en Transpiler fir JavaScript Code.

Babel konvertéiert JavaScript Code vun der moderner ECMAScript 2015+ Versioun an eng legacy Versioun kompatibel mat eelere Browser. Zum Beispill, kritt eng ES2015 Pfeilfunktioun:

[2, 4, 6].map((n) => n * 2);

... Babel wäert et an seng ES5 Versioun ëmsetzen:

[2, 4, 6].map(function(n) {
  return n * 2;
});

Wat ass PHP Transpiléieren?

Wat potenziell nei an der Webentwécklung ass, ass d'Méiglechkeet fir Server-Säit Code ze transpiléieren, besonnesch PHP.

Transpiléiere vu PHP funktionnéiert déiselwecht wéi JavaScript transpiléieren: Quellcode vun enger moderner PHP Versioun gëtt an en gläichwäertege Code fir eng méi al PHP Versioun ëmgewandelt.

No deemselwechte Beispill wéi virdrun, eng Pfeilfunktioun vu PHP 7.4:

$nums = array_map(fn($n) => $n * 2, [2, 4, 6]);

...kann a seng gläichwäerteg PHP 7.3 Versioun transpiléiert ginn:

$nums = array_map(
  function ($n) {
    return $n * 2;
  },
  [2, 4, 6]
);

Pfeilfunktiounen kënnen transpiléiert ginn well se syntaktesch Zocker sinn, also eng nei Syntax fir en existent Verhalen ze produzéieren. Dëst ass déi niddereg hängend Fruucht.

Wéi och ëmmer, et ginn och nei Features déi en neit Verhalen erstellen, an als solch gëtt et kee gläichwäertege Code fir fréier Versioune vu PHP. Dat ass de Fall mat Gewerkschaftstypen, a PHP 8.0 agefouert:

function someFunction(float|int $param): string|float|int|null
{
  // ...
}

An dëse Situatiounen kann d'Transpilatioun nach ëmmer gemaach ginn soulaang déi nei Feature fir d'Entwécklung erfuerderlech ass awer net fir d'Produktioun. Da kënne mir d'Feature ganz einfach aus dem transpiléierte Code läschen ouni sérieux Konsequenzen.

Een esou Beispill ass Gewerkschaftstypen. Dës Fonktioun gëtt benotzt fir ze kontrolléieren ob et kee Mëssverständnis tëscht der Inputtyp a sengem geliwwerte Wäert ass, wat hëlleft Bugs ze vermeiden. Wann et e Konflikt mat Typen ass, gëtt et e Feeler schonn an der Entwécklung, a mir sollten et fänken a fixéieren ier de Code d'Produktioun erreecht.

Dofir kënne mir eis leeschten d'Feature aus dem Code fir d'Produktioun ze läschen:

function someFunction($param)
{
  // ...
}

Wann de Feeler geschitt nach an der Produktioun, wäert de geheien Feeler Message manner präziist wéi wa mir Gewerkschaft Zorte haten. Wéi och ëmmer, dëse potenziellen Nodeel gëtt iwwerwältegt andeems Dir Gewerkschaftstypen iwwerhaapt benotze kënnt.

An enger perfekter Welt sollte mir fäeg sinn PHP 8.0 op all eise Siten ze benotzen an ze aktualiséieren soubal eng nei Versioun erauskomm ass 😌 Dat ass awer net ëmmer de Fall. Léiert alles wat Dir wësse musst iwwer PHP Code transpiléieren hei 👇Klickt fir Tweet

Virdeeler vun Transpiling PHP Code

Transpiling erméiglecht et eng Applikatioun mat der leschter Versioun vu PHP ze codéieren an eng Verëffentlechung ze produzéieren déi och an Ëmfeld funktionnéiert, déi méi al Versioune vu PHP lafen.

Dëst kann besonnesch nëtzlech sinn fir Entwéckler déi Produkter fir Legacy Content Management Systemer (CMS) erstellen. WordPress, zum Beispill, ënnerstëtzt nach ëmmer offiziell PHP 5.6 (och wann et PHP 7.4+ recommandéiert). De Prozentsaz vu WordPress Siten, déi PHP Versiounen 5.6 bis 7.2 lafen - déi all End-of-Life (EOL) sinn, dat heescht datt se keng Sécherheetsupdate méi kréien - steet op e wesentlechen 34.8%, an déi lafen op all PHP Versioun anescht wéi 8.0 steet op ganz 99.5%:

WordPress Notzung no Versioun
WordPress Benotzungsstatistiken no Versioun. Bildquell: WordPress

Dofir wäerte WordPress Themen a Plugins, déi op e globale Publikum gezielt sinn, ganz wahrscheinlech mat enger aler Versioun vu PHP kodéiert ginn fir hir méiglech Erreechung ze erhéijen. Dank der Transpiléierung kënnen dës mat PHP 8.0 kodéiert ginn, an nach ëmmer fir eng méi al PHP Versioun verëffentlecht ginn, sou datt esou vill Benotzer wéi méiglech gezielt ginn.

Tatsächlech kann all Applikatioun déi all PHP Versioun aner wéi déi lescht (och am Beräich vun den aktuell ënnerstëtzte PHP Versiounen) ënnerstëtzen, profitéieren.

Dëst ass de Fall mat Drupal, wat PHP 7.3 erfuerdert. Dank der Transpiléierung kënnen d'Entwéckler ëffentlech verfügbar Drupal Moduler mat PHP 8.0 erstellen, a se mat PHP 7.3 befreien.

En anert Beispill ass wann Dir personaliséiert Code erstellt fir Clienten déi PHP 8.0 an hiren Ëmfeld net aus engem oder anere Grond lafen kënnen. Trotzdem, dank der Transpiléierung, kënnen d'Entwéckler nach ëmmer hir Liwwerungen mat PHP 8.0 codéieren an se op dës legacy Ëmfeld lafen.

Wéini PHP ze transpiléieren

PHP Code kann ëmmer transpiléiert ginn, ausser et enthält eng PHP Feature déi keen Äquivalent an der viregter Versioun vu PHP huet.

Dat ass méiglecherweis de Fall mat Attributer, a PHP 8.0 agefouert:

#[SomeAttr]
function someFunc() {}

#[AnotherAttr]
class SomeClass {}

Am fréiere Beispill mat Pfeilfunktiounen kann de Code transpiléiert ginn well Pfeilfunktiounen syntaktesch Zocker sinn. Attributer, am Géigesaz, kreéieren komplett neit Verhalen. Dëst Verhalen kéint och mat PHP 7.4 a méi reproduzéiert ginn, awer nëmmen duerch manuell Kodéierung, also net automatesch baséiert op engem Tool oder Prozess (AI kéint eng Léisung ubidden, awer mir sinn nach net do).

Attributer geduecht fir Entwécklung benotzt, wéi #[Deprecated], kann déi selwecht Manéier geläscht ginn, datt Gewerkschaft Zorte geläscht ginn. Awer Attributer déi d'Verhalen vun der Applikatioun an der Produktioun änneren kënnen net geläscht ginn, a si kënnen och net direkt transpiléiert ginn.

Vun haut un kann keen Transpiler Code mat PHP 8.0 Attributer huelen an automatesch säin gläichwäertege PHP 7.4 Code produzéieren. Dofir, wann Äre PHP Code Attributer muss benotzen, dann transpiléiere wäert et schwéier oder onméiglech sinn.

PHP Features déi transpiléiert kënne ginn

Dëst sinn d'Features vu PHP 7.1 a méi héich, déi momentan transpiléiert kënne ginn. Wann Äre Code nëmmen dës Funktiounen benotzt, kënnt Dir d'Sécherheet genéissen datt Är transpiléiert Applikatioun funktionnéiert. Soss musst Dir beurteelen ob den transpiléierten Code Feeler produzéiert.

PHP Versioun Eegeschaften
7.1 alles
7.2 - object Typ
- Erweiderung vum Parametertyp
- PREG_UNMATCHED_AS_NULL Fändel an preg_match
7.3 - Referenzaufgaben an list() / Array destrukturéieren (Ausser bannen foreach — #4376)
- Flexibel Heredoc an Nowdoc Syntax
- Verfollegt Kommaen a Funktiounen Uruff
- set(raw)cookie akzeptéiert $ Optioun Argument
7.4 - Typen Eegeschafte
- Pfeil Funktiounen
- Null coalescing Aufgab Bedreiwer
- Auspacken bannent Arrays
- Numeresch literal Separator
- strip_tags() mat enger Rei vun Tag Nimm
- covariant Retour Typen a contravariant Param Typen
8.0 - Unioun Zorte
- mixed pseudo Typ
- static zréck Typ
- ::class magesch konstant op Objete
- match Ausdrock
- catch Ausnahmen nëmmen no Typ
- Null-sécher Bedreiwer
- Klass constructor Immobilie Promotioun
- Trailing Kommaen a Parameterlëschten a Schließung use Lëschten

PHP Transpiler

De Moment gëtt et een Tool fir PHP Code ze transpiléieren: Rector.

Rector ass e PHP-Rekonstruktor-Tool, deen PHP-Code konvertéiert baséiert op programméierbare Reegelen. Mir Input de Quellcode an de Set vu Regelen fir ze lafen, an de Rector wäert de Code transforméieren.

De Rector gëtt iwwer Kommandozeil operéiert, am Projet iwwer Composer installéiert. Wann ausgefouert gëtt, wäert de Rector en "diff" ausginn (Ergänzunge a gréng, Ewechhuele rout) vum Code virun an no der Konversioun:

"diff" Ausgang vum Rektor
"diff" Ausgang vum Rektor

Wéi eng Versioun vu PHP fir ze transpiléieren

Fir Code iwwer PHP Versiounen ze transpiléieren, mussen déi entspriechend Regelen erstallt ginn.

Haut enthält d'Rektorbibliothéik déi meescht Regele fir de Code ze transpiléieren am Beräich vu PHP 8.0 bis 7.1. Dofir kënne mir eis PHP Code zouverlässeg transpiléieren sou wäit wéi d'Versioun 7.1.

Et ginn och Regele fir Transpilatioun vu PHP 7.1 op 7.0 a vu 7.0 op 5.6, awer dës sinn net ustrengend. D'Aarbecht ass amgaang fir se ze kompletéieren, sou datt mir eventuell PHP-Code bis op d'Versioun 5.6 transpiléiere kënnen.

Transpiléieren vs Backporting

Backporting ass ähnlech wéi transpiléieren, awer méi einfach. Backporting Code vertrauen net onbedéngt op nei Features vun enger Sprooch. Amplaz kann déi selwecht Funktionalitéit fir eng méi al Versioun vun der Sprooch geliwwert ginn andeems Dir de entspriechende Code vun der neier Versioun vun der Sprooch kopéiert/paste/adaptéiert.

Zum Beispill, d'Funktioun str_contains gouf a PHP 8.0 agefouert. Déi selwecht Funktioun fir PHP 7.4 an ënnen kann einfach esou implementéiert ginn:

if (!defined('PHP_VERSION_ID') || (defined('PHP_VERSION_ID') && PHP_VERSION_ID < 80000)) {
  if (!function_exists('str_contains')) {
    /**
     * Checks if a string contains another
     *
     * @param string $haystack The string to search in
     * @param string $needle The string to search
     * @return boolean Returns TRUE if the needle was found in haystack, FALSE otherwise.
     */
    function str_contains(string $haystack, string $needle): bool
    {
      return strpos($haystack, $needle) !== false;
    }
  }
}

Well Backporting méi einfach ass wéi transpiléieren, sollte mir fir dës Léisung wielen wann de Backporting d'Aarbecht mécht.

Wat de Beräich tëscht PHP 8.0 bis 7.1 ugeet, kënne mir d'Symfony Polyfill Bibliothéike benotzen:

  • Polyfill PHP 7.1
  • Polyfill PHP 7.2
  • Polyfill PHP 7.3
  • Polyfill PHP 7.4
  • Polyfill PHP 8.0

Dës Bibliothéiken backportéieren déi folgend Funktiounen, Klassen, Konstanten an Interfaces:

PHP Versioun Eegeschaften
7.2 Funktiounen:

  • spl_object_id
  • utf8_encode
  • utf8_decode

Konstanten:

  • PHP_FLOAT_*
  • PHP_OS_FAMILY
7.3 Funktiounen:

  • array_key_first
  • array_key_last
  • hrtime
  • is_countable

Ausnahmen:

  • JsonException
7.4 Funktiounen:

  • get_mangled_object_vars
  • mb_str_split
  • password_algos
8.0 Schnëttplazen:

  • Stringable

Course:

  • ValueError
  • UnhandledMatchError

Konstanten:

  • FILTER_VALIDATE_BOOL

Funktiounen:

  • fdiv
  • get_debug_type
  • preg_last_error_msg
  • str_contains
  • str_starts_with
  • str_ends_with
  • get_resource_id

Beispiller vun Transpiled PHP

Loosst eis e puer Beispiller vum transpiléierten PHP Code iwwerpréiwen, an e puer Packagen déi komplett transpiléiert ginn.

PHP Code

d' match Ausdrock gouf a PHP 8.0 agefouert. Dëse Quellcode:

function getFieldValue(string $fieldName): ?string
{
  return match($fieldName) {
    'foo' => 'foofoo',
    'bar' => 'barbar',
    'baz' => 'bazbaz',
    default => null,
  };
}

... wäert op seng gläichwäerteg PHP 7.4 Versioun transpiléiert ginn, mat der switch Bedreiwer:

function getFieldValue(string $fieldName): ?string
{
  switch ($fieldName) {
    case 'foo':
      return 'foofoo';
    case 'bar':
      return 'barbar';
    case 'baz':
      return 'bazbaz';
    default:
      return null;
  }
}

Den nullsafe Bedreiwer gouf och a PHP 8.0 agefouert:

public function getValue(TypeResolverInterface $typeResolver): ?string
{
  return $this->getResolver($typeResolver)?->getValue();
}

Den transpiléierten Code muss als éischt de Wäert vun der Operatioun un eng nei Variabel zouzeschreiwen, fir d'Operatioun zweemol auszeféieren:

public function getValue(TypeResolverInterface $typeResolver): ?string
{
  return ($val = $this->getResolver($typeResolver)) ? $val->getValue() : null;
}

D'Constructor Property Promotiouns Feature, och a PHP 8.0 agefouert, erlaabt d'Entwéckler manner Code ze schreiwen:

class QueryResolver
{
  function __construct(protected QueryFormatter $queryFormatter)
  {
  }
}

Wann Dir et fir PHP 7.4 transpiléiert, gëtt dat ganzt Stéck Code produzéiert:

 class QueryResolver
 {
  protected QueryFormatter $queryFormatter;

  function __construct(QueryFormatter $queryFormatter)
  {
    $this->queryFormatter = $queryFormatter;
  }
}

Den transpiléierten Code uewendriwwer enthält getippten Eegeschaften, déi a PHP 7.4 agefouert goufen. Transpiléiere vun deem Code erof op PHP 7.3 ersetzt se mat docblocks:

 class QueryResolver
 {
  /**
   * @var QueryFormatter
   */
  protected $queryFormatter;

  function __construct(QueryFormatter $queryFormatter)
  {
    $this->queryFormatter = $queryFormatter;
  }
}

PHP Packagen

Déi folgend Bibliothéike gi fir d'Produktioun transpiléiert:

Bibliothéik / Beschreiwung Code / Notizen
Recteur
PHP-Rekonstruktor-Tool dat Transpiléiere méiglech mécht
- Source Code
- Transpiléiert Code
- Notizen
Einfach Kodéierungsnormen
Tool fir PHP Code un enger Rei vu Reegelen ze halen
- Source Code
- Transpiléiert Code
- Notizen
GraphQL API fir WordPress
Plugin ubitt e GraphQL Server fir WordPress
- Source Code
- Transpiléiert Code
- Notizen

Virdeeler an Nodeeler vum Transpiléiere vu PHP

De Virdeel vum Transpiléiere vu PHP gouf scho beschriwwen: et erlaabt de Quellcode PHP 8.0 ze benotzen (dh déi lescht Versioun vu PHP), déi an eng méi niddereg Versioun fir PHP transforméiert gëtt fir d'Produktioun an enger legacy Applikatioun oder Ëmfeld ze lafen.

Dëst erlaabt eis effektiv besser Entwéckler ze ginn, Code mat méi héijer Qualitéit ze produzéieren. Dëst ass well eise Quellcode kann PHP 8.0 Gewerkschaftstypen benotzen, PHP 7.4 getippten Eegeschaften, an déi verschidden Aarten a Pseudo-Typen déi zu all nei Versioun vu PHP bäigefüügt ginn (mixed vu PHP 8.0, object vu PHP 7.2), ënner anerem modernen Features vu PHP.

Mat dëse Fonctiounen kënne mir Bugs während der Entwécklung besser fangen a Code schreiwen deen méi einfach ass ze liesen.

Loosst eis elo d'Nodeeler kucken.

Et muss kodéiert a gehale ginn

De Rector kann de Code automatesch transpiléieren, awer de Prozess wäert méiglecherweis e puer manuelle Input erfuerderen fir et mat eisem spezifesche Setup ze schaffen.

Drëtt Partei Bibliothéike mussen och transpiléiert ginn

Dëst gëtt en Thema wann se transpiléieren Feeler produzéiert well mir dann an hire Quellcode musse verdéiwen fir de méigleche Grond erauszefannen. Wann de Problem ka fixéiert ginn an de Projet ass Open Source, musse mir eng Pull-Ufro ofginn. Wann d'Bibliothéik net Open Source ass, kënne mir e Stroossespär treffen.

De Rektor informéiert eis net wann de Code net kann transpiléiert ginn

Wann de Quellcode PHP 8.0 Attributer enthält oder all aner Feature déi net transpiléiert ka ginn, kënne mir net weidergoen. Wéi och ëmmer, de Rektor wäert dës Konditioun net iwwerpréiwen, also musse mir et manuell maachen. Dëst ass vläicht net e grousse Problem betreffend eisen eegene Quellcode well mir et scho vertraut sinn, awer et kéint en Hindernis betreffend Drëtt Partei Ofhängegkeeten ginn.

Debugging Informatioun benotzt den transpiléierten Code, net de Quellcode

Wann d'Applikatioun e Fehlermeldung mat engem Stackspur an der Produktioun produzéiert, weist d'Linnnummer op den transpiléierte Code. Mir musse vum transpiléierten an den originale Code zréck konvertéieren fir déi entspriechend Linnnummer am Quellcode ze fannen.

Den Transpiléierte Code muss och virgeschriwwe sinn

Eis transpiléiert Projet an eng aner Bibliothéik, déi och am Produktiounsëmfeld installéiert ass, kéinten déiselwecht Drëtt Partei Ofhängegkeet benotzen. Dës Ofhängegkeet vun Drëttubidder gëtt fir eise Projet transpiléiert an hält säin originelle Quellcode fir déi aner Bibliothéik. Dofir muss déi transpiléiert Versioun iwwer PHP-Scoper, Strauss oder en anert Tool virgeschriwwe ginn fir potenziell Konflikter ze vermeiden.

Transpiléierung muss stattfannen wärend der kontinuéierlecher Integratioun (CI)

Well den transpiléierten Code natierlech de Quellcode iwwerschreift, sollte mir den Transpiléierungsprozess net op eisen Entwécklungscomputer ausféieren, oder mir riskéieren Nebenwirkungen ze kreéieren. De Prozess während engem CI Run auszeféieren ass méi gëeegent (méi doriwwer hei ënnen).

Wéi PHP transpiléieren

Als éischt musse mir de Rector an eisem Projet fir Entwécklung installéieren:

composer require rector/rector --dev

Mir kreéieren dann eng rector.php Konfiguratiounsdatei am Root-Verzeichnis vum Projet mat de erfuerderleche Sets vu Reegelen. Fir de Code vun PHP 8.0 op 7.1 erofzesetzen, benotze mir dës Konfiguratioun:

use RectorSetValueObjectDowngradeSetList;
use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $containerConfigurator->import(DowngradeSetList::PHP_80);
    $containerConfigurator->import(DowngradeSetList::PHP_74);
    $containerConfigurator->import(DowngradeSetList::PHP_73);
    $containerConfigurator->import(DowngradeSetList::PHP_72);
};

Fir sécherzestellen datt de Prozess wéi erwaart ausféiert, kënne mir de Rector's lafen process Kommando am dréchene Modus, laanscht d'Plaz(en) fir ze veraarbecht (an dësem Fall all Dateien ënner dem Dossier src/):

vendor/bin/rector process src --dry-run

Fir d'Transpilatioun auszeféieren, lafe mir Rector's process Kommando, wat d'Dateien an hirer existéierender Plaz änneren:

vendor/bin/rector process src

Opgepasst: wa mir lafen rector process an eiser Entwécklung Computeren, de Quelltext wäert am Plaz ëmgerechent ginn, ënner src/. Wéi och ëmmer, mir wëllen de konvertéierte Code op enger anerer Plaz produzéieren fir de Quellcode net ze iwwerschreiden wann Dir de Code downgradéiert. Aus dësem Grond ass de Prozess am meeschte gëeegent wärend der kontinuéierlecher Integratioun.

Optimisatioun vum Transpiléierungsprozess

Fir eng transpiléiert Liwwerung fir d'Produktioun ze generéieren, muss nëmmen de Code fir d'Produktioun ëmgewandelt ginn; Code nëmme fir Entwécklung gebraucht ka ginn iwwersprongen. Dat heescht, mir kënnen vermeiden all Tester (fir eise Projet a seng Ofhängegkeeten) an all Ofhängegkeete fir d'Entwécklung ze transpiléieren.

Wat Tester ugeet, wësse mer scho wou déi fir eise Projet stinn - zum Beispill ënnert dem Dossier tests/. Mir mussen och erausfannen, wou déi fir d'Ofhängegkeete sinn - zum Beispill ënner hiren Ënnerdopper tests/, test/ an Test/ (fir verschidde Bibliothéiken). Dann soen mir dem Rector fir dës Ordner ze veraarbecht:

return static function (ContainerConfigurator $containerConfigurator): void {
  // ...

  $parameters->set(Option::SKIP, [
    // Skip tests
    '*/tests/*',
    '*/test/*',
    '*/Test/*',
  ]);
};

Wat Ofhängegkeeten ugeet, weess de Komponist déi fir d'Entwécklung sinn (déi ënner der Entrée require-dev in composer.json) a wéi eng fir d'Produktioun sinn (déi ënner der Entrée require).

Fir vum Composer d'Weeër vun all Ofhängegkeete fir d'Produktioun ze recuperéieren, lafe mir:

composer info --path --no-dev

Dëse Kommando wäert eng Lëscht vun Ofhängegkeeten mat hirem Numm a Wee produzéieren, sou:

brain/cortex                     /Users/leo/GitHub/leoloso/PoP/vendor/brain/cortex
composer/installers              /Users/leo/GitHub/leoloso/PoP/vendor/composer/installers
composer/semver                  /Users/leo/GitHub/leoloso/PoP/vendor/composer/semver
guzzlehttp/guzzle                /Users/leo/GitHub/leoloso/PoP/vendor/guzzlehttp/guzzle
league/pipeline                  /Users/leo/GitHub/leoloso/PoP/vendor/league/pipeline

Mir kënnen all d'Weeër extrahéieren an se an de Rektor Kommando fidderen, deen dann eise Projet veraarbecht src/ Dossier plus déi Ordner déi all Ofhängegkeete fir d'Produktioun enthalen:

$ paths="$(composer info --path --no-dev | cut -d' ' -f2- | sed 's/ //g' | tr 'n' ' ')"
$ vendor/bin/rector process src $paths

Eng weider Verbesserung kann verhënneren datt de Rector dës Ofhängegkeete veraarbecht déi scho mat der Zil-PHP Versioun benotzt. Wann eng Bibliothéik mat PHP 7.1 (oder all Versioun hei drënner) kodéiert gouf, da brauch se net op PHP 7.1 ze transpiléieren.

Fir dëst z'erreechen, kënne mir d'Lëscht vun de Bibliothéiken kréien, déi PHP 7.2 a méi erfuerderen an nëmmen déi veraarbecht. Mir kréien d'Nimm vun all dëse Bibliothéiken iwwer Composer's why-not Kommando, esou:

composer why-not php "7.1.*" | grep -o "S*/S*"

Well dëst Kommando net mat der --no-dev Fändel, fir nëmmen Ofhängegkeete fir d'Produktioun ze enthalen, musse mir fir d'éischt d'Ofhängegkeete fir d'Entwécklung erofhuelen an den Autoloader regeneréieren, de Kommando ausféieren an se dann erëm derbäi:

$ composer install --no-dev
$ packages=$(composer why-not php "7.1.*" | grep -o "S*/S*")
$ composer install

Komponist info --path Kommando recuperéiert de Wee fir e Package, mat dësem Format:

# Executing this command
$ composer info psr/cache --path   
# Produces this response:
psr/cache /Users/leo/GitHub/leoloso/PoP/vendor/psr/cache

Mir maachen dëst Kommando fir all Elementer an eiser Lëscht aus fir all Weeër ze kréien fir ze transpiléieren:

Braucht Dir eng Hosting-Léisung déi Iech e Konkurrenzvirdeel gëtt? BehmasterEt huet Iech mat onheemlecher Geschwindegkeet, moderner Sécherheet, an Auto-Scaling ofgedeckt. Kuckt eis Pläng

for package in $packages
do
  path=$(composer info $package --path | cut -d' ' -f2-)
  paths="$paths $path"
done

Schlussendlech gi mir dës Lëscht dem Rektor (plus dem Projet src/ Dossier):

vendor/bin/rector process src $paths

Falen ze vermeiden wann Dir Code transpiléiert

Transpiléiere vum Code kéint als Konscht ugesi ginn, déi dacks Tweaks erfuerderen spezifesch fir de Projet. Loosst eis e puer Probleemer gesinn, déi mir kënne kommen.

Kette Regele ginn net ëmmer veraarbecht

Eng Kette Regel ass wann eng Regel de Code muss konvertéieren, dee vun enger viregter Regel produzéiert gëtt.

Zum Beispill, Bibliothéik symfony/cache enthält dëse Code:

final class CacheItem implements ItemInterface
{
  public function tag($tags): ItemInterface
  {
    // ...
    return $this;
  }
}

Wann Dir vun PHP 7.4 op 7.3 transpiléiert, Funktioun tag muss zwou Ännerunge maachen:

  • De Retour Typ ItemInterface muss éischt ëmgerechent ginn self, wéinst Regel DowngradeCovariantReturnTypeRector
  • De Retour Typ self muss dann ewechgeholl ginn, wéinst Regel DowngradeSelfTypeDeclarationRector

D'Ennresultat sollt dëst sinn:

final class CacheItem implements ItemInterface
{
  public function tag($tags)
  {
    // ...
    return $this;
  }
}

Wéi och ëmmer, de Rektor erausgëtt nëmmen déi Tëschenzäit:

final class CacheItem implements ItemInterface
{
  public function tag($tags): self
  {
    // ...
    return $this;
  }
}

D'Fro ass, datt de Rektor net ëmmer d'Uerdnung kann kontrolléieren an där d'Regele applizéiert ginn.

D'Léisung ass z'identifizéieren wéi eng Kette Reegelen onveraarbecht gelooss goufen, an en neie Rector Run auszeféieren fir se anzesetzen.

Fir déi geketten Regelen z'identifizéieren, lafe mir de Rector zweemol um Quellcode, sou:

$ vendor/bin/rector process src
$ vendor/bin/rector process src --dry-run

Déi éischte Kéier lafe mir de Rector wéi erwaart, fir d'Transpilatioun auszeféieren. Déi zweete Kéier benotze mir de --dry-run Fändel fir ze entdecken ob et nach Ännerunge musse gemaach ginn. Wann et ass, gëtt de Kommando mat engem Fehlercode erausgaang, an den "diff" Ausgang weist wéi eng Regel(en) nach ëmmer applizéiert kënne ginn. Dat géif heeschen datt den éischte Laf net fäerdeg war, mat enger chained Regel net veraarbecht ginn.

Running Rector with --dry-run Fändel
Running Rector with –dry-run Fändel

Wann mir déi net ugewandte Kette Regel (oder Regelen) identifizéiert hunn, kënne mir dann eng aner Rector Configuratiounsdatei erstellen - zum Beispill, rector-chained-rule.php wäert déi fehlend Regel ausféieren. Amplaz e komplette Set vu Regele fir all Dateien ënner ze veraarbecht src/, dës Kéier kënne mir déi spezifesch fehlend Regel op der spezifescher Datei lafen, wou se applizéiert muss ginn:

// rector-chained-rule.php
use RectorCoreConfigurationOption;
use RectorDowngradePhp74RectorClassMethodDowngradeSelfTypeDeclarationRector;
use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
  $services = $containerConfigurator->services();
  $services->set(DowngradeSelfTypeDeclarationRector::class);

  $parameters = $containerConfigurator->parameters();
  $parameters->set(Option::PATHS, [
    __DIR__ . '/vendor/symfony/cache/CacheItem.php',
  ]);
};

Endlech soen mir dem Rector op sengem zweete Pass fir déi nei Configuratiounsdatei iwwer Input ze benotzen --config:

# First pass with all modifications
$ vendor/bin/rector process src

# Second pass to fix a specific problem
$ vendor/bin/rector process --config=rector-chained-rule.php

Komponist Ofhängegkeete kënne inkonsistent sinn

Bibliothéike kënnen eng Ofhängegkeet deklaréieren fir d'Entwécklung (dh ënner require-dev in composer.json), awer ëmmer nach e puer Code vun hinnen fir d'Produktioun ze referenzéieren (wéi op e puer Dateien ënner src/, net tests/).

Normalerweis ass dëst kee Problem well dee Code vläicht net op der Produktioun gelueden gëtt, sou datt et ni e Feeler op der Applikatioun gëtt. Wéi och ëmmer, wann de Rector de Quellcode a seng Ofhängegkeeten veraarbecht, validéiert et datt all referenzéierte Code ka geluede ginn. De Rector wäert e Feeler werfen wann all Datei e Stéck Code vun enger net-installéierter Bibliothéik referéiert (well et deklaréiert gouf nëmme fir d'Entwécklung néideg ze sinn).

Zum Beispill, Klass EarlyExpirationHandler vum Symfony's Cache Komponent implementéiert Interface MessageHandlerInterface aus dem Messenger Komponent:

class EarlyExpirationHandler implements MessageHandlerInterface
{
    //...
}

allerdéngs, symfony/cache deklaréiert symfony/messenger eng Ofhängegkeet fir Entwécklung ze sinn. Dann, wann Dir de Rector op engem Projet leeft, deen hänkt dovun of symfony/cache, gëtt et e Feeler:

[ERROR] Could not process "vendor/symfony/cache/Messenger/EarlyExpirationHandler.php" file, due to:             
  "Analyze error: "Class SymfonyComponentMessengerHandlerMessageHandlerInterface not found.". Include your files in "$parameters->set(Option::AUTOLOAD_PATHS, [...]);" in "rector.php" config.
  See https://github.com/rectorphp/rector#configuration".   

Et ginn dräi Léisunge fir dëst Thema:

  1. An der Rector Config, sprang d'Veraarbechtung vun der Datei déi dat Stéck Code referéiert:
return static function (ContainerConfigurator $containerConfigurator): void {
  // ...

  $parameters->set(Option::SKIP, [
    __DIR__ . '/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php',
  ]);
};
  1. Luet déi fehlend Bibliothéik erof a füügt säi Wee derbäi fir vum Rektor automatesch gelueden ze ginn:
return static function (ContainerConfigurator $containerConfigurator): void {
  // ...

  $parameters->set(Option::AUTOLOAD_PATHS, [
    __DIR__ . '/vendor/symfony/messenger',
  ]);
};
  1. Hutt Äre Projet ofhängeg vun der fehlend Bibliothéik fir d'Produktioun:
composer require symfony/messenger

Transpiléieren a kontinuéierlech Integratioun

Wéi virdru scho gesot, an eiser Entwécklungscomputer musse mir d' --dry-run Fändel wann Dir Rector leeft, oder soss, gëtt de Quellcode mam transpiléierte Code iwwerschratt. Aus dësem Grond ass et méi gëeegent fir den aktuellen Transpiléierungsprozess wärend der kontinuéierlecher Integratioun (CI) auszeféieren, wou mir temporär Leefer kënne spinnen fir de Prozess auszeféieren.

Eng ideal Zäit fir den Transpiléierungsprozess auszeféieren ass wann Dir d'Verëffentlechung fir eise Projet generéiert. Zum Beispill, de Code hei ënnen ass e Workflow fir GitHub Actions, déi d'Verëffentlechung vun engem WordPress Plugin erstellt:

name: Generate Installable Plugin and Upload as Release Asset
on:
  release:
    types: [published]
jobs:
  build:
    name: Build, Downgrade and Upload Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Downgrade code for production (to PHP 7.1)
        run: |
          composer install
          vendor/bin/rector process
          sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php
      - name: Build project for production
        run: |
          composer install --no-dev --optimize-autoloader
          mkdir build
      - name: Create artifact
        uses: montudor/action-zip@v0.1.0
        with:
          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**
      - name: Upload artifact
        uses: actions/upload-artifact@v2
        with:
            name: graphql-api
            path: build/graphql-api.zip
      - name: Upload to release
        uses: JasonEtco/upload-to-release@master
        with:
          args: build/graphql-api.zip application/zip
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Dëse Workflow enthält eng Standardprozedur fir e WordPress Plugin iwwer GitHub Actions ze verëffentlechen. Déi nei Ergänzung, fir de Code vum Plugin vun PHP 7.4 op 7.1 ze transpiléieren, geschitt an dësem Schrëtt:

      - name: Downgrade code for production (to PHP 7.1)
        run: |
          vendor/bin/rector process
          sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php

Zesummegefaasst mécht dësen Workflow elo déi folgend Schrëtt:

  1. Kontrolléiert de Quellcode fir e WordPress Plugin aus sengem Repository, geschriwwen mat PHP 7.4
  2. Installéiert seng Composer Ofhängegkeeten
  3. Transpiléiert säi Code vu PHP 7.4 op 7.1
  4. Ännert den "Verlaangt PHP" Entrée an der Haaptdatei vum Plugin Header vun "7.4" ze maachen "7.1"
  5. Entfernt d'Ofhängegkeete fir d'Entwécklung néideg
  6. Erstellt d'.zip Datei vum Plugin, ausser all onnéideg Dateien
  7. Eroplueden d'.zip Datei als Verëffentlechungsaktiv (an zousätzlech als Artefakt fir d'GitHub Action)

Testen de Transpiléierte Code

Wann de Code op PHP 7.1 transpiléiert ass, wéi wësse mir datt et gutt funktionnéiert? Oder, an anere Wierder, wéi wësse mir datt et grëndlech ëmgewandelt gouf, a keng Iwwerreschter vu méi héije Versioune vum PHP Code sinn hannerlooss?

Ähnlech wéi de Code transpiléieren, kënne mir d'Léisung an engem CI Prozess ëmsetzen. D'Iddi ass d'Ëmfeld vum Leefer mat PHP 7.1 opzestellen an e Linter op den transpiléierten Code auszeféieren. Wann e Stéck Code net kompatibel ass mat PHP 7.1 (wéi eng getippten Eegeschafte vu PHP 7.4 déi net ëmgewandelt gouf), da wäert d'Linter e Feeler werfen.

E Linter fir PHP dee gutt funktionnéiert ass PHP Parallel Lint. Mir kënnen dës Bibliothéik als Ofhängegkeet fir d'Entwécklung an eisem Projet installéieren, oder de CI-Prozess installéiere wéi e Standalone Composer-Projet:

composer create-project php-parallel-lint/php-parallel-lint

Wann ëmmer de Code PHP 7.2 a méi enthält, wäert PHP Parallel Lint e Feeler wéi dësen werfen:

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
PHP 7.1.33 | 10 parallel jobs
............................................................   60/2870 (2 %)
............................................................  120/2870 (4 %)
...
............................................................  660/2870 (22 %)
.............X..............................................  720/2870 (25 %)
............................................................  780/2870 (27 %)
...
............................................................ 2820/2870 (98 %)
..................................................           2870/2870 (100 %)


Checked 2870 files in 15.4 seconds
Syntax error found in 1 file

------------------------------------------------------------
Parse error: layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php:55
    53|     '0.8.0',
    54|     __('GraphQL API for WordPress', 'graphql-api'),
  > 55| ))) {
    56|     $plugin->setup();
    57| }
Unexpected ')' in layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php on line 55
Error: Process completed with exit code 1.

Loosst eis d'Linter an de Workflow vun eisem CI addéieren. D'Schrëtt fir auszeféieren fir Code vu PHP 8.0 op 7.1 ze transpiléieren an ze testen sinn:

  1. Kuckt de Quellcode aus
  2. Loosst d'Ëmfeld PHP 8.0 lafen, sou datt de Rector de Quellcode interpretéiere kann
  3. Transpiléiert de Code op PHP 7.1
  4. Installéiert de PHP Linter Tool
  5. Wiesselt d'PHP Versioun vun der Ëmwelt op 7.1
  6. Run der linter op der transpiled Code

Dëse GitHub Action Workflow mécht d'Aarbecht:

name: Downgrade PHP tests
jobs:
  main:
    name: Downgrade code to PHP 7.1 via Rector, and execute tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set-up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.0
          coverage: none

      - name: Local packages - Downgrade PHP code via Rector
        run: |
          composer install
          vendor/bin/rector process

      # Prepare for testing on PHP 7.1
      - name: Install PHP Parallel Lint
        run: composer create-project php-parallel-lint/php-parallel-lint --ansi

      - name: Switch to PHP 7.1
        uses: shivammathur/setup-php@v2
        with:
          php-version: 7.1
          coverage: none

      # Lint the transpiled code
      - name: Run PHP Parallel Lint on PHP 7.1
        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

Notéiert w.e.g. datt e puer bootstrap80.php Dateien aus Symfony's Polyfill Bibliothéiken (déi net transpiléiert musse ginn) mussen aus der Linter ausgeschloss ginn. Dës Dateien enthalen PHP 8.0, sou datt d'Linter Feeler beim Veraarbechtung werfen. Wéi och ëmmer, dës Dateien auszeschléissen ass sécher well se nëmmen op der Produktioun gelueden ginn wann Dir PHP 8.0 oder méi leeft:

if (PHP_VERSION_ID >= 80000) {
  return require __DIR__.'/bootstrap80.php';
}

Egal ob Dir en ëffentleche Plugin fir WordPress erstellt oder Dir legacy Code aktualiséiert, et gi vill Grënn datt d'Benotzung vun der leschter PHP Versioun onméiglech ass 👩‍💻 Léiert wéi d'Transpilatioun an dësem Guide hëllefe kann 👇Klickt fir Tweet

Resumé

Dësen Artikel huet eis geléiert wéi mir eise PHP Code transpiléiere kënnen, wat eis erlaabt PHP 8.0 am Quellcode ze benotzen an eng Verëffentlechung ze kreéieren déi op PHP 7.1 funktionnéiert. Transpiléiere gëtt iwwer Rector gemaach, e PHP Rekonstruktor Tool.

Eise Code transpiléieren mécht eis besser Entwéckler well mir Bugs an der Entwécklung besser kënne fangen a Code produzéieren deen natierlech méi einfach ass ze liesen a verstoen.

Transpiléieren erlaabt eis och eise Code mat spezifesche PHP Ufuerderunge vum CMS ofzekoppelen. Mir kënnen dat elo maachen wa mir wëllen déi lescht Versioun vu PHP benotzen fir en ëffentlech verfügbare WordPress Plugin oder Drupal Modul ze kreéieren ouni eis Userbase staark ze beschränken.

Hutt Dir Froen iwwer PHP transpiléieren? Loosst eis an der Kommentarsektioun wëssen!

Verbonnen Artikelen

Hannerlooss eng Äntwert

Är E-Mail-Adress gëtt net publizéiert ginn.

Zréck erop zum Knäppchen