Site icon Hip-Hop Website Design and Development

The Final Information for Transpiling PHP Code

In perfect circumstances, we must always use PHP 8.0 (the most recent model as of scripting this) for all our websites and replace it as quickly as a brand new model is launched. Nonetheless, builders will typically have to work with earlier PHP variations, resembling when making a public plugin for WordPress or working with legacy code which impedes upgrading the webserver’s atmosphere.

In these conditions, we might hand over hope of utilizing the most recent PHP code. However there’s a higher various: we will nonetheless write our supply code with PHP 8.0 and transpile it to a earlier PHP model — even to PHP 7.1.

On this information, we’ll train you all the things you want to learn about transpiling PHP code.

What Is Transpiling?

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

Transpiling will not be a brand new idea inside internet development: client-side builders will fairly doubtless be aware of Babel, a transpiler for JavaScript code.

Babel converts JavaScript code from the trendy ECMAScript 2015+ model right into a legacy model appropriate with older browsers. As an illustration, given an ES2015 arrow perform:

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

…Babel will convert it into its ES5 model:

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

What Is Transpiling PHP?

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

Transpiling PHP works the identical means as transpiling JavaScript: supply code from a fashionable PHP model is transformed into an equal code for an older PHP model.

Following the identical instance as earlier than, an arrow perform from PHP 7.4:

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

…will be transpiled into its equal PHP 7.3 model:

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

Arrow features will be transpiled as a result of they’re syntactic sugar, i.e. a brand new syntax to provide an current habits. That is the low-hanging fruit.

Nonetheless, there are additionally new options that create a brand new habits, and as such, there shall be no equal code for earlier variations of PHP. That’s the case with union sorts, launched in PHP 8.0:

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

In these conditions, transpiling can nonetheless be performed so long as the brand new characteristic is required for development however not for manufacturing. Then, we will merely take away the characteristic altogether from the transpiled code with out critical penalties.

One such instance is union sorts. This characteristic is used to examine that there isn’t a mismatch between the enter sort and its supplied worth, which helps stop bugs. If there’s a battle with sorts, there shall be an error already in development, and we must always catch it and repair it earlier than the code reaches manufacturing.

Therefore, we will afford to take away the characteristic from the code for manufacturing:

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

If the error nonetheless occurs in manufacturing, the thrown error message shall be much less exact than if we had union sorts. Nonetheless, this potential drawback is outweighed by having the ability to use union sorts within the first place.

physique a.novashare-ctt{show:block;background:#00abf0;margin:30px auto;padding:20px 20px 20px 15px;colour:#fff;text-decoration:none!necessary;box-shadow:none!necessary;-webkit-box-shadow:none!necessary;-moz-box-shadow:none!necessary;border:none;border-left:5px strong #00abf0}physique a.novashare-ctt:hover{colour:#fff;border-left:5px strong #008cc4}physique a.novashare-ctt:visited{colour:#fff}physique a.novashare-ctt *{pointer-events:none}physique a.novashare-ctt .novashare-ctt-tweet{show:block;font-size:18px;line-height:27px;margin-bottom:10px}physique a.novashare-ctt .novashare-ctt-cta-container{show:block;overflow:hidden}physique a.novashare-ctt .novashare-ctt-cta{float:proper}physique a.novashare-ctt.novashare-ctt-cta-left .novashare-ctt-cta{float:left}physique a.novashare-ctt .novashare-ctt-cta-text{font-size:16px;line-height:16px;vertical-align:center}physique a.novashare-ctt .novashare-ctt-cta-icon{margin-left:10px;show:inline-block;vertical-align:center}physique a.novashare-ctt .novashare-ctt-cta-icon svg{vertical-align:center;top:18px}physique a.novashare-ctt.novashare-ctt-simple{background:0 0;padding:10px 0 10px 20px;colour:inherit}physique a.novashare-ctt.novashare-ctt-simple-alt{background:#f9f9f9;padding:20px;colour:#404040}physique a.novashare-ctt.novashare-ctt-simple-alt:hover,physique a.novashare-ctt.novashare-ctt-simple:hover{border-left:5px strong #008cc4}physique a.novashare-ctt.novashare-ctt-simple .novashare-ctt-cta,physique a.novashare-ctt.novashare-ctt-simple-alt .novashare-ctt-cta{colour:#00abf0}physique a.novashare-ctt.novashare-ctt-simple-alt:hover .novashare-ctt-cta,physique a.novashare-ctt.novashare-ctt-simple:hover .novashare-ctt-cta{colour:#008cc4}In an ideal world, we must always have the ability to use PHP 8.0 on all our websites and replace it as quickly as a brand new model is launched However that is not all the time the case. Study all the things you want to learn about transpiling PHP code right here Click on to Tweet

Benefits of Transpiling PHP Code

Transpiling permits one to code an software utilizing the most recent model of PHP and produce a launch that additionally works in environments operating older variations of PHP.

This may be notably helpful for builders creating merchandise for legacy content material administration programs (CMS). WordPress, as an example, nonetheless formally helps PHP 5.6 (although it recommends PHP 7.4+). The proportion of WordPress websites operating PHP variations 5.6 to 7.2 — that are all Finish-of-Life (EOL), that means they’re not receiving safety updates anymore — stands at a large 34.8%, and people operating on any PHP model aside from 8.0 stands at a whopping 99.5%:

WordPress utilization stats by model. Picture supply: WordPress

Consequently, WordPress themes and plugins focused at a worldwide viewers will fairly doubtless be coded with an previous model of PHP to extend their doable attain. Because of transpiling, these might be coded utilizing PHP 8.0, and nonetheless be launched for an older PHP model, thus focusing on as many customers as doable.

Certainly, any software that should assist any PHP model aside from the latest one (even inside the vary of the presently supported PHP variations) can profit.

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

One other instance is when creating customized code for shoppers who can not run PHP 8.0 of their environments as a result of one cause or one other. Nonetheless, because of transpiling, builders can nonetheless code their deliverables utilizing PHP 8.0 and run them on these legacy environments.

When to Transpile PHP

PHP code can all the time be transpiled until it accommodates some PHP characteristic that has no equal within the earlier model of PHP.

That’s probably the case with attributes, launched in PHP 8.0:

#[SomeAttr]
perform someFunc() {}

#[AnotherAttr]
class SomeClass {}

Within the earlier instance utilizing arrow features, the code might be transpiled as a result of arrow features are syntactic sugar. Attributes, in distinction, create fully new habits. This habits may be reproduced with PHP 7.4 and beneath, however solely by manually coding it, i.e. not robotically based mostly on a software or course of (AI might present an answer, however we’re not there but).

Attributes supposed for development use, resembling #[Deprecated], will be eliminated the identical means that union sorts are eliminated. However attributes that modify the appliance’s habits in manufacturing can’t be eliminated, and so they can’t be immediately transpiled both.

As of right this moment, no transpiler can take code with PHP 8.0 attributes and robotically produce its equal PHP 7.4 code. Consequently, in case your PHP code wants to make use of attributes, then transpiling will probably be tough or unfeasible.

PHP Options Which Can Be Transpiled

These are the options from PHP 7.1 and above which may at present be transpiled. In case your code solely makes use of these options, you’ll be able to benefit from the certainty that your transpiled software will work. In any other case, you’ll have to assess if the transpiled code will produce failures.

PHP Model Options
7.1 Every part
7.2 object sort
parameter sort widening
PREG_UNMATCHED_AS_NULL flag in preg_match
7.3 Reference assignments in checklist() / array destructuring (Besides inside foreach#4376)
Versatile Heredoc and Nowdoc syntax
Trailing commas in features calls
set(uncooked)cookie accepts $choice argument
7.4 Typed properties
Arrow features
Null coalescing task operator
Unpacking inside arrays
Numeric literal separator
strip_tags() with array of tag names
covariant return sorts and contravariant param sorts
8.0 Union sorts
combined pseudo sort
static return sort
::class magic fixed on objects
match expressions
catch exceptions solely by sort
Null-safe operator
Class constructor property promotion
Trailing commas in parameter lists and closure use lists

PHP Transpilers

Presently, there’s one software for transpiling PHP code: Rector.

Rector is a PHP reconstructor software, which converts PHP code based mostly on programmable guidelines. We enter the supply code and the algorithm to run, and Rector will rework the code.

Rector is operated through command line, put in within the challenge through Composer. When executed, Rector will output a “diff” (additions in inexperienced, removals in crimson) of the code earlier than and after conversion:

“diff” output from Rector

Which Model of PHP to Transpile to

To transpile code throughout PHP variations, the corresponding guidelines should be created.

As we speak, the Rector library consists of a lot of the guidelines for transpiling code inside the vary of PHP 8.0 to 7.1. Therefore, we will reliably transpile our PHP code as far down as model 7.1.

There are additionally guidelines for transpiling from PHP 7.1 to 7.0 and from 7.0 to five.6, however these are usually not exhaustive. Work is underway to finish them, so we could finally transpile PHP code all the way down to model 5.6.

Transpiling vs Backporting

Backporting is much like transpiling, however less complicated. Backporting code doesn’t essentially depend on new options from a language. As an alternative, the identical performance will be supplied to an older model of the language just by copying/pasting/adapting the corresponding code from the brand new model of the language.

As an illustration, the perform str_contains was launched in PHP 8.0. The identical perform for PHP 7.4 and beneath will be simply applied like this:

if (!outlined('PHP_VERSION_ID') || (outlined('PHP_VERSION_ID') && PHP_VERSION_ID < 80000)) {
  if (!function_exists('str_contains')) {
    /**
     * Checks if a string accommodates one other
     *
     * @param string $haystack The string to look in
     * @param string $needle The string to look
     * @return boolean Returns TRUE if the needle was present in haystack, FALSE in any other case.
     */
    perform str_contains(string $haystack, string $needle): bool
    {
      return strpos($haystack, $needle) !== false;
    }
  }
}

As a result of backporting is less complicated than transpiling, we must always go for this answer every time backporting does the job.

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

These libraries backport the next features, lessons, constants, and interfaces:

PHP Model Options
7.2 Features:

Constants:

7.3 Features:

Exceptions:

7.4 Features:
8.0 Interfaces:
  • Stringable

Courses:

  • ValueError
  • UnhandledMatchError

Constants:

  • FILTER_VALIDATE_BOOL

Features:

Examples of Transpiled PHP

Let’s examine a number of examples of transpiled PHP code, and some packages that are being absolutely transpiled.

PHP Code

The match expression was launched in PHP 8.0. This supply code:

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

…shall be transpiled to its equal PHP 7.4 model, utilizing the swap operator:

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

The nullsafe operator was additionally launched in PHP 8.0:

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

The transpiled code must assign the worth of the operation to a brand new variable first, as to keep away from executing the operation twice:

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

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

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

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

 class QueryResolver
 {
  protected QueryFormatter $queryFormatter;

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

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

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

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

PHP Packages

The next libraries are being transpiled for manufacturing:

Library/description Code/notes
Rector
PHP reconstructor software that makes transpiling doable
Supply code
Transpiled code
Notes
Simple Coding Requirements
Device to have PHP code adhere to a algorithm
Supply code
Transpiled code
Notes
GraphQL API for WordPress
Plugin offering a GraphQL server for WordPress
Supply code
Transpiled code
Notes

Execs 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 most recent model of PHP), which shall be reworked to a decrease model for PHP for manufacturing to run in a legacy software or atmosphere.

This successfully permits us to turn into higher builders, producing code with greater high quality. It’s because our supply code can use PHP 8.0’s union sorts, PHP 7.4’s typed properties, and the differing types and pseudo-types added to every new model of PHP (combined from PHP 8.0, object from PHP 7.2), amongst different fashionable options of PHP.

Utilizing these options, we will higher catch bugs throughout development and write code that’s simpler to learn.

Now, let’s check out the drawbacks.

It Should Be Coded And Maintained

Rector can transpile code robotically, however the course of will doubtless require some guide enter to make it work with our particular setup.

Third-Get together Libraries Should Additionally Be Transpiled

This turns into a problem every time transpiling them produces errors since we should then delve into their supply code to search out out the doable cause. If the difficulty will be mounted and the challenge is open supply, we might want to submit a pull request. If the library will not be open supply, we could hit a roadblock.

Rector Does Not Inform Us When The Code Can not Be Transpiled

If the supply code accommodates PHP 8.0 attributes or every other characteristic that can not be transpiled, we can not proceed. Nonetheless, Rector is not going to take a look at this situation, so we have to do it manually. This is probably not a giant drawback regarding our personal supply code since we’re already aware of it, but it surely might turn into an impediment regarding third-party dependencies.

Debugging Info Makes use of The Transpiled Code, 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 have to convert again from transpiled to authentic code to search out the corresponding line quantity within the supply code.

The Transpiled Code Should Additionally Be Prefixed

Our transpiled challenge and another library additionally put in within the manufacturing atmosphere might use the identical third-party dependency. This third-party dependency shall be transpiled for our challenge and maintain its authentic supply code for the opposite library. Therefore, the transpiled model should be prefixed through PHP-Scoper, Strauss, or another software to keep away from potential conflicts.

Signal Up For the Publication

Transpiling Should Take Place Throughout Steady Integration (CI)

As a result of the transpiled code will naturally override the supply code, we must always not run the transpiling course of on our development computer systems, or we’ll threat creating unwanted side effects. Working the method throughout a CI run is extra appropriate (extra on this beneath).

Easy methods to Transpile PHP

First, we have to set up Rector in our challenge for development:

composer require rector/rector --dev

We then create a rector.php configuration file within the root listing of the challenge containing the required units of guidelines. To downgrade code from PHP 8.0 to 7.1, we use this config:

use RectorSetValueObjectDowngradeSetList;
use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;

return static perform (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 run Rector’s course of command in dry mode, passing the situation(s) to course of (on this case, all information underneath the folder src/):

vendor/bin/rector course of src --dry-run

To carry out the transpiling, we run Rector’s course of command, which can modify the information inside their current location:

vendor/bin/rector course of src

Please discover: if we run rector course of in our development computer systems, the supply code shall be transformed in place, underneath src/. Nonetheless, we wish 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 throughout steady integration.

Optimizing the Transpiling Course of

To generate a transpiled deliverable for manufacturing, solely the code for manufacturing should be transformed; code wanted just for development will be skipped. Meaning we will keep away from transpiling all exams (for each our challenge and its dependencies) and all dependencies for development.

Regarding exams, we are going to already know the place those for our challenge are situated — as an example, underneath the folder exams/. We should additionally discover out the place those for the dependencies are — for instance, underneath their subfolders exams/, check/ and Check/ (for various libraries). Then, we inform Rector to skip processing these folders:

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

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

Regarding dependencies, Composer is aware of which of them are for development (these underneath entry require-dev in composer.json) and which of them are for manufacturing (these underneath entry require).

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

composer data --path --no-dev

This command will produce an inventory of dependencies with their identify and path, like this:

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

We will extract all of the paths and feed them into the Rector command, which can then course of our challenge’s src/ folder plus these folders containing all dependencies for manufacturing:

$ paths="$(composer data --path --no-dev | minimize -d' ' -f2- | sed 's/ //g' | tr 'n' ' ')"
$ vendor/bin/rector course of src $paths

An extra enchancment can stop Rector from processing these dependencies already utilizing the goal PHP model. If a library has been coded with PHP 7.1 (or any model beneath), then there’s no have to transpile it to PHP 7.1.

To attain this, we will get hold of the checklist of libraries requiring PHP 7.2 and above and course of solely these. We are going to get hold of the names of all these libraries through Composer’s why-not command, like this:

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

As a result of this command doesn’t work with the --no-dev flag, to incorporate solely dependencies for manufacturing, we first have to take away the dependencies for development and regenerate the autoloader, execute the command, after which add them once more:

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

Composer’s data --path command retrieves the trail for a bundle, with this format:

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

We execute this command for all gadgets in our checklist to acquire all paths to transpile:

Want a internet hosting answer that offers you a aggressive edge? Kinsta’s received you lined with unbelievable pace, state-of-the-art safety, and auto-scaling. Try our plans

for bundle in $packages
do
  path=$(composer data $bundle --path | minimize -d' ' -f2-)
  paths="$paths $path"
performed

Lastly, we offer this checklist to Rector (plus the challenge’s src/ folder):

vendor/bin/rector course of src $paths

Pitfalls to Keep away from When Transpiling Code

Transpiling code might be thought-about an artwork, typically requiring tweaks particular to the challenge. Let’s see a number of issues we could come into.

Chained Guidelines Are Not At all times Processed

A chained rule is when a rule must convert the code produced by a earlier rule.

As an illustration, library symfony/cache accommodates this code:

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

When transpiling from PHP 7.4 to 7.3, perform tag should bear two modifications:

The tip consequence needs to be this one:

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

Nonetheless, Rector solely outputs the intermediate stage:

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

The problem is that Rector can not all the time management the order during which the foundations are utilized.

The answer is to determine which chained guidelines have been left unprocessed, and execute a brand new Rector run to use them.

To determine the chained guidelines, we run Rector twice on the supply code, like this:

$ vendor/bin/rector course of src
$ vendor/bin/rector course of src --dry-run

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

Working Rector with –dry-run flag

As soon as we’ve recognized the unapplied chained rule (or guidelines), we will then create one other Rector config file — as an example, rector-chained-rule.php will execute the lacking rule. As an alternative of processing a full algorithm for all information underneath src/, this time, we will run the particular lacking rule on the particular file the place it must be utilized:

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

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

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

Lastly, we inform Rector on its second cross to make use of the brand new config file through enter --config:

# First cross with all modifications
$ vendor/bin/rector course of src

# Second cross to repair a particular drawback
$ vendor/bin/rector course of --config=rector-chained-rule.php

Composer Dependencies Could Be Inconsistent

Libraries might declare a dependency to be slated for development (i.e. underneath require-dev in composer.json), but nonetheless, reference some code from them for manufacturing (resembling on some information underneath src/, not exams/).

Often, this isn’t an issue as a result of that code is probably not loaded on manufacturing, so there’ll by no means be an error on the appliance. Nonetheless, when Rector processes the supply code and its dependencies, it validates that each one referenced code will be loaded. Rector will throw an error if any file references some piece of code from a non-installed library (as a result of it was declared to be wanted for development solely).

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

class EarlyExpirationHandler implements MessageHandlerInterface
{
    //...
}

Nonetheless, symfony/cache declares symfony/messenger to be a dependency for development. Then, when operating Rector on a challenge which will depend on symfony/cache, it’s going to throw an error:

[ERROR] Couldn't course of "vendor/symfony/cache/Messenger/EarlyExpirationHandler.php" file, as a result of:             
  "Analyze error: "Class SymfonyComponentMessengerHandlerMessageHandlerInterface not discovered.". Embody your information in "$parameters->set(Possibility::AUTOLOAD_PATHS, [...]);" in "rector.php" config.
  See https://github.com/rectorphp/rector#configuration".   

There are three options to this situation:

  1. Within the Rector config, skip processing the file that references that piece of code:
return static perform (ContainerConfigurator $containerConfigurator): void {
  // ...

  $parameters->set(Possibility::SKIP, [
    __DIR__ . '/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php',
  ]);
};
  1. Obtain the lacking library and add its path to be autoloaded by Rector:
return static perform (ContainerConfigurator $containerConfigurator): void {
  // ...

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

Transpiling and Steady Integration

As talked about earlier, in our development computer systems we should use the --dry-run flag when operating Rector, or in any other case, the supply code shall be overridden with the transpiled code. Because of this, it’s extra appropriate to run the precise transpiling course of throughout steady integration (CI), the place we will spin up short-term runners to execute the method.

A perfect time to execute the transpiling course of is when producing the discharge for our challenge. As an illustration, the code beneath is a workflow for GitHub Actions, which creates the discharge of a WordPress plugin:

identify: Generate Installable Plugin and Add as Launch Asset
on:
  launch:
    sorts: [published]
jobs:
  construct:
    identify: Construct, Downgrade and Add Launch
    runs-on: ubuntu-latest
    steps:
      - identify: Checkout code
        makes use of: actions/checkout@v2
      - identify: Downgrade code for manufacturing (to PHP 7.1)
        run: |
          composer set up
          vendor/bin/rector course of
          sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php
      - identify: Construct challenge for manufacturing
        run: |
          composer set up --no-dev --optimize-autoloader
          mkdir construct
      - identify: 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**
      - identify: Add artifact
        makes use of: actions/upload-artifact@v2
        with:
            identify: graphql-api
            path: construct/graphql-api.zip
      - identify: 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 accommodates a typical process to launch a WordPress plugin through GitHub Actions. The brand new addition, to transpile the plugin’s code from PHP 7.4 to 7.1, occurs on this step:

      - identify: Downgrade code for manufacturing (to PHP 7.1)
        run: |
          vendor/bin/rector course of
          sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php

Taken collectively, this workflow now performs the next steps:

  1. Checks 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 “Requires PHP” entry within the plugin’s principal file’s header from "7.4" to "7.1"
  5. Removes the dependencies wanted for development
  6. Creates the plugin’s .zip file, excluding all unneeded information
  7. Uploads the .zip file as a launch asset (and, as well as, as an artifact to the GitHub Motion)

Testing the Transpiled Code

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

Just like transpiling the code, we will implement the answer inside a CI course of. The concept is to arrange the runner’s atmosphere with PHP 7.1 and run a linter on the transpiled code. If any piece of code will not be appropriate with PHP 7.1 (resembling a typed property from PHP 7.4 that wasn’t transformed), then the linter will throw an error.

A linter for PHP that works properly is PHP Parallel Lint. We will set up this library as a dependency for development in our challenge, or have the CI course of set up it as a standalone Composer challenge:

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

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

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 information in 15.4 seconds
Syntax error present 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| )))  
Surprising ')' in layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php on line 55
Error: Course of accomplished with exit code 1.

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

  1. Try the supply code
  2. Have the atmosphere 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 software
  5. Swap the atmosphere’s PHP model to 7.1
  6. Run the linter on the transpiled code

This GitHub Motion workflow does the job:

identify: Downgrade PHP exams
jobs:
  principal:
    identify: Downgrade code to PHP 7.1 through Rector, and execute exams
    runs-on: ubuntu-latest
    steps:
      - identify: Checkout code
        makes use of: actions/checkout@v2

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

      - identify: Native packages - Downgrade PHP code through Rector
        run: |
          composer set up
          vendor/bin/rector course of

      # Put together for testing on PHP 7.1
      - identify: Set up PHP Parallel Lint
        run: composer create-project php-parallel-lint/php-parallel-lint --ansi

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

      # Lint the transpiled code
      - identify: 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

Please discover that a number of bootstrap80.php information from Symfony’s polyfill libraries (which needn’t be transpiled) should be excluded from the linter. These information comprise PHP 8.0, so the linter would throw errors when processing them. Nonetheless, excluding these information is protected since they are going to be loaded on manufacturing solely when operating PHP 8.0 or above:

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

Whether or not you are making a public plugin for WordPress otherwise you’re updating legacy code, there are numerous causes that utilizing the most recent PHP model could also be not possible Find out how transpiling will help on this information Click on to Tweet

Abstract

This text taught us tips on how to 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 through Rector, a PHP reconstructor software.

Transpiling our code makes us higher builders since we will higher catch bugs in development and produce code that’s naturally simpler to learn and perceive.

Transpiling additionally permits us to decouple our code with particular PHP necessities from the CMS. We will now accomplish that if we want to make use of the most recent model of PHP to create a publicly obtainable WordPress plugin or Drupal module with out severely proscribing our userbase.

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

The publish The Final Information for Transpiling PHP Code appeared first on Kinsta.