Upgrade your custom modules to be Drupal 10 and PHP 8.1 compliant

Guidance on how to upgrade your custom modules to be Drupal 10 and PHP 8.1 compliant.

The following sections provide examples of the most common updates that you might need to make. However, you must check the deprecation lists for Drupal and PHP to ensure that you cover all of the scenarios in your Developer Portal sites.

Notable changes the update brings

The following items are some of the key changes in Drupal 10.

  • CKEditor4 is removed and CKEditor5 is now the default text editor.

  • Claro is now the default theme for the Admin UI, which gives it a modern and refreshing look.

  • Ensure that none of your custom modules depend on them as they will not be there after the upgrade.

    The following modules are uninstalled as they do not support Drupal 10:
    • ckeditor_media_embed

    • console

    • flood_unblock

    • message

    • message_notify

    • message_subscribe

    • video_embed_field

    • video_embed_html5

    • content_browser

    • entity_browser

    • entity_browser_enhanced

    • entity_embed

    • file_browser

    • editor_file

    • color

    • Unlimited_number

    • embed

    • ckeditor

    • quickedit

  • The Drupal 10 upgrade comes with a major Symfony upgrade version. Drupal 9 used Symfony 4.4, and Drupal 10 now uses Symfony 6, so if you are using any Symfony ensure that they have not been deprecated. Symfony provides a guide at the following link https://symfony.com/doc/current/setup/upgrade_major.html.

  • The default Drush version is now Drush 11. If you are writing Drush commands in your custom modules, you must ensure they are Drush 11 compatible. The following links provide examples on how you can achieve compatibility, https://www.specbee.com/blogs/writing-your-own-custom-drush-9-and-10-commands and https://www.drush.org/11.x/commands/.

PHP 8.1 deprecations

The following items are some of the key PHP 8.1 deprecations.

Passing null to non-nullable internal function parameters is deprecated
There are multiple scenarios that require a fix with the possibility to pass a null to some PHP functions.
The following example is the most common PHP deprecation.
Before:
$redacted_url = $url;
if (strlen($redacted_url) > 0 && strpos($redacted_url, '/') !== 0) {
After:
$redacted_url = $url ?? '';
if (strlen($redacted_url) > 0 && strpos($redacted_url, '/') !== 0) {
Optional parameters specified before the required parameters
Some functions in code have optional parameters before the required ones. However, in PHP 8.1, the required parameters come after the optional ones.
Improved type safety
PHP 8.1 improves the type of safety, which can result in numerous warnings that might require changing functions and variable declarations.
The following example addresses the improved safety and the optional parameters before required parameters deprecations.
Before:
protected $id;

public function oldFunction($url = '', $data) {
  return $id;
}
After:

/**
 * Drupal entity ID
 *
 * @var string
 */
protected $id;

public function newFunction(someObject $data, string $url = ''): string {
  return $id;
}

Drupal 10 deprecations

The following items are some of the key PHP 8.1 deprecations.

Entity queries now require an accessCheck
If you have custom modules with entities, you must do an accessCheck. For more information about access checking, see https://www.drupal.org/node/3201242.
For example,
Before:

$query = \Drupal::entityQuery('node');
$query->condition('type', 'api');
$query->condition('status', 1);
$nids = $query->execute();
After:

$query = \Drupal::entityQuery('node');
$query->condition('type', 'api');
$query->condition('status', 1);
$nids = $query->accessCheck()->execute();
The Once function is removed from the jQuery library and added to its own core library
If you have any modules that use the oncefunction in your JavaScript files, you need an update to pull in the new library. For more information about how to remove jQuery dependency, see https://www.drupal.org/node/3158256.
For more examples on how you might upgrade your code, see https://www.drupal.org/project/drupal/issues/3183149.
For example,
Before:

// ibm_apim.libraries.yml 
validate_password:
  version: 1.x

// validate_password.js
(function($, Drupal, drupalSettings) {
  var $passwordInput = $(context).find('input.js-password-field').once('ibmApimValidatePassword');
})(jQuery, Drupal, drupalSettings);
After:

// ibm_apim.libraries.yml 
validate_password:
  version: 1.x
  dependencies:
    - core/once

// validate_password.js
(function($, Drupal, drupalSettings, once) {
  var $passwordInput = $(once('ibmApimValidatePassword', context)).find('input.js-password-field');
})(jQuery, Drupal, drupalSettings, once);
The app.root and site.path services that are converted into container parameters
If you use the app.root and site.path services in your custom modules, you must ensure they are converted into container parameters.
For example,
Before:
 \Drupal::service('config.factory')
    ->getEditable('locale.settings')
    ->set('translation.use_source', 'local')
    ->set('translation.path', \Drupal::service('site.path') . '/translations')
    ->save();
After:
\Drupal::service('config.factory')
    ->getEditable('locale.settings')
    ->set('translation.use_source', 'local')
    ->set('translation.path', \Drupal::getContainer()->getParameter('site.path') . '/translations')
    ->save();
The parameters of some Formsare updated, so be aware if you are extending them.
If you are extending your Forms, note the parameters of some of them have changed. For more information about the updated parameters, see https://www.drupal.org/node/3251987.

Next steps

This topic covers the key upgrades you might need when upgrading your custom modules to be Drupal 10 and PHP 8.1 compliant. However, note that it is your responsibility to check the deprecation lists for Drupal and PHP to ensure that you cover all of the scenarios in your Developer Portal sites.

For more information about making your custom modules Drupal 10 and PHP 8.1 compliant, see the following links: