Skip to main content

Writing backward-compatible deprecation fixes for contributed modules will be much easier for Drupal 11

Published on

One of the major problems observed in getting contributed modules Drupal 10 compatible was maintaining support for Drupal 9.5, which remains in security support (or remained if it is now past November 1, 2023.) Contributed modules should be compatible with all security-supported versions of Drupal core or more if they so choose. This can be difficult as Drupal 9.5 contained deprecated code removed in Drupal 10. It was up to maintainers and contributors to find workarounds and copy-pasting tricks using if/else statements with version_compare.

If you're interested in these challenges, I recommend catching my talk from MidCamp this year – Lessons learned from helping port the top contrib projects to Drupal 10.

Making backward-compatible calls possible

Luckily, Drupal 10.1.3 released a new utility class that will make supporting multiple versions of Drupal core easier while addressing deprecated code. The new class \Drupal\Component\Utility\DeprecationHelper and its backwardsCompatibleCall method allow code to be executed conditionally based on the current Drupal core version (change record.)

For instance, the user_roles() function has been deprecated in Drupal 10.2.0. Here's an example of using the deprecated user_roles function and its replacement using the DeprecationHelper::backwardsCompatibleCall method. Note – the example uses named arguments for demonstration purposes and is not required.

$result = DeprecationHelper::backwardsCompatibleCall(
    currentVersion: \Drupal::VERSION,
    deprecatedVersion: '10.2',
    currentCallable: fn() => Role::loadMultiple(),
    deprecatedCallable: fn() => user_roles(),
);

Let's break down this code. DeprecationHelper::backwardsCompatibleCall has four arguments

  • currentVersion is the version to be checked, which is the version of Drupal from \Drupal::VERSION.
  • deprecatedVersion is the version that introduced the deprecated code path, which happened in the release of Drupal 10.2.0.
  • currentCallable is the callable to be invoked for the new code path.
  • deprecatedCallable is the callable to be invoked for the deprecated code path

The method uses PHP's version_compare function to determine if currentVersion is greater than or equal to deprecatedVersion.

I sincerely appreciate everyone's discussions and bikeshedding of this code. There was a lot of back and forth over naming and documentation, which is extremely important for a tool like this. It may only be a few lines of code, but we needed to nail the developer experience.

There is a caveat, however. Modules using DeprecationHelper must support a minimum version of 10.1.3 for Drupal core. With the release of Drupal 10.2.0 during the week of December 11, 2023, Drupal 10.0.x will no longer receive security support. That means modules can begin choosing to drop support for 10.0.x and bump their minimum supported version to 10.1.

Making drupal-rector backwards compatible

Earlier this year, I wrote about the need to add backward compatibility to automated code fixes provided through Rector. Björn Brala (bbrala) has made this a reality: https://github.com/palantirnet/drupal-rector/pull/250!

As the Project Update Bot delivers automatic code fixes to projects for Drupal 11 compatibility through Rector, we can use DeprecationHelper to ensure breaking changes are not introduced. This is a giant leap forward in innovation for Drupal as the community works to reduce manual upkeep and maintenance of Drupal sites.

I'm available for one-on-one consulting calls – click here to book a meeting with me 🗓️

#