How do i migrate to php 8?

Change language:

Submit a Pull Request Report a Bug

Table of Contents

  • New Features
  • Backward Incompatible Changes
  • Deprecated Features
  • Other Changes

This new major version brings with it a number of new features and some incompatibilities that should be tested for before switching PHP versions in production environments.

See also the migration guides for PHP versions 7.0.x, 7.1.x, 7.2.x, 7.3.x. 7.4.x.

+add a note

User Contributed Notes

There are no user contributed notes for this page.

In this video we'll look at Rector, an opensource tool that significantly speeds up the migration process to PHP 8, or any other PHP version.

Links

  • Front Line PHP: the book that explains all PHP 8 features and modern PHP in depth
  • Rector on GitHub

This article is not about new features or comparative tests; first, we need to update the project to make it compatible with PHP 8.

Today, we are going to draw up a plan for the update and discuss the key potential difficulties based on the example of updating a large project from PHP 7.4 to 8.0. Most of the steps will also be helpful when planning an upgrade from earlier versions.

Why Now?

PHP 8 was released four months ago. At the time of writing, there are three-patch versions of PHP 8 that fix critical bugs. Besides, the most popular libraries and composer packages have finally added PHP 8 support. Although for many libraries, PHP 8 support involves changing the version in the composer, the officially confirmed compatibility of all dependencies the vendor folder is a blocker for the upgrade.

Prerequisites

This article is a retrospective of the PHP update process from 7.4 to 8.0 in the Oro Inc. monolithic repository with 11 web applications and 45 modules of a fairly large project based on Symfony 4.4 LTS, 3M + LoC, and more than 600 000 PHP classes, including tests but excluding vendor.

The numbers do not reflect the project’s complexity but indicate the amount of PHP code that was updated. I should also note that most of the code does not use strict_types=1, which technically complicates the update.

Major Changes

Aside from the new features, which in most cases have an alternative, the major release enables PHP language developers to break backward compatibility. It’s not all that bad though as there is no need to rewrite the majority of the project in order to upgrade.

Compatibility is broken only by removing features that were marked as deprecated in previous minor versions of PHP 7.0–7.4. To introduce improvements, PHP core developers can also change the behavior of functions that have not been previously documented or have been positioned as uncertain from the beginning.

Most changes have taken place in the field of strict comparisons and strict types of arguments. Although strict_types=1 did not become the default behavior, in version 8, a significant part of PHP’s built-in functions have received strict argument typing, sometimes without typecasting.

Upgrading PHP Extensions

If you use third-party PHP extensions, update them first. Although for most projects this shouldn’t be a big problem, it may block further upgrade.

Determining Which Composer Packages Require Upgrade

Spoiler alert: dependencies update has been the most complex stage of the upgrade.

PHP has many ready-made solutions, so it is hard to find an application without third-party dependencies. In most cases, the composer is used to manage them. This tool allows you to determine the dependency on packages and specify the so-called platform dependencies of each package, which includes the PHP version and a list of required PHP extensions.

The first thing that comes to mind is to specify a new PHP version in the root composer.json but don’t rush just yet as this will trigger an unpleasant chain of error corrections next time you run the composer update. It’s best to ask the composer directly if any libraries may interfere with the upgrade. To do this, go to the root of the project and run the following command:

composer why-not php 8

If you use popular packages and update the dependencies to the latest versions in time, you’ll see this message in a minute or so:

Most will see a “complete” list of dependencies that need to be updated to support PHP 8. Complete is in inverted commas here because there are always a few libraries that set very weak platform restrictions from the outset or did not specify them altogether, which means they are not actually compatible with PHP 8. If the project has good test coverage, it will play an important role in the future and help to identify such packages. If not, let’s try and be optimistic.

Next, with a complete list of dependencies that don’t support PHP 8 in the current versions, navigate to packagist.org and check if there are any versions of packages that support the new version of the programming language. An application with OroCommerce and OroCRM has about 300 composer packages in dependencies, and so far they have all received compatible updates. In some cases, like with friendsofsymfony/rest-bundle, you don’t even need to update the package to the latest version that contains many backward incompatibilities.

Contribution to Open-Source

If you have found a few packages that trail behind, no need to worry. The PHP community is quite active. In most cases, upgrading to PHP 8 does not require significant investments. First, check the master branch to see if support has already been added and will be included in the next release. Then, check the list of issues and pull requests for PHP 8 mentions. In most cases, the reasons for delayed updates and workarounds will already be identified at this stage.

However, if the library is not widely used and previous methods produced no results, you can always contribute to the open-source. In our case, it was enough to fix tests in symfony/acl-bundle and write a comment for an issue in nelmio/security-bundle. This allowed us to reduce the list by two points. Being very active, the developers of these libraries managed to release a new version in just a few hours after a minimum contribution. This doesn’t always happen so quickly. In some cases, we had to wait a few weeks for a new release. For most projects, this is not a big problem. If the library is no longer supported, it makes sense to think about forks or alternatives; maybe a quality fork has already been created for you.

Composer Packages Upgrade

Updating composer packages is probably the longest stage for most projects because, along with the new PHP version’s support, active extension developers often release a major version and break backward compatibility. But the good news is that most changes are described in detail in the release notes or changelog in the root of the repository. Compile the lists of changes, and that’s the majority of your upgrade scope.

Upgrade Time Estimation

Depending on how the dependency is used [for example, whether only the interfaces are used or there are numerous overrides], the upgrade effort of one package may differ. It can often be identified by reading the list of changes and performing a quick search on the code by namespaces, classes, and names of functions that have changed. This makes it possible to obtain an approximate estimate of the update time promptly.

Updating Dependencies

It’s important to note that for the most part, the new dependency version with support for PHP 8 still supports PHP 7.4, so the update can be done gradually, package by package.

If you have successfully finished the composer update and reached this stage, congratulations! The vendor folder, which usually contains most of the PHP code in the project, is theoretically compatible. We can go further and update the code directly.

Hacks for Incompatible Dependencies

If you need to update now to test the incompatible library’s compatibility before updating it to PHP 8, here are a few tricks to do this:

  • Tell composer that you are using PHP 7.4 but run the code on PHP 8:
"platform": {"php": "7.4"}

Since almost all third-party libraries work simultaneously with both PHP 8 and 7.4, you can obtain updates and, at the same time, successfully complete the composer update with incompatible platform-level dependency requirements. Composer ignores the real version of PHP if a project-level platform config is specified.

  • Redefine incompatible classes. If DiC is used, then it’s usually quite simple. Decorate, or even completely override, the original class through the configuration of the container. Even if the class in the vendor is declared as final, it can always be overridden at the level of autoload.classmap in composer.json, for example, like this. Of course, it’s messy, but as Marco Pivetta said, a hack should look like a hack.

Updating Your Code

It’s not time for new features just yet as you first need to fix what has broken. Here, automatic tests will come in handy. With their help, you can install the project on a new version, run all the tests, and estimate the amount of further work in a matter of a few hours. For example, at Oro, most of the functionality is covered by Behat, functional, and unit tests. Just to give you an idea about the number of tests, the total test time is more than 30 hours. This allows us to see a complete and accurate picture immediately after updating the vendor and fixing application installation issues.

Configuration Before Testing

If your fellow developers have been diligent and already use strict typing, the update should run smoothly; however, you may encounter potential problems even then. That’s why it’s important to test the project, automatically or manually, and draw up a list of what has broken, writing out exceptions from logs if there are any. Testing makes sense with strict error messages enabled in php.ini:

error_reporting = E_ALL

Of course, this is providing that the project previously worked in this mode without errors. Otherwise, in combination with display_errors=off, it will help you see the log of errors and warnings without breaking the pages’ structure. If the log isn’t long, this list will prove to be quite useful.

Automatic Error Detection

Thanks to AST by Nikita Popov, there are quite a few static analyzers in PHP that can show problem areas and sometimes even update the code automatically.

  • Phpstan and Psalm are the most popular tools to help maintain strict typing. But if they haven’t been used in the project before, no need to hurry, as they will find a lot of issues. Not all of them, however, will require an immediate fix to upgrade to PHP 8.
  • Rector will help to “rewrite” the code using syntactic sugar improvements, but it will not show any urgent problems.
  • From the “Five Minutes PHP” podcast, I learned about a rather interesting PHPCompatibility plugin for PHP_CodeSniffer, which was supposed to show the problems. Unfortunately, what it actuallydid was only show a few false positives in our project, so I can’t recommend it. The plugin does not check real compatibility; instead, it checks compatibility according to the documentation that doesn’t always correspond to reality. For example, it showed that we can’t use “string” in namespaces starting with version 7.0, even though it still works without errors.

PhpStorm turned out to be by far the most useful. By default, IntelliJ IDEA validates only open files, but it has a feature to analyze the entire project. To do so, the code that you write must be perfect from the PhpStorm perspective; otherwise, it may find numerous errors after a full scan. Here, we can launch one particular inspection.

When you see an error, copy its name, select the folder of the code in the file tree, select Code> Run inspection by name in the PhpStorm menu, enter the name of the inspection. Very quickly, you’ll get the result for the whole project. For example, when updating third-party libraries, the “Class Hierarchy Checks Inspection” will show the method signatures that need to be fixed in the code after updating the vendor.

Changes to keep in mind

If there are no automatic tests on the project or the coverage is incomplete, you need to pay attention to the sorting and comparing functions.

Sorting

The behavior of sort functions [usort,…] has changed. First, the comparison function used in sorting should now return -1, 0, or 1.

True and false still work but trigger a deprecation notice. The spaceship operator `` will be helpful here. But most importantly, sorting of the elements with the same weight now leaves the order of the elements unchanged, which is different from the behavior in PHP 7.4. Suppose the project is large enough and has a modular structure like at Oro. In that case, problems may arise in extensions that are implemented using a chain of responsibilities, decorators, or a simple registry with a sorted list of add-ons. In our case, some extensions that depended on the order of execution were broken, because the sorting priority was not clearly specified. For example, the order of columns and rows in some tables changed, and in other places, we received an exception.

usort[$exportFiles, function [File $a, File $b] {
- return $b->getMtime[] > $a->getMtime[];
+ return $b->getMtime[] $a->getMtime[];
}];

A hack to save sorting, like in PHP 7:

$comparisonClosure = function [ExtensionVisitorInterface $a, ExtensionVisitorInterface $b] {
if [$a->getPriority[] === $b->getPriority[]] {
- return 0;
+ return -1;
}
return $a->getPriority[] > $b->getPriority[] ? -1 : 1;
}

In the figure below, only the last two Extensions have explicit priority. Here, pay attention to item 4:

Non-Strict Comparisons

Non-strict comparisons between numbers and non-numeric strings now work by casting the number to a string and comparing the strings. We had problems with the first two examples:

╔═══════════════╦════════╦═══════╗
║ Comparison ║ Before ║ Now ║
╠═══════════════╬════════╬═══════╣
║ 0 == "foo" ║ true ║ false ║
║ 0 == "" ║ true ║ false ║
║ 42 == "42foo" ║ true ║ false ║
╚═══════════════╩════════╩═══════╝

Strict Comparisons

These problems are difficult to find in the code, but it’s better to know about them in advance.

As mentioned above, some built-in PHP functions switched to strictly typed arguments, and third-party libraries have supported the same trend. If strict typing isn’t used in the code, everything will still work with typecasting in most cases. But there are exceptions; some PHP functions throw an error or trigger a deprecation warning.

For example, round function does not accept a string argument that does not match int|float signature. String “40.5” will work but “40.5” will not, or vice versa, depending on the locale:

- $amount = round[$row[$attributeName], 2];
+ $amount = round[[float]$row[$attributeName], 2];

str_replace with declare[strict_types=1] no longer accepts integer as a second argument because it expects an array|string:

- $alias = str_replace[WebsiteIdPlaceholder::NAME, $websiteId, $alias];
+ $alias = str_replace[WebsiteIdPlaceholder::NAME, [string]$websiteId, $alias];

But there are also less obvious cases where behavior changes silently. In our case, php-amqplib stopped connecting to RabbitMQ because we passed an empty array to the function when null was expected. In the previous version, with a loose comparison, it worked completely differently.

- $this->channel->wait[[], false, $timeout];
+ $this->channel->wait[null, false, $timeout];

Named Arguments

We experienced problems from this rather contradictory feature even before we started using it.

For example, now you can’t safely pass an associative array with an argument to call_user_func_array:

- $data = call_user_func_array['array_merge', $indexData];
+ $data = array_merge[...array_values[$indexData]];

Error Instead of Return False

Here are some examples of changes to our code:

get_parent_class:

- $parentClassName = get_parent_class[$className];
+ $parentClassName = class_exists[$className] ? get_parent_class[$className] : false;

method_exists:

- } elseif [method_exists[$domainObject, 'getId']] {
+ } elseif [null !== $domainObject && method_exists[$domainObject, 'getId']] {

Or actually catching an error:

- return new \NumberFormatter[$locale, $style]]->format[$value];
+ try {
+ return new \NumberFormatter[$locale, $style]]->format[$value];
+ } catch [\TypeError $error] {
+ return $value;
+ }

These are just the compulsory changes that were required when upgrading one repository with 3,000,000,000 lines of PHP code to version 8.0, which are little described in the documentation. You can find a complete list in the official update guide.

Results

So how many lines of code did we have to update in Oro repositories to support PHP 8? :]

As you can see, the update was quite simple and generally helped to find code that wasn’t of the best quality. Since the goal was to support PHP 8.0 without using new features, and the developers tried to maintain backward compatibility, the pull request turned out to be relatively small: 86 files with +316 and 257 modified lines. This is without taking into account changes in composer.json and composer.lock.

The pull request is small, primarily because we completed most of the vendor’s update three months ago as part of the LTS release preparation. The upgrade process, excluding reviews, took about 40 hours across the entire monolithic repository, which is relatively little by the experience of previous technology updates at Oro. Of course, the estimate will vary depending on the amount and content of the code, but since most PHP projects are mostly high-level, you shouldn’t face many more problems than we did.

What’s next?

Now you can use all the new features to improve the code’s readability — at least in the new code. At most, if you’re not worried about losing git blame, you can use them across the whole project, thanks to the automation provided by Rector and similar tools.

Turn on JIT, cross fingers, and launch ApacheBench.

And wait for PHP 8.1 [which will finally include enums], the revolution from fibers, and when all these checks of types that work in runtime in PHP will slow down the application less.

Conclusion

Based on the practical experience of upgrading a large codebase to PHP 8, we see that:

  • The open-source community actively monitors PHP releases and quite quickly adds support for new versions of PHP to their libraries;
  • Adhering to strict discipline by library authors, listing only those versions of PHP that are actually supported in composer.json greatly helps developers who use these libraries in their projects thanks to the built-in dependency analyzer of the composer;
  • PHPStorm is not only an excellent IDE for writing code but also provides static code analysis tools to help you migrate your codebase to newer versions of PHP;
  • High coverage by automated tests dramatically simplifies the migration process;
  • Upgrading to new major versions of PHP is not as difficult as it may seem at first glance.

Enjoy the upgrade!

Should I migrate to PHP 8?

Should I upgrade to PHP 8 ? A: PHP 8 provides improved code execution performance and will expand and improve over time. Better comparisons will get rid of frequent bugs and unexpected behaviors that often plague PHP developers. Resulting Increased Speed and optimized performance.

How do I change my PHP version?

Changing the PHP version.
Navigate to the Manage Websites page..
Click the Manage button to the right of your domain..
Under the PHP section, click the Modify icon..
Choose a version of PHP from the dropdown menu..
Scroll down and click the Change PHP Version button to save your changes..

How do I upgrade to PHP 8 on Windows?

PHP 8 Installation Steps on Windows 10.
The first step is to install C++ redistributable for Visual Studio 2015 or 2016 and 2019 o our system. ... .
Now download the PHP 8 installer zip file on your system..
Extract the zip file on your system..
Rename the extracted folder to PHP8..

Is PHP 8.0 good for WordPress?

Can WordPress use PHP 8? Yes, WordPress can use PHP 8, and it is recommended to use PHP 8.0 with WordPress 5.6 or higher version for compatibility and better performance. If you are using an older WordPress version, you can test your site with WordPress 5.6 in a staging environment.

Chủ Đề