In ultimate instances, we will have to use PHP 8.0 (the newest edition as of penning this) for all our websites and replace it once a brand new edition is launched. Then again, builders will regularly want to paintings with earlier PHP variations, corresponding to when making a public plugin for WordPress or operating with legacy code which impedes upgrading the webserver’s setting.

In those eventualities, shall we surrender hope of the usage of the newest PHP code. However there’s a higher choice: we will be able to nonetheless write our supply code with PHP 8.0 and transpile it to a prior PHP edition — even to PHP 7.1.

On this information, we’ll train you the entirety you want to find out about transpiling PHP code.

What Is Transpiling?

Transpiling converts supply code from a programming language into an identical supply code of the similar or a unique programming language.

Transpiling isn’t a brand new thought inside internet construction: client-side builders will moderately most likely be aware of Babel, a transpiler for JavaScript code.

Babel converts JavaScript code from the fashionable ECMAScript 2015+ edition right into a legacy edition suitable with older browsers. As an example, given an ES2015 arrow serve as:

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

…Babel will convert it into its ES5 edition:

[2, 4, 6].map(serve as(n) {
  go back n * 2;
});

What Is Transpiling PHP?

What’s doubtlessly new inside internet construction is the opportunity of transpiling server-side code, particularly PHP.

Transpiling PHP works the similar method as transpiling JavaScript: supply code from a modern PHP version is transformed into an identical code for an older PHP edition.

Following the similar instance as earlier than, an arrow serve as from PHP 7.4:

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

…can also be transpiled into its identical PHP 7.3 edition:

$nums = array_map(
  serve as ($n) {
    go back $n * 2;
  },
  [2, 4, 6]
);

Arrow purposes can also be transpiled as a result of they’re syntactic sugar, i.e. a brand new syntax to supply an present conduct. That is the low-hanging fruit.

Then again, there also are new options that create a brand new conduct, and as such, there shall be no identical code for earlier variations of PHP. That’s the case with union types, presented in PHP 8.0:

serve as someFunction(go with the flow|int $param): string|go with the flow|int|null
{
  // ...
}

In those eventualities, transpiling can nonetheless be performed so long as the brand new characteristic is needed for construction however now not for manufacturing. Then, we will be able to merely take away the characteristic altogether from the transpiled code with out severe penalties.

One such instance is union kinds. This selection is used to test that there is not any mismatch between the enter sort and its equipped price, which is helping save you insects. If there’s a warfare with kinds, there shall be an error already in construction, and we will have to catch it and connect it earlier than the code reaches manufacturing.

Therefore, we will be able to find the money for to take away the characteristic from the code for manufacturing:

serve as someFunction($param)
{
  // ...
}

If the mistake nonetheless occurs in manufacturing, the thrown error message shall be much less exact than if we had union kinds. Then again, this doable drawback is outweighed through having the ability to use union kinds within the first position.

frame a.novashare-ctt{show:block;background:#00abf0;margin:30px auto;padding:20px 20px 20px 15px;colour:#fff;text-decoration:none!essential;box-shadow:none!essential;-webkit-box-shadow:none!essential;-moz-box-shadow:none!essential;border:none;border-left:5px cast #00abf0}frame a.novashare-ctt:hover{colour:#fff;border-left:5px cast #008cc4}frame a.novashare-ctt:visited{colour:#fff}frame a.novashare-ctt *{pointer-events:none}frame a.novashare-ctt .novashare-ctt-tweet{show:block;font-size:18px;line-height:27px;margin-bottom:10px}frame a.novashare-ctt .novashare-ctt-cta-container{show:block;overflow:hidden}frame a.novashare-ctt .novashare-ctt-cta{go with the flow:proper}frame a.novashare-ctt.novashare-ctt-cta-left .novashare-ctt-cta{go with the flow:left}frame a.novashare-ctt .novashare-ctt-cta-text{font-size:16px;line-height:16px;vertical-align:center}frame a.novashare-ctt .novashare-ctt-cta-icon{margin-left:10px;show:inline-block;vertical-align:center}frame a.novashare-ctt .novashare-ctt-cta-icon svg{vertical-align:center;top:18px}frame a.novashare-ctt.novashare-ctt-simple{background:0 0;padding:10px 0 10px 20px;colour:inherit}frame a.novashare-ctt.novashare-ctt-simple-alt{background:#f9f9f9;padding:20px;colour:#404040}frame a.novashare-ctt.novashare-ctt-simple-alt:hover,frame a.novashare-ctt.novashare-ctt-simple:hover{border-left:5px cast #008cc4}frame a.novashare-ctt.novashare-ctt-simple .novashare-ctt-cta,frame a.novashare-ctt.novashare-ctt-simple-alt .novashare-ctt-cta{colour:#00abf0}frame a.novashare-ctt.novashare-ctt-simple-alt:hover .novashare-ctt-cta,frame a.novashare-ctt.novashare-ctt-simple:hover .novashare-ctt-cta{colour:#008cc4}In a perfect world, we should be able to use PHP 8.0 on all our sites and update it as soon as a new version is released 😌 But that’s not always the case. Learn everything you need to know about transpiling PHP code here 👇Click to Tweet

Benefits of Transpiling PHP Code

Transpiling allows one to code an software the usage of the newest edition of PHP and bring a launch that still works in environments operating older variations of PHP.

This can also be in particular helpful for builders developing merchandise for legacy content management systems (CMS). WordPress, as an example, nonetheless officially supports PHP 5.6 (although it recommends PHP 7.4+). The share of WordPress websites operating PHP variations 5.6 to 7.2 — which might be all Finish-of-Lifestyles (EOL), which means they’re now not receiving safety updates anymore — stands at a large 34.8%, and the ones operating on any PHP edition as opposed to 8.0 stands at a whopping 99.5%:

WordPress usage by version

WordPress utilization stats through edition. Symbol supply: WordPress

In consequence, WordPress subject matters and plugins focused at an international target audience will moderately most likely be coded with an outdated edition of PHP to extend their imaginable achieve. Because of transpiling, those may well be coded the usage of PHP 8.0, and nonetheless be launched for an older PHP edition, thus focused on as many customers as imaginable.

Certainly, any software that should strengthen any PHP edition as opposed to the latest one (even inside the vary of the at this time supported PHP variations) can get advantages.

That is the case with Drupal, which requires PHP 7.3. Because of transpiling, builders can create publicly to be had Drupal modules the usage of PHP 8.0, and launch them with PHP 7.3.

Any other instance is when developing customized code for shoppers who can not run PHP 8.0 of their environments because of one explanation why or any other. Nonetheless, because of transpiling, builders can nonetheless code their deliverables the usage of PHP 8.0 and run them on the ones legacy environments.

When to Transpile PHP

PHP code can at all times be transpiled until it incorporates some PHP characteristic that has no identical within the earlier edition of PHP.

That’s perhaps the case with attributes, presented in PHP 8.0:

#[SomeAttr]
serve as someFunc() {}

#[AnotherAttr]
elegance SomeClass {}

Within the previous instance the usage of arrow purposes, the code may well be transpiled as a result of arrow purposes are syntactic sugar. Attributes, by contrast, create utterly new conduct. This conduct may be reproduced with PHP 7.4 and underneath, however most effective through manually coding it, i.e. now not robotically in keeping with a device or procedure (AI may provide an answer, however we’re now not there but).

Attributes meant for construction use, corresponding to #[Deprecated], can also be got rid of the similar method that union kinds are got rid of. However attributes that fluctuate the appliance’s conduct in manufacturing can’t be got rid of, they usually can’t be immediately transpiled both.

As of lately, no transpiler can take code with PHP 8.0 attributes and robotically produce its identical PHP 7.4 code. In consequence, in case your PHP code wishes to make use of attributes, then transpiling it is going to be tricky or unfeasible.

PHP Options Which Can Be Transpiled

Those are the options from PHP 7.1 and above which will recently be transpiled. In case your code most effective makes use of those options, you’ll be able to benefit from the simple task that your transpiled software will paintings. Differently, you’ll want to assess if the transpiled code will produce disasters.

PHP Model Options
7.1 The entirety
7.2 object type
parameter type widening
PREG_UNMATCHED_AS_NULL flag in preg_match
7.3 Reference assignments in list() / array destructuring (Except for within foreach#4376)
Flexible Heredoc and Nowdoc syntax
Trailing commas in functions calls
set(raw)cookie accepts $option argument
7.4 Typed properties
Arrow functions
Null coalescing assignment operator
Unpacking inside arrays
Numeric literal separator
strip_tags() with array of tag names
covariant return types and contravariant param types
8.0 Union types
mixed pseudo type
static return type
::class magic constant on objects
match expressions
catch exceptions only by type
Null-safe operator
Class constructor property promotion
Trailing commas in parameter lists and closure use lists

PHP Transpilers

These days, there may be one device for transpiling PHP code: Rector.

Rector is a PHP reconstructor device, which converts PHP code in keeping with programmable regulations. We enter the supply code and the set of rules to run, and Rector will turn into the code.

Rector is operated by the use of command line, put in within the task by the use of Composer. When finished, Rector will output a “diff” (additions in inexperienced, removals in purple) of the code earlier than and after conversion:

"diff" output from Rector

“diff” output from Rector

Which Model of PHP to Transpile to

To transpile code throughout PHP variations, the corresponding regulations will have to be created.

Nowadays, the Rector library contains lots of the regulations for transpiling code inside the vary of PHP 8.0 to 7.1. Therefore, we will be able to reliably transpile our PHP code as a long way down as edition 7.1.

There also are rules for transpiling from PHP 7.1 to 7.0 and from 7.0 to 5.6, however those don’t seem to be exhaustive. Paintings is underway to finish them, so we would possibly sooner or later transpile PHP code all the way down to edition 5.6.

Transpiling vs Backporting

Backporting is very similar to transpiling, however more effective. Backporting code does now not essentially depend on new options from a language. As a substitute, the similar capability can also be equipped to an older edition of the language just by copying/pasting/adapting the corresponding code from the brand new edition of the language.

As an example, the serve as str_contains was once presented in PHP 8.0. The similar serve as for PHP 7.4 and underneath can also be simply applied like this:

if (!outlined('PHP_VERSION_ID') || (outlined('PHP_VERSION_ID') && PHP_VERSION_ID < 80000)) {
  if (!function_exists('str_contains')) {
    /**
     * Assessments if a string incorporates any other
     *
     * @param string $haystack The string to look in
     * @param string $needle The string to look
     * @go back boolean Returns TRUE if the needle was once present in haystack, FALSE differently.
     */
    serve as str_contains(string $haystack, string $needle): bool
    {
      go back strpos($haystack, $needle) !== false;
    }
  }
}

As a result of backporting is more effective than transpiling, we will have to go for this resolution every time backporting does the process.

In regards to the vary between PHP 8.0 to 7.1, we will be able to use Symfony‘s polyfill libraries:

Those libraries backport the next purposes, categories, constants, and interfaces:

PHP Model Options
7.2 Purposes:

Constants:

7.3 Purposes:

Exceptions:

7.4 Purposes:

8.0 Interfaces:

  • Stringable

Categories:

  • ValueError
  • UnhandledMatchError

Constants:

  • FILTER_VALIDATE_BOOL

Purposes:

Examples of Transpiled PHP

Let’s check up on a couple of examples of transpiled PHP code, and a couple of programs which might be being totally transpiled.

PHP Code

The fit expression was once introduced in PHP 8.0. This supply code:

serve as getFieldValue(string $fieldName): ?string
{
  go back fit($fieldName) {
    'foo' => 'foofoo',
    'bar' => 'barbar',
    'baz' => 'bazbaz',
    default => null,
  };
}

…shall be transpiled to its identical PHP 7.4 edition, the usage of the transfer operator:

serve as getFieldValue(string $fieldName): ?string
{
  transfer ($fieldName) {
    case 'foo':
      go back 'foofoo';
    case 'bar':
      go back 'barbar';
    case 'baz':
      go back 'bazbaz';
    default:
      go back null;
  }
}

The nullsafe operator was once additionally presented in PHP 8.0:

public serve as getValue(TypeResolverInterface $typeResolver): ?string
{
  go back $this->getResolver($typeResolver)?->getValue();
}

The transpiled code must assign the worth of the operation to a brand new variable first, as to steer clear of executing the operation two times:

public serve as getValue(TypeResolverInterface $typeResolver): ?string
{
  go back ($val = $this->getResolver($typeResolver)) ? $val->getValue() : null;
}

The constructor property promotion characteristic, additionally presented in PHP 8.0, permits builders to jot down much less code:

elegance QueryResolver
{
  serve as __construct(safe QueryFormatter $queryFormatter)
  {
  }
}

When transpiling it for PHP 7.4, the total piece of code is produced:

 elegance QueryResolver
 {
  safe QueryFormatter $queryFormatter;

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

The transpiled code above incorporates typed properties, which have been presented in PHP 7.4. Transpiling that code all the way down to PHP 7.3 replaces them with docblocks:

 elegance QueryResolver
 {
  /**
   * @var QueryFormatter
   */
  safe $queryFormatter;

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

PHP Programs

The next libraries are being transpiled for manufacturing:

Library/description Code/notes
Rector
PHP reconstructor device that makes transpiling imaginable
Source code
Transpiled code
Notes
Easy Coding Standards
Software to have PHP code adhere to a algorithm
Source code
Transpiled code
Notes
GraphQL API for WordPress
Plugin offering a GraphQL server for WordPress
Source code
Transpiled code
Notes

Professionals and Cons of Transpiling PHP

The advantage of transpiling PHP has already been described: it permits the supply code to make use of PHP 8.0 (i.e. the newest edition of PHP), which shall be reworked to a decrease edition for PHP for manufacturing to run in a legacy software or setting.

This successfully permits us to transform higher builders, generating code with upper high quality. It is because our supply code can use PHP 8.0’s union kinds, PHP 7.4’s typed homes, and the differing kinds and pseudo-types added to every new edition of PHP (combined from PHP 8.0, object from PHP 7.2), amongst different trendy options of PHP.

The usage of those options, we will be able to higher catch insects all over construction and write code that’s more uncomplicated to learn.

Now, let’s check out the drawbacks.

It Should Be Coded And Maintained

Rector can transpile code robotically, however the procedure will most likely require some handbook enter to make it paintings with our explicit setup.

3rd-Birthday party Libraries Should Additionally Be Transpiled

This turns into a subject every time transpiling them produces mistakes since we will have to then delve into their supply code to determine the imaginable explanation why. If the problem can also be fastened and the task is open supply, we can want to publish a pull request. If the library isn't open supply, we would possibly hit a roadblock.

Rector Does Now not Tell Us When The Code Can not Be Transpiled

If the supply code incorporates PHP 8.0 attributes or every other characteristic that can't be transpiled, we can not continue. Then again, Rector is not going to take a look at this situation, so we want to do it manually. This might not be a large downside relating to our personal supply code since we're already aware of it, however it might transform a drawback relating to third-party dependencies.

Debugging Data Makes use of The Transpiled Code, Now not The Supply Code

When the appliance produces an error message with a stack hint in manufacturing, the road quantity will level to the transpiled code. We want to convert again from transpiled to unique code to seek out the corresponding line quantity within the supply code.

The Transpiled Code Should Additionally Be Prefixed

Our transpiled task and a few different library additionally put in within the manufacturing setting may use the similar third-party dependency. This third-party dependency shall be transpiled for our task and stay its unique supply code for the opposite library. Therefore, the transpiled edition will have to be prefixed by the use of PHP-Scoper, Strauss, or another device to steer clear of doable conflicts.

Signal Up For the E-newsletter

Transpiling Should Take Position All the way through Steady Integration (CI)

Since the transpiled code will naturally override the supply code, we will have to now not run the transpiling procedure on our construction computer systems, or we’ll possibility developing unwanted side effects. Operating the method all over a CI run is extra appropriate (extra in this underneath).

Easy methods to Transpile PHP

First, we want to set up Rector in our task for construction:

composer require rector/rector --dev

We then create a rector.php configuration record within the root listing of the task containing the specified units of regulations. To downgrade code from PHP 8.0 to 7.1, we use this config:

use RectorSetValueObjectDowngradeSetList;
use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;

go back static serve as (ContainerConfigurator $containerConfigurator): void {
    $containerConfigurator->import(DowngradeSetList::PHP_80);
    $containerConfigurator->import(DowngradeSetList::PHP_74);
    $containerConfigurator->import(DowngradeSetList::PHP_73);
    $containerConfigurator->import(DowngradeSetList::PHP_72);
};

To verify the method executes as anticipated, we will be able to run Rector’s procedure command in dry mode, passing the positioning(s) to procedure (on this case, all recordsdata beneath the folder src/):

supplier/bin/rector procedure src --dry-run

To accomplish the transpiling, we run Rector’s procedure command, which can alter the recordsdata inside their present location:

supplier/bin/rector procedure src

Please understand: if we run rector procedure in our construction computer systems, the supply code shall be transformed in position, beneath src/. Then again, we need to produce the transformed code in a unique location to not override the supply code when downgrading code. Because of this, operating the method is best suited all over steady integration.

Optimizing the Transpiling Procedure

To generate a transpiled deliverable for manufacturing, most effective the code for manufacturing will have to be transformed; code wanted just for construction can also be skipped. That implies we will be able to steer clear of transpiling all assessments (for each our task and its dependencies) and all dependencies for construction.

Relating to assessments, we can already know the place those for our task are positioned — as an example, beneath the folder assessments/. We will have to additionally in finding out the place those for the dependencies are — as an example, beneath their subfolders assessments/, check/ and Check/ (for various libraries). Then, we inform Rector to skip processing those folders:

go back static serve as (ContainerConfigurator $containerConfigurator): void {
  // ...

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

Relating to dependencies, Composer is aware of which of them are for construction (the ones beneath access require-dev in composer.json) and which of them are for manufacturing (the ones beneath access require).

To retrieve from Composer the trails of all dependencies for manufacturing, we run:

composer data --path --no-dev

This command will produce a listing of dependencies with their title and course, like this:

mind/cortex                     /Customers/leo/GitHub/leoloso/PoP/supplier/mind/cortex
composer/installers              /Customers/leo/GitHub/leoloso/PoP/supplier/composer/installers
composer/semver                  /Customers/leo/GitHub/leoloso/PoP/supplier/composer/semver
guzzlehttp/guzzle                /Customers/leo/GitHub/leoloso/PoP/supplier/guzzlehttp/guzzle
league/pipeline                  /Customers/leo/GitHub/leoloso/PoP/supplier/league/pipeline

We will be able to extract the entire paths and feed them into the Rector command, which can then procedure our task’s src/ folder plus the ones folders containing all dependencies for manufacturing:

$ paths="$(composer data --path --no-dev | reduce -d' ' -f2- | sed 's/ //g' | tr 'n' ' ')"
$ supplier/bin/rector procedure src $paths

An additional development can save you Rector from processing the ones dependencies already the usage of the objective PHP edition. If a library has been coded with PHP 7.1 (or any edition underneath), then there’s no want to transpile it to PHP 7.1.

To succeed in this, we will be able to download the record of libraries requiring PHP 7.2 and above and procedure most effective the ones. We will be able to download the names of some of these libraries by the use of Composer’s why-not command, like this:

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

As a result of this command does now not paintings with the --no-dev flag, to incorporate most effective dependencies for manufacturing, we first want to take away the dependencies for construction and regenerate the autoloader, execute the command, after which upload them once more:

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

Composer’s data --path command retrieves the trail for a package deal, with this layout:

# Executing this command
$ composer data psr/cache --path   
# Produces this reaction:
psr/cache /Customers/leo/GitHub/leoloso/PoP/supplier/psr/cache

We execute this command for all pieces in our record to procure all paths to transpile:

Desire a webhosting resolution that will provide you with a aggressive edge? Kinsta’s were given you coated with unbelievable velocity, state of the art safety, and auto-scaling. Check out our plans

for package deal in $programs
do
  course=$(composer data $package deal --path | reduce -d' ' -f2-)
  paths="$paths $course"
performed

After all, we offer this record to Rector (plus the task’s src/ folder):

supplier/bin/rector procedure src $paths

Pitfalls to Keep away from When Transpiling Code

Transpiling code may well be regarded as an artwork, regularly requiring tweaks explicit to the task. Let’s see a couple of issues we would possibly come into.

Chained Laws Are Now not At all times Processed

A chained rule is when a rule must convert the code produced through a prior rule.

As an example, library symfony/cache incorporates this code:

ultimate elegance CacheItem implements ItemInterface
{
  public serve as tag($tags): ItemInterface
  {
    // ...
    go back $this;
  }
}

When transpiling from PHP 7.4 to 7.3, serve as tag will have to go through two adjustments:

The outcome will have to be this one:

ultimate elegance CacheItem implements ItemInterface
{
  public serve as tag($tags)
  {
    // ...
    go back $this;
  }
}

Then again, Rector most effective outputs the intermediate level:

ultimate elegance CacheItem implements ItemInterface
{
  public serve as tag($tags): self
  {
    // ...
    go back $this;
  }
}

The problem is that Rector cannot always control the order in which the rules are applied.

The answer is to spot which chained regulations had been left unprocessed, and execute a brand new Rector run to use them.

To spot the chained regulations, we run Rector two times at the supply code, like this:

$ supplier/bin/rector procedure src
$ supplier/bin/rector procedure src --dry-run

The primary time, we run Rector as anticipated, to execute the transpiling. The second one time, we use the --dry-run flag to find if there are nonetheless adjustments to be made. If there are, the command will go out with an error code, and the “diff” output will point out which rule(s) can nonetheless be carried out. That will imply that the primary run was once now not whole, with some chained rule now not being processed.

Running Rector with --dry-run flag

Operating Rector with –dry-run flag

After we’ve recognized the unapplied chained rule (or regulations), we will be able to then create any other Rector config record — as an example, rector-chained-rule.php will execute the lacking rule. As a substitute of processing a complete algorithm for all recordsdata beneath src/, this time, we will be able to run the precise lacking rule at the explicit record the place it must be carried out:

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

go back static serve as (ContainerConfigurator $containerConfigurator): void {
  $facilities = $containerConfigurator->facilities();
  $services->set(DowngradeSelfTypeDeclarationRector::elegance);

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

After all, we inform Rector on its 2nd cross to make use of the brand new config record by the use of enter --config:

# First cross with all adjustments
$ supplier/bin/rector procedure src

# 2d cross to mend a selected downside
$ supplier/bin/rector procedure --config=rector-chained-rule.php

Composer Dependencies Would possibly Be Inconsistent

Libraries may claim a dependency to be slated for construction (i.e. beneath require-dev in composer.json), but nonetheless, reference some code from them for manufacturing (corresponding to on some recordsdata beneath src/, now not assessments/).

Normally, this isn’t an issue as a result of that code might not be loaded on manufacturing, so there'll by no means be an error at the software. Then again, when Rector processes the supply code and its dependencies, it validates that each one referenced code can also be loaded. Rector will throw an error if any record references some piece of code from a non-installed library (as it was once declared to be wanted for construction most effective).

As an example, elegance EarlyExpirationHandler from Symfony’s Cache element implements interface MessageHandlerInterface from the Messenger element:

elegance EarlyExpirationHandler implements MessageHandlerInterface
{
    //...
}

Then again, symfony/cache proclaims symfony/messenger to be a dependency for development. Then, when operating Rector on a task which will depend on symfony/cache, it is going to throw an error:

[ERROR] May just now not procedure "supplier/symfony/cache/Messenger/EarlyExpirationHandler.php" record, because of:             
  "Analyze error: "Elegance SymfonyComponentMessengerHandlerMessageHandlerInterface now not discovered.". Come with your recordsdata in "$parameters->set(Choice::AUTOLOAD_PATHS, [...]);" in "rector.php" config.
  See https://github.com/rectorphp/rector#configuration".   

There are 3 answers to this factor:

  1. Within the Rector config, skip processing the record that references that piece of code:
go back static serve as (ContainerConfigurator $containerConfigurator): void {
  // ...

  $parameters->set(Choice::SKIP, [
    __DIR__ . '/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php',
  ]);
};
  1. Obtain the lacking library and upload its course to be autoloaded through Rector:
go back static serve as (ContainerConfigurator $containerConfigurator): void {
  // ...

  $parameters->set(Choice::AUTOLOAD_PATHS, [
    __DIR__ . '/vendor/symfony/messenger',
  ]);
};
  1. Have your task rely at the lacking library for manufacturing:
composer require symfony/messenger

Transpiling and Steady Integration

As discussed previous, in our construction computer systems we will have to use the --dry-run flag when operating Rector, or differently, the supply code shall be overridden with the transpiled code. Because of this, it’s extra appropriate to run the true transpiling procedure all over steady integration (CI), the place we will be able to spin up transient runners to execute the method.

A really perfect time to execute the transpiling procedure is when producing the discharge for our task. As an example, the code below is a workflow for GitHub Actions, which creates the discharge of a WordPress plugin:

title: Generate Installable Plugin and Add as Unlock Asset
on:
  launch:
    kinds: [published]
jobs:
  construct:
    title: Construct, Downgrade and Add Unlock
    runs-on: ubuntu-latest
    steps:
      - title: Checkout code
        makes use of: movements/checkout@v2
      - title: Downgrade code for manufacturing (to PHP 7.1)
        run: |
          composer set up
          supplier/bin/rector procedure
          sed -i 's/Calls for PHP: 7.4/Calls for PHP: 7.1/' graphql-api.php
      - title: Construct task for manufacturing
        run: |
          composer set up --no-dev --optimize-autoloader
          mkdir construct
      - title: Create artifact
        makes use of: montudor/action-zip@v0.1.0
        with:
          args: zip -X -r construct/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** construct**
      - title: Add artifact
        makes use of: movements/upload-artifact@v2
        with:
            title: graphql-api
            course: construct/graphql-api.zip
      - title: Add to launch
        makes use of: JasonEtco/upload-to-release@grasp
        with:
          args: construct/graphql-api.zip software/zip
        env:
          GITHUB_TOKEN: ${{ secrets and techniques.GITHUB_TOKEN }}

This workflow incorporates a typical process to release a WordPress plugin via GitHub Actions. The brand new addition, to transpile the plugin’s code from PHP 7.4 to 7.1, occurs on this step:

      - title: Downgrade code for manufacturing (to PHP 7.1)
        run: |
          supplier/bin/rector procedure
          sed -i 's/Calls for PHP: 7.4/Calls for PHP: 7.1/' graphql-api.php

Taken in combination, this workflow now plays the next steps:

  1. Assessments out the supply code for a WordPress plugin from its repository, written with PHP 7.4
  2. Installs its Composer dependencies
  3. Transpiles its code from PHP 7.4 to 7.1
  4. Modifies the “Calls for PHP” access within the plugin’s primary record’s header from "7.4" to "7.1"
  5. Gets rid of the dependencies wanted for construction
  6. Creates the plugin’s .zip record, with the exception of all unneeded recordsdata
  7. Uploads the .zip record as a launch asset (and, as well as, as an artifact to the GitHub Motion)

Trying out the Transpiled Code

As soon as the code has been transpiled to PHP 7.1, how do we all know that it really works neatly? Or, in different phrases, how can we realize it has been totally transformed, and no remnants of upper variations of PHP code had been left in the back of?

Very similar to transpiling the code, we will be able to put in force the answer inside a CI procedure. The speculation is to arrange the runner’s setting with PHP 7.1 and run a linter at the transpiled code. If any piece of code isn't suitable with PHP 7.1 (corresponding to a typed assets from PHP 7.4 that wasn’t transformed), then the linter will throw an error.

A linter for PHP that works neatly is PHP Parallel Lint. We will be able to set up this library as a dependency for construction in our task, or have the CI procedure set up it as a standalone Composer task:

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

Each time the code incorporates PHP 7.2 and above, PHP Parallel Lint will throw an error like this one:

Run php-parallel-lint/parallel-lint layers/ supplier/ --exclude supplier/symfony/polyfill-ctype/bootstrap80.php --exclude supplier/symfony/polyfill-intl-grapheme/bootstrap80.php --exclude supplier/symfony/polyfill-intl-idn/bootstrap80.php --exclude supplier/symfony/polyfill-intl-normalizer/bootstrap80.php --exclude supplier/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 recordsdata in 15.4 seconds
Syntax error present in 1 record

------------------------------------------------------------
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| )))  
Surprising ')' in layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php on line 55
Error: Procedure finished with go out code 1.

Let’s upload the linter into our CI’s workflow. The stairs to execute to transpile code from PHP 8.0 to 7.1 and check it are:

  1. Take a look at the supply code
  2. Have the surroundings run PHP 8.0, so Rector can interpret the supply code
  3. Transpile the code to PHP 7.1
  4. Set up the PHP linter device
  5. Transfer the surroundings’s PHP edition to 7.1
  6. Run the linter at the transpiled code

This GitHub Action workflow does the process:

title: Downgrade PHP assessments
jobs:
  primary:
    title: Downgrade code to PHP 7.1 by the use of Rector, and execute assessments
    runs-on: ubuntu-latest
    steps:
      - title: Checkout code
        makes use of: movements/checkout@v2

      - title: Set-up PHP
        makes use of: shivammathur/setup-php@v2
        with:
          php-version: 8.0
          protection: none

      - title: Native programs - Downgrade PHP code by the use of Rector
        run: |
          composer set up
          supplier/bin/rector procedure

      # Get ready for checking out on PHP 7.1
      - title: Set up PHP Parallel Lint
        run: composer create-project php-parallel-lint/php-parallel-lint --ansi

      - title: Transfer to PHP 7.1
        makes use of: shivammathur/setup-php@v2
        with:
          php-version: 7.1
          protection: none

      # Lint the transpiled code
      - title: Run PHP Parallel Lint on PHP 7.1
        run: php-parallel-lint/parallel-lint src/ supplier/ --exclude supplier/symfony/polyfill-ctype/bootstrap80.php --exclude supplier/symfony/polyfill-intl-grapheme/bootstrap80.php --exclude supplier/symfony/polyfill-intl-idn/bootstrap80.php --exclude supplier/symfony/polyfill-intl-normalizer/bootstrap80.php --exclude supplier/symfony/polyfill-mbstring/bootstrap80.php

Please understand that a number of bootstrap80.php recordsdata from Symfony’s polyfill libraries (which needn't be transpiled) will have to be excluded from the linter. Those recordsdata comprise PHP 8.0, so the linter would throw mistakes when processing them. Then again, with the exception of those recordsdata is protected since they'll be loaded on manufacturing most effective when running PHP 8.0 or above:

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

Whether you're creating a public plugin for WordPress or you're updating legacy code, there are many reasons that using the latest PHP version may be impossible 👩‍💻 Learn how transpiling can help in this guide 👇Click to Tweet

Abstract

This newsletter taught us how you can transpile our PHP code, permitting us to make use of PHP 8.0 within the supply code and create a launch that works on PHP 7.1. Transpiling is finished by the use of Rector, a PHP reconstructor device.

Transpiling our code makes us higher builders since we will be able to higher catch insects in construction and bring code that’s naturally more uncomplicated to learn and perceive.

Transpiling additionally allows us to decouple our code with explicit PHP necessities from the CMS. We will be able to now accomplish that if we want to make use of the newest edition of PHP to create a publicly to be had WordPress plugin or Drupal module with out significantly limiting our userbase.

Do you may have any questions left about transpiling PHP? Tell us within the feedback phase!

The put up The Ultimate Guide for Transpiling PHP Code seemed first on Kinsta.

WP Hosting

[ continue ]