The end-of-life (EOL) for PHP 7.4 was Monday, November 28, 2022. If you’re like me, that date snuck up much faster than anticipated. While your PHP 7.4 code isn’t going to immediately stop working, you do need to begin making plans for the future of this codebase.
What are your options?
You could continue to remain on PHP 7.4, but there are several benefits to updating. The biggest are security risk and support. As we move farther and farther away from the EOL date, attackers will turn their focus to PHP 7.4 knowing that any vulnerabilities they discover will go unpatched in the majority of systems. Staying on PHP 7.4 drastically increases the risk of your site being compromised in the future. In a similar vein, finding support for issues you encounter with PHP 7.4 will become increasingly more difficult. In addition, you will most likely begin to encounter compatibility issues with third-party code/packages as they update their code to be compatible with later versions and drop support for 7.4. You’ll also be missing out on significant speed and performance improvements introduced in 8.0 and further improved in 8.1. But upgrading all that legacy code is daunting!
Where to start?
Luckily, PHP provides an official migration guide from PHP 7.4 to 8.0 to get you started (and an 8.0 to 8.1 migration guide as well). Be sure to read through the Backward Incompatible Changes and Deprecated Features sections. While these guides are incredibly handy, you may very well have tens of thousands of lines of code to check, some of which you may have inherited. Luckily there are some options to help pinpoint potential problem areas in the migration.
PHPCodeSniffer + PHPCompatibility sniffs
PHPCodeSniffer (PCS) is a package for syntax checking of PHP Code. It checks your code against a collection of defined rules (aka “sniffs”) referred to as “standards”. PHPCodeSniffer ships with a collection of standards you can use including PEAR, PSR1, PSR2, PSR12, Squiz, and Zend. Luckily, you can write your own collection of sniffs to define any set of rules you like.
PHPCompability has entered the chat
PHPCompatibility “is a set of sniffs for PHP CodeSniffer that checks for PHP cross-version compatibility” allowing you to test your codebase for compatibility with different versions of PHP, including PHP 8.0 and 8.1. This means you can use PHPCodeSniffer to scan your codebase, applying the rules from PHPCompability to sniff out any incompatibilities with PHP 8.1 that might be present.
Before I continue…
While PHP8.2 was released on December 8, 2022, and I encourage you to begin looking over the official 8.1 to 8.2 migration guide and begin making plans to upgrade, most of the checkers I mention in this article have not completed full support for 8.2 at this time. For those reasons, I’ll be focusing on migrating the code to PHP8.1, and not 8.2.
In the process of writing this article, I discovered PHPCompatiblity has a known issue when checking for compatibility with PHP 8.0/8.1 where it will report issues that should be Errors as Warnings. The only workaround for now is to use the
develop branch for PHPCompatibility instead of
master. While they state it is stable, please be aware that in this article, I’m using the non-stable branch. You may want to weigh the pros and cons of using the
develop branch before implementing it anywhere else than in a local development environment. While I found PCS+PHPCompatibility to be the most straightforward and comprehensive solution for checking for incompatible code, if you do not want to use a non-stable version of PCS, see the section at the end of the article about alternative options.
For the purposes of this article, I’ll be using the 1.4.6 version of SimpleSAMLphp to test for incompatibilities. This is a six-year-old version of the code base. I do this not to pick on SimpleSAMLphp, but because I wanted something that would definitely have some errors. As it turns out, all of the platform.sh code I tested, as well as my own code was already compatible with PHP8.1 and required no changes.
To get started, first clone your codebase, and then create a new branch. You’ll now need to decide if you want to install the dependencies and run the scans on your local machine or in a local development environment using something like DDEV, Lando, or Docksal. In this demo, I’m using DDEV. I suggest using a local development environment vs running directly on your local machine because while it’s not required to use the version of PHP you want to test against, for the best results, it is recommended you do so. If you don’t have PHP installed, or don’t have the target version installed, a local development environment allows you to create an ephemeral environment with exactly what you need without changing your machine.
After setting up your environment for PHP 8.1, at a terminal prompt (in my case, I’ve run
ddev start and once the containers are available, shell into the web app using
ddev ssh), you need to add these new packages so you use them to test with. I’ll be adding them with composer, however, there are multiple ways to install them if you would prefer to do so differently. If your codebase isn’t already using composer, you’ll need to do composer init before continuing.
Because you'll be using the develop branch of PHPCompatibility there are a couple of extra steps to do that aren’t in the regular installation instructions. First is that the develop branch of PHPCompatibility requires an alpha version of
phpcsstandards/phpcsutils. Because it is marked as alpha, you'll need to let composer know this one package is OK to install even though it is below your minimum stability requirements.
$ composer require --dev phpcsstandards/phpcsutils:"^1.0@dev"
Next, install PHPCompatibility targeting the
$ composer require --dev phpcompatibility/php-compatibility:dev-develop
develop branch also installs
dealerdirect/phpcodesniffer-composer-installer so you don’t need to add it manually or direct PCS to this new standard.
To verify our new standards are installed, you'll have PCS display the standards it is aware of.
$ phpcs -i The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend, PHPCompatibility, PHPCS23Utils and PHPCSUtils
Now that you know your standards are available, you can have PCS scan our code. To instruct PCS to use a specific standard, use the
--standard option and tell it to use
PHPCompatibility. However, you also need to tell PHPCompatibility which PHP version you want to test against. For that, use PCS’
--runtime-set option and pass it the key
testVersion and value of
Before you start the scan, the one issue remaining is that code you want to scan is in the root of the project (
.) but the
vendor directly is also in the project root. You don’t want the code in
vendor scanned, as those aren’t packages you necessarily control. PCS allows you to tell it to not scan files/directories with the
--ignore option. Finally, you want to see the progress as PCS parses the file so you'll pass in the
Putting it all together:
$ phpcs -p . --standard=PHPCompatibility --runtime-set testVersion 8.1 --ignore=*/vendor/*
This kicks off PCS which will output its progress as it scans through your project’s code.
W indicates Warnings, and
E indicates Errors. At the end of the scan it will output: a full report with the file containing the issue, the line number where the issue occurs, whether the issue is a Warning or an Error, and the specific issue discovered.
In general, Errors are things that will cause a fatal error in PHP 8.1 and will need to be fixed before you can migrate. Warnings can be things that have been deprecated in 8.0/8.1 but not yet removed or issues that PCS ran into while trying to parse the file.
Given that the report might be long, and is output all at once into your terminal, there are numerous options for changing the information that is included in the report, as well as multiple reporting formats.
As you begin to fix your code, you can rerun the report as many times as needed. However, at some point, you’ll need to test the code on an actual PHP8.1 environment with real data. If you’re using Platform.sh, which is as easy as creating a branch, changing a single line in your configuration file, and pushing that branch to us. You can check out this video to see how easy it is!
There’s too much to fix!
Now that you have a solid idea of what needs to be updated before you can migrate, you might be facing an incredible amount of work ahead of you. Luckily, you have some options to help you out. PCS ships with a code fixer called PHP Code Beautifier and Fixer (
phpcbf). Running phpcbf is almost identical to running phpcs and most of the options are identical. The other option is Rector. Usage of these tools is beyond the scope of this article, but as with any automation, you’ll want to test and verify before promoting changes to production.
If for any reason you don’t feel comfortable using a non-stable version of PCS, you do have other options for checking your code.
Phan is a static code analyzer for PHP. It offers multiple levels of analysis and allows for incrementally strengthening that analysis.
“Static analysis needs to be introduced slowly if you want to avoid your team losing their minds.”
Phan doesn’t target just compatibility with newer versions, it can highlight areas of code that will error in later versions. However, there are some caveats when using Phan for checking compatibility:
- Slower than PCS+PHPCompatibility.
- Phan requires the ast php extension which is not available by default on Platform.sh (or in DDEV). You’ll need to install it in your local development environment and add it to your php.ini file. Alternatively, you can use the
--allow-polyfill-parseroption, but it is considerably slower.
- Phan’s default reporting output isn’t as easy to read as other options
- I came across an issue where if your code base sets a different
vendordirectory via composer’s
[config:vendor-dir](https://getcomposer.org/doc/06-config.md#vendor-dir)option, it will error out stating it can’t find certain files in the
- As mentioned, Phan analyzes much more than just PHP8.1 compatibility. While certainly a strength in other situations, if your goal is to migrate from 7.4 to 8.1 as quickly as possible, you will have to parse through errors that are unrelated to version compatibility.
- Requires you run it on the PHP version you want to target
Similar to Phan, PHPStan is a static code analyzer for PHP that promises to “find bugs without writing tests.” And a similar set of caveats apply:
- Slower than either PCS or Phan
- Analyzes much more than just PHP8.1 compatibility so depending on your current codebase, you will have to possibly parse through a bunch of errors that are unrelated to version compatibility
- Requires you run it on the PHP version you want to target
A very fast PHP linter that can lint your codebase for issues, but can also check for deprecations. While it is exceptionally fast, it is only a linter, and therefore can only surface deprecations that are thrown at compile time, not at runtime. In my example code, it only found 2 deprecations vs the 960 deprecations PCS uncovered.
Code migrations, while never fun, are crucial to minimizing organizational risk. Platform.sh gives you the flexibility to test your code using the same data and configurations as your production site, but in a siloed environment. Combine this with the tools above, and you have everything you need for a strong, efficient code migration.
This article originally published on the Platform.sh community site and has been republished with permission.