Skip to main content

Updating Drupal to PHP 8

Almost 2 years ago PHP 7.4 reached its end-of-life, and in a little less than 2 months it will no longer have security support as well. Consequently, in recent months a large number of clients and providers have felt the need to upgrade their servers and/or applications to PHP 8 (or 8.1).

The following table shows that Drupal.org recommends keeping our applications in version 9.x, while using PHP version 8.0 as a minimum.

Image
Drupal PHP requirements

Drupal PHP requirements

We have decided to compile in this post the necessary steps to make our Drupal applications compatible with this PHP version, providing our experience for those who might need it.

Keep in mind that we are going to focus on updating a Composer project based on Metadrop Drupal boilerplate, but these steps can be applied to other Drupal-based Composers. Note that previous knowledge of Docker and Drupal is required to understand the more complex steps.

The steps

1. Update the local environment

Our development environments are based on Docker, so the first thing you have to do, which is fundamental, is to change the PHP image so that Docker can use the appropriate PHP 8 container.

In our case, we use the Metadrop boilerplate and this in turn uses the wodby/drupal-php image. All the tags are listed in Docker Hub, where we have to locate the one that matches the version we are going to have in our production environment.

As an example, we are looking for PHP version 8.1.9, the tag we get from Docker Hub is "8.1-dev-4.37.9".
You will need to edit the docker-compose.yml file or the one that defines the containers you are using in your project.

2. Change the platform requirements

The next step is to update the composer.json of the project to require PHP version 8 in the platform requirements section. By adding it, you are forcing Composer to operate using PHP 8.1 as the project platform. If you don't, Composer will use the PHP version of the environment.

    "config": {
        ...
        "platform": {
            "php": "8.1"
        },

Note: Not all packages have the PHP restriction, so some packages may need to be updated and tested manually.

3. Let Composer work its magic

One of the critical moments has arrived. Let's check that you’re environment and composer are well configured and that the packages can be obtained correctly.

To do this, inside the container, launch:

$ composer update

Accept plugins during the process. Composer will try to resolve the project requirements and update the packages based on that, so the environment should be as close to production as possible.

Direct dependencies will be updated, and dependencies that cannot be updated due to some restriction conflict in the Composer project will be shown. In these cases, it will be necessary to solve the issue manually according to the needs of each project.

Note: If you don't want to upgrade Composer packages but want to know which packages are not compatible or need to be upgraded, you can run the command with the dry-run parameter.

composer update --dry-run

4. Check that the code is compatible

For the code compatibility review, we have used PHP Compatibility, a PHP Code Sniffer plugin, which analyses the code in search of compatibility between versions. As it is a static code analyzer, it is not infallible because it does not execute the code, but it can give you more than interesting clues to avoid possible problems.

Note: The dev branch is required because the current stable version (9.3.5) has some problems that have already been solved and merged into the develop branch.

First, add it to Composer:

composer require "phpcompatibility/php-compatibility:dev-develop as 9.3.6" \
--dev

Next, run the Code Sniffer by specifying the folder to check.

We recommend doing this in custom modules and also in contrib modules. Note that it is very possible that the errors shown by the Code Sniffer have already been fixed by the community, so probably updating the module or library will be enough to fix them. To launch PHP Code Sniffer with this plugin, run the following:

./vendor/bin/phpcs -p <folder> --standard=PHPCompatibility

Don't pay attention to the JS or CSS file reports, won’t be any compatibility problem. If necessary, in the following more complex example, you can see how to ignore them:

./vendor/bin/phpcs -p web/modules/custom/ --standard=PHPCompatibility  \
 --runtime-set testVersion 8.0 --extensions=php,module,install,inc \
 --report-full=./tmp/drupal9-php8-compatibility.txt --ignore=*/tests/*

Analyzing them a little bit the parameters we can see that they are used for the following:

--runtime-set testVersion 8.0- : To run all the checks for PHP 8.0 and above.
--extensions=php,module,install,inc : To specific extensions files.
--report-full=<folder/file.txt> : To save the report to a file.
--ignore=*/tests/*” : To ignore specific folders.

5. Check that everything works properly

For the review process, we followed the workflow in our local environment:

  • Clear the error logs to start from scratch.
  • Run the automatic test batteries that you usually use.
  • Do also manual checks of the critical points.
  • Finally, check the logs (Watchdog for example) to see if any warning or error was introduced related to the upgrade to PHP 8.

It is very useful to have the logs report as clean, clear, and structured as possible to avoid missing hidden bugs.

After running the test suite (we use Behat) and doing the manual tests, some messages about deprecated functionality may appear in the report. If you have Watchdog activated, you can check the errors from the Drupal UI or directly from the database with the following query.

select count(*) count, message, variables, max(location)
from watchdog
where message like '%deprecated%'
or variables like '%deprecated%'
group by variables, message;

6. Update Drush if necessary

Drush version 10 has some problems with PHP 8, so it is advisable to work directly with version 11.

$ composer require drush/drush:^11

After updating Drush, when executing its commands, some modules may trigger a debug message, this is not critical, and the message will appear when launching commands with "verbose":

[debug] votingapi commands loaded even though its constraint (^9)
is incompatible with Drush 11.1.1.
Broaden the constraint in modules/contrib/votingapi/composer.json
(see 'extra\drush\services' section) to remove this message

As the message says, the commands work, but it is necessary to specify in the composer.json of the module the constraint for version 11. You can see an issue to solve in a module in Drush 11 Support.

7. Check that the project is compatible with previous PHP versions

This step is optional, but necessary if the servers will be on the previous version at the time of deployment.

A. First, fix Composer to the desired PHP version (e.g. PHP 7.4.3) as you did in step 2, and check the status with Drush as follows:

​$ drush status

B. If the following message appears, it means that some package is requiring PHP version 8 (if it does not appear, skip directly to point D).

Composer detected issues in your platform:

Your Composer dependencies require a PHP version ">= 8.1.0".
You are running 7.4.3.

To get a list of packages that require PHP 8, run the following command and analyze in which ones PHP 7 does not appear as compatible:

$ composer why php | egrep "requires php \([^0-9]*8\.\d*\)"

C. In this case, we have installed symfony/string in version 6 and it is requiring PHP 8.1. We would have to see which package symfony/string depends on and analyze if any of those versions that it allows us to install is compatible with PHP 7. Following the example, this would be the command and the conclusion:

$ composer why symfony/string
chi-teck/drupal-code-generator 2.5.3 requires symfony/string (^5.1 || ^6)

As you can see, chi-teck/drupal-code-generator has required the installation of symfony/string version 5.1 or 6. We can see manually the composer.json of symfony/string version 5.1 (the version before the one we have installed), and check if it is compatible with PHP 7. If it is compatible, you can downgrade the version without any problem:

$ composer require symfony/string:^5.1

Repeat the process for all packages that mark PHP 8 as a minimum requirement, as we said at the beginning, as long as you need your application to remain compatible with PHP 7.

D. Finally, remove the platform from the composer.json (added on point 2. Change the platform requirements) and execute:

$ composer update –lock

8. Deployment and conclusion

It's time to deploy. In this guide, we have explained how to upgrade our Drupal application to PHP 8 keeping backward compatibility with PHP 7. This will allow deploying to production without the need to synchronize with the update of the servers to PHP 8. If you have not been able or have not been required to maintain this backward compatibility, you must take into account that the server must be updated before deploying, otherwise, the application will probably not work (and you will have a complicated day).

As a final statement, this guide is generic, so use it as a reference and adapt it to the context of your project.

Alberto Ortega

Alberto Ortega

Junior Drupal developer
Eduardo Morales Alberti

Eduardo Morales

Senior Drupal developer