On this page
- Introduction
- Testing
- How to deprecate
- Format of the deprecation message
- @deprecated PHPdoc tag format
- @trigger_error() format
- Definitions
- What can be deprecated
- Methods
- Method parameters
- Constructor parameter additions
- Constructor parameter removals
- Injected service properties
- Interface and base/abstract class method signature changes
- Step 1: Prepare the new signature
- Step 2: Implement the new signature
- Procedural functions
- Return values (especially array data structures)
- Services
- Hooks
- Code paths & behavior
- Classes
- Abstract classes, interfaces, traits, and classes with a private constructor
- Concrete, instantiated classes
- Plugins
- Files containing procedural functions
- Unintended behavior and security issues
- Deprecating internal APIs
- Asset Libraries
- JavaScript
- Settings used by Drupal core
- Configuration schema
- Theme templates
- Modules and themes
Drupal deprecation policy
Introduction
We deprecate code to keep the backwards compatibility promise and to provide a continuous upgrade path.
This document explains how to deprecate code and what can be deprecated.
Test classes are not deprecated. Any test that could be considered deprecated should be removed or modified to be relevant.
Testing
Tests which exercise deprecated code can be annotated with @group legacy
in order to avoid deprecation notices during testing, but a follow-up issue should be filed in order to fix such tests to not exercise deprecated code.
How to deprecate
A deprecation consists of three parts:
- A
@deprecated
PHPdoc tag that indicates when the code was deprecated, when it will be removed, and what to use instead, and an @see link to the change record for the change. - A
@trigger_error('...', E_USER_DEPRECATED)
at runtime to notify developers that deprecated code is being used. The@
suppression should be used in most cases so that we can customize the error handling and avoid flooding logs on production. In some cases, we will omit the@
if it is important to notify developers of a behavior or BC break (e.g. for a critical issue). - A unit test proving the deprecation notice will be triggered when the deprecated code is called. Use a
@group legacy
annotation in conjunction with calls to
$this->expectDeprecation()
Format of the deprecation message
@deprecated PHPdoc tag format
@deprecated in %deprecation-version% and is removed from %removal-version%. %extra-info%.
@see %cr-link%
@trigger_error() format
When the trigger_error() call is associated with a matching @deprecated doc tag, the format of the text is the same as the @deprecated tag:
%thing% is deprecated in %deprecation-version% and is removed from %removal-version%. %extra-info%. See %cr-link%
Where there is no associated @deprecated doc tag, the format is more relaxed to allow flexibilty in wording:
%thing% is deprecated in %deprecation-version% (free text describing what will happen) %removal-version%. %extra-info%(optional). See %cr-link%
Definitions
- %thing%
- What is being deprecated - for example, the class name, method name, function name, service name or the use or optional status of a parameter
- %deprecation-version%
- The version string representing when the change occurred.
- For Drupal core and contrib projects that use semantic versioning, the version string is:
project:major.minor.patch
orproject:major.minor.patch-tag[n]
- For contrib projects that use legacy core-compatibility-prefixed versioning, the version string is:
project:8.x-minor.patch
orproject:8.x-minor.patch-tag[n]
- For Drupal core and contrib projects that use semantic versioning, the version string is:
- %removal-version%
- The version string representing when the deprecated code path will be removed.
- %extra-info%
- This is free text. Useful things to include are hints on how to correct the code, what replacement to use, etc.
- %cr-link%
- The link to the change record on drupal.org (for core deprecations) or the relevant issue.
What can be deprecated
Methods
Add @trigger_error('...', E_USER_DEPRECATED)
at the top of the method. Add @deprecated
to the docblock for the method. For example:
/**
* Checks if a string is safe to output.
*
* @param string|\Drupal\Component\Render\MarkupInterface $string
* The content to be checked.
* @param string $strategy
* (optional) This value is ignored.
*
* @return bool
* TRUE if the string has been marked secure, FALSE otherwise.
*
* @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
* Instead, you should just check if a variable is an instance of
* \Drupal\Component\Render\MarkupInterface.
*
* @see https://www.drupal.org/node/2549395
*/
public static function isSafe($string, $strategy = 'html') {
@trigger_error('SafeMarkup::isSafe() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, you should just check if a variable is an instance of \Drupal\Component\Render\MarkupInterface. See https://www.drupal.org/node/2549395', E_USER_DEPRECATED);
return $string instanceof MarkupInterface;
}
Method parameters
Add @trigger_error('...', E_USER_DEPRECATED)
at the start of the code block that handles the parameter. Add an (deprecated)
phpdoc parameter and detail how it is deprecated. Add an @see to the related change record to the method. For example:
/**
* Sets a service.
*
* @param string $id
* The service identifier.
* @param object $service
* The service instance.
* @param string $scope
* (deprecated) The scope of the service. The $scope parameter is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
*
* @see https://www.drupal.org/node/2549395
*/
public function set($id, $service, $scope = ContainerInterface::SCOPE_CONTAINER) {
if (!in_array($scope, array('container', 'request')) || ('request' === $scope && 'request' !== $id)) {
@trigger_error('The concept of container scopes is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Omit the third parameter. See https://www.drupal.org/node/2549395', E_USER_DEPRECATED);
}
$this->services[$id] = $service;
}
Constructor parameter additions
If there is an acceptable default value for the parameter (for example, for injecting a new service): Add new parameters with a default value of NULL. At the top of the constructor, check whether the parameter is NULL. If it is, set a default value and raise the @trigger_error('...', E_USER_DEPRECATED)
. For example:
/**
* Constructs a new thingamajig.
*
* @param FooInterface|null $foo_service
* The Foo service.
*/
public function __construct(FooInterface $foo_service = NULL) {
if ($foo_service === NULL) {
@trigger_error('Calling ' . __METHOD__ . ' without the $foo_service argument is deprecated in drupal:9.1.0 and it will be required in drupal:10.0.0. See https://www.drupal.org/node/3078162', E_USER_DEPRECATED);
$foo_service = \Drupal::service('namespace.foo_service');
}
$this->fooService = $foo_service;
}
}
Constructor property promotion usage example:
/**
* Constructs a new thingamajig.
*
* @param FooInterface|null $fooService
* The Foo service.
*/
public function __construct(protected ?FooInterface $fooService = NULL) {
if ($this->fooService === NULL) {
@trigger_error('Calling ' . __METHOD__ . ' without the $fooService argument is deprecated in drupal:10.1.0 and it will be required in drupal:11.0.0. See https://www.drupal.org/node/3078162', E_USER_DEPRECATED);
$this->fooService = \Drupal::service('namespace.foo_service');
}
}
}
Constructor parameter removals
For the parameters following the one being removed use a union type to declare both types. Use instanceof to determine if the deprecated parameter is passed and, if so, use func_get_arg() to initialize the parameters and raise the @trigger_error('...', E_USER_DEPRECATED). For example:
Before
/**
* Constructs a new thingamajig.
*
* @see https://www.drupal.org/node/3078162
*/
public function __construct(FooManager $foo, BarManager $bar, BazManager $baz) {
$this->fooManager = $foo;
$this->barManager = $bar;
$this->bazManager = $baz;
}
After
/**
* Constructs a new thingamajig.
*
* @param \Drupal\Core\Foo\FooManager $foo
* @param \Drupal\Core\Baz\BazManager|\Drupal\Core\Bar\BarManager $baz
*
* @see https://www.drupal.org/node/3078162
*/
public function __construct(FooManager $foo, BazManager|BarManager $baz) {
$this->fooManager = $foo;
$this->bazManager = $baz;
if ($baz instanceof BarInterface) {
$this->bazManager = func_get_arg(2);
@trigger_error('Calling ' . __CLASS__ . '::_construct() with the $bar argument is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. See https://www.drupal.org/node/3078162', E_USER_DEPRECATED);
}
}
Injected service properties
First deprecate the constructor parameters as outlined above. Then use \Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait
and define the removed properties and their original service ID in a new $deprecatedProperties
property.
For example:
Before
class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
/**
* The inbound path processor.
*
* @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface|null
*/
protected $pathProcessor;
After
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
use DeprecatedServicePropertyTrait;
/**
* Defines deprecated injected properties.
*
* @var array
*/
protected array $deprecatedProperties = [
'pathProcessor' => 'path_processor_manager',
];
Interface and base/abstract class method signature changes
Beginning with Drupal 10, interface and base/abstract class arguments and signatures may be changed in a major release, using Symfony's DebugClassLoader
.
Since it's normally a BC break to introduce new arguments (because any implementing class that does not comply with the changed signature would fail at loading), this is a two-step process.
This pattern is applicable on all argument changes. For example, changing the argument order of a method.
Step 1: Prepare the new signature
-
During the current major release cycle, introduce the argument change(s) in an inline comment. For example:
- public function foo($bar); + public function foo($bar /* , BazInterface $baz */);
Or, change the typehint of an argument. For example:
- public function foo(string $bar); + public function foo(/* string|Stringable */$bar);
-
Document the changes in the docblock of the method, inside a
phpcs:disable
block to prevent PHPCS from firing an error when parsing the signature of the method. Include an@see
to a followup issue that will implement the change in the next major release:* phpcs:disable Drupal.Commenting * @todo Uncomment new method parameters before drupal:11.0.0. * @see https://www.drupal.org/project/drupal/issues/3354672 * * @param BazInterface $baz * Documentation for parameter $baz. * phpcs:enable
-
Tag the follow-up issue with 'Major version only'.
This will allow the testing framework to trigger deprecation errors for the implementing classes that do not have the new signature in place yet. Since PHP allows methods to add additional non-interface arguments without errors, classes can immediately update. In order to prevent tests failures because of that, add an ignore line in
.deprecation-ignore.txt
. For example:# Drupal 11. %Foo::foo\(\).* will require a new "BazInterface \$baz" argument in the next major version of its interface% %Bar::bar\(\).* will require a new "BazInterface \$baz" argument in the next major version of its interface%
Step 2: Implement the new signature
Once the new major branch branch opens for development:
-
Remove the inline comment from the interface, exposing the new full signature:
- public function foo($bar /* , BazInterface $baz */); + public function foo($bar, BazInterface $baz);
or
- public function foo(/* string|Stringable $bar */); + public function foo(string|Stringable $bar);
- Remove the PHPCS ignore and
@todo
from the docblock. - Implement the new signature in the concrete classes.
- Remove the ignore line from the
.deprecation-ignore.txt
file.
Procedural functions
Add @trigger_error('...', E_USER_DEPRECATED)
at the top of the function. Add @deprecated
to the docblock for the function. For example:
/**
* Deletes old cached CSS files.
*
* @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
* Use \Drupal\Core\Asset\AssetCollectionOptimizerInterface::deleteAll().
*
* @see https://www.drupal.org/node/2317841
*/
function drupal_clear_css_cache() {
@trigger_error('drupal_clear_css_cache() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Asset\AssetCollectionOptimizerInterface::deleteAll(). See https://www.drupal.org/node/2317841', E_USER_DEPRECATED);
\Drupal::service('asset.css.collection_optimizer')->deleteAll();
}
Return values (especially array data structures)
Generally a return value of method should not changed. However if the method returns an array it might be permissible to add new values and deprecate existing values. In this instance, it is not possible to use @deprecated
because the method is not being deprecated, or @trigger_error()
because you cannot determine if the deprecated key is used. Therefore it is important that the return value is fully documented and the array keys are listed and those that are deprecated are clearly listed. The method should also have an @see to the relevant change record. For example:
* @return array
* An array of typed data IDs keyed by corresponding relation URI. The keys
* are:
* - 'entity_type_id'
* - 'bundle'
* - 'field_name'
* - 'entity_type' (deprecated)
* The values for 'entity_type_id', 'bundle' and 'field_name' are strings.
* The 'entity_type' key exists for backwards compatibility and its value is
* the full entity type object. The 'entity_type' key is removed from Drupal 9.
*
* @see https://www.drupal.org/node/2877608
(From \Drupal\hal\LinkManager\RelationLinkManager::getRelations())
Also because we want to be able to find the deprecated code to remove before Drupal 9 you need to add an @todo to #2716163: [META] Remove deprecated classes, methods, procedural functions and code paths outside of deprecated modules on the Drupal 9 branch. For example:
// @todo https://www.drupal.org/node/2716163 Remove this in Drupal 9.0.
foreach ($data as $relation_uri => $ids) {
$data[$relation_uri]['entity_type'] = $this->entityManager->getDefinition($ids['entity_type_id']);
}
Services
Add a deprecation key and message to the service definition.
router.matcher.final_matcher:
class: Drupal\Core\Routing\UrlMatcher
arguments: ['@path.current']
deprecated: The "%service_id%" service is deprecated. You should use the 'router.no_access_checks' service instead. See https://www.drupal.org/node/2317841
Further reading: https://symfony.com/blog/new-in-symfony-2-8-deprecated-service-definitions
Hooks
Mark the hook as @deprecated
in the *.api.php file that documents it.
Convert the invocation of the hook to use ModuleHandlerInterface::invokeDeprecated()
or invokeAllDeprecated()
instead of invoke()
or invokeAll()
. Alter hooks should call alterDeprecated()
. These methods on ModuleHandler
will then call @trigger_error()
if the installation has any implementations of that hook.
Add change records for deprecated hooks explaining how to accomplish use-cases without the hook.
Code paths & behavior
For example when we have a code path handling a deprecated key in a YAML file.
Add @trigger_error('...', E_USER_DEPRECATED)
at the start of the code block that handles the backwards compatibility layer. DO NOT add an @deprecated
phpdoc annotation to the method or function docblock that contains the code path as this causes IDEs to mark the entire method as deprecated. For example:
// @todo
Classes
Abstract classes, interfaces, traits, and classes with a private constructor
Add @trigger_error('...', E_USER_DEPRECATED)
under the namespace declaration. Add an @deprecated
and @see
phpdoc annotations to the class docblock. For example:
<?php
namespace Drupal\datetime\Tests\Views;
@trigger_error('The ' . __NAMESPACE__ . '\DateTimeHandlerTestBase is deprecated in drupal:8.4.0 and is removed from drupal:9.0.0. Instead, use \Drupal\Tests\BrowserTestBase. See https://www.drupal.org/node/the-change-notice-nid', E_USER_DEPRECATED);
/**
* Base class for testing datetime handlers.
*
* @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0. Use
* \Drupal\Tests\BrowserTestBase.
*
* @see https://www.drupal.org/node/the-change-notice-nid
*/
abstract class DateTimeHandlerTestBase extends HandlerTestBase {
Concrete, instantiated classes
Add @trigger_error('...', E_USER_DEPRECATED)
to the constructor. If there is no constructor, add one and ensure it calls the parent constructor. Add an @deprecated
and @see
phpdoc annotations to the class docblock. For example:
<?php
namespace Drupal\taxonomy;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityViewBuilder;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Theme\Registry;
/**
* View builder handler for taxonomy terms.
*
* @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0. Use
* \Drupal\Core\Entity\EntityViewBuilder instead.
*
* @see https://www.drupal.org/node/2924233
*/
class TermViewBuilder extends EntityViewBuilder {
/**
* {@inheritdoc}
*/
public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, Registry $theme_registry = NULL) {
@trigger_error(__NAMESPACE__ . '\TermViewBuilder is deprecated in drupal:8.4.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Entity\EntityViewBuilder instead. See https://www.drupal.org/node/2924233', E_USER_DEPRECATED);
parent::__construct($entity_type, $entity_manager, $language_manager, $theme_registry);
}
}
Plugins
Add @trigger_error('...', E_USER_DEPRECATED)
to the plugin constructor. If the plugin does not have a constructor, add one. Add no_ui = true
to the plugin definition. If the plugin does not support no_ui but is selectable in a UI then an issue will be needed to add it. Also, add an @deprecated
and @see
phpdoc annotations to the plugin's class docblock. For example:
<?php
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
/**
* Plugin implementation of the 'timestamp' formatter as time ago.
*
* @FieldFormatter(
* id = "timestamp_ago",
* label = @Translation("Time ago (deprecated)"),
* field_types = {
* "timestamp",
* "created",
* "changed",
* },
* no_ui = true
* )
*
* @deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use the
* \Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampFormatter formatter
* instead and configure it with "Display as 'time ago'" option.
*
* @see https://www.drupal.org/node/2926275
*/
class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
/**
* Constructs a TimestampAgoFormatter object.
* ... rest of the docs ...
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatterInterface $date_formatter, Request $request) {
@trigger_error('\Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampAgoFormatter is deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampFormatter instead. See https://www.drupal.org/node/2926275', E_USER_DEPRECATED);
// ... constructor code...
}
Files containing procedural functions
Add @trigger_error('...', E_USER_DEPRECATED)
after the @file
docblock. Add an @deprecated
phpdoc annotation to the file docblock. For example:
/**
* @file
* Miscellaneous functions.
*
* @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0.
* See each function in the file for individual deprecation notices with
* upgrade instructions.
*
* @see https://www.drupal.org/node/12345678
*/
@trigger_error(__FILE__ . ' is deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. See individual methods in the file for individual deprecation notices with upgrade instructions. See https://www.drupal.org/node/12345678', E_USER_DEPRECATED);
Unintended behavior and security issues
For cases where using the previous API could result in unintended behavior or security issues, we may deliberately break backwards compatibility. Omit error suppression in such cases, so that developers can find and fix code that may no longer work after the BC break. For example:
elseif (strpos($string, $key) !== FALSE) {
trigger_error("Fallthrough for unrecognized placeholders to %variable is deprecated in drupal:8.2.0 and is removed from drupal:8.2.0. Invalid placeholder ($key) in string: $string. See https://www.drupal.org/node/2605274', E_USER_DEPRECATED);
}
In the code above from #2807705: FormattableMarkup::placeholderFormat() can result in unsafe replacements we removed the ability to replace placeholders that started with an alpha character. The replacement was done without sanitization and opened possible security issues, so rather than maintain backwards compatibility, we stopped doing the replacement altogether. Therefore, we trigger an error without suppression so contributed projects, custom code, and real sites are alerted immediately to the problem and could fix any errant placeholders.
Deprecating internal APIs
Anything considered internal API according to the backwards compatibility policy does not strictly require BC. However, as a best practice, we will still deprecate internal APIs first to reduce disruption and make the process easier to understand.
To make it clear that deprecated internal APIs are still internal code without full BC support, the deprecation notice should have the following format:
baz() is deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. Use \Drupal\Foo\Bar::baz() instead. As internal API, baz() may also be removed in a minor release.
Note that it is not required to have a change record linked in the message, since internal API changes do not always have a change record. (Some internal API changes do have change records if they are significant enough, and if so, the link to the change record should be included in the normal fashion.)
The patch that adds the deprecation should also ensure that the code is explicitly marked as @internal
in the PHP docblock.
Backwards compatibility layers for @internal code also generally do not require test coverage for the deprecated code path.
Asset Libraries
Drupal's *.libraries.yml
may now add a deprecated
key to an asset library to indicate that the asset library is deprecated and will be removed in the next major version. This key should contain the deprecation information in the standard format, and may use the token %library_id%
as a placeholder for the library name. The LibraryDiscovery
service will then trigger an error if this library is accessed.
Tests of the deprecation message are not required when using the 'deprecated' key.
For example:
jquery.ui.effects.scale:
version: *jquery_ui_version
license: *jquery_ui_license
js:
assets/vendor/jquery.ui/ui/effects/effect-scale-min.js: { minified: true }
dependencies:
- core/jquery.ui.effects.core
deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Require jQuery UI as an explicit dependency or create a pure JavaScript solution. See https://www.drupal.org/node/3064015
JavaScript
A @deprecated
JSDoc tag that indicates when the code was deprecated, when it will be removed, and what to use instead, and an @see link to the change record for the change.
The JSDoc text format should be formatted like this:
@deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use Drupal.theme.fooBar() instead.
@see https://www.drupal.org/node/the-change-notice-nid
To trigger JavaScript deprecation errors from arbitary code, use Drupal.deprecationError()
:
Drupal.theme.div = function ($elements) {
Drupal.deprecationError({
message: 'The Drupal.theme.div is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/2575199.'
});
return $('<div></div>');
};
To trigger deprecation errors for deprecated properties, use Drupal.deprecatedProperty()
. This notifies at runtime developers that deprecated code is being used.
Drupal.deprecatedProperty({
target: { some_property: 'value', someProperty: 'value' },
deprecatedProperty: 'property',
message: 'The some_property property has been deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use someProperty instead.',
});
JavaScript deprecation errors can be found in the browser console. However, JavaScript deprecation errors are suppressed by default. The suppression can be disabled by adding the following code to a module:
function hook_js_settings_alter(&$settings) {
$settings['suppressDeprecationErrors'] = FALSE;
}
JavaScript deprecation errors can be also tracked in automated tests. JavaScript deprecation errors will be translated into PHP @trigger_error
calls on WebDriver based tests.
On Nightwatch based tests, it is recommended to assert that the test scenario didn't run into deprecated code paths by adding the following assertion to the end of each test scenario:
browser.assert.noDeprecationErrors();
We follow the same general best practices for JavaScript deprecations as we do for PHP, namely: public APIs should always provide BC and a deprecation. Internal APIs do not require BC and deprecations, but it's recommended to provide them unless there are substantial costs to doing so (in terms of maintainability, performance, etc.).
Settings used by Drupal core
As of Drupal core 9.1.0 Core settings keys can be deprecated. Settings are managed by the \Drupal\Core\Site\Settings
class (see core/lib/Drupal/Core/Site/Settings.php).
To do this, add the information to the Settings::$deprecatedSettings
array. The information is keyed by the deprecated setting name. The value is an array with 2 keys:
replacement
- The new setting name.
message
- The deprecation message triggered when the deprecated setting is configured in settings.php and when
Settings::get()
is used to get the value for the deprecated setting.
For example:
private static $deprecatedSettings = [
'old_setting' => [
'replacement' => 'new_setting',
'message' => 'The "old_setting" setting is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use "new_setting" instead. See https://www.drupal.org/node/CR-NID',
],
];
If the settings.php
file for a site (or any file it includes) contains the legacy setting name (continuing the example above, 'old_setting'), the deprecation warning will be generated. If code calls Settings::get('new_setting')
but 'new_setting' is not yet defined, the value of 'old_setting' will be returned.
If any code calls Settings::get()
with the legacy name, the deprecation warning will also be generated. If the replacement setting is already defined, Settings::get('old_setting')
will return the value of new_setting
.
Only once the legacy setting has been removed from settings.php
and all calls to Settings::get('old_setting')
have been removed will a site stop generating these deprecation warnings.
Since settings are used very early in a Drupal request (e.g. before Drupal can connect to a database, build a service container, load modules, etc.), there's no way for contributed extensions to utilize the same mechanism to deprecate settings they introduce. Contributed extensions need to deprecate settings in the code that uses the setting (the spot where the extension calls Settings::get()
to retrieve a value). See Code paths & behavior above for more.
Configuration schema
Add a deprecated
property in the deprecated config schema entry. The value should be the deprecation message. For example:
complex_structure:
type: mapping
label: Complex
deprecated: "The 'complex_structure' config schema is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use the 'complex' config schema instead. See http://drupal.org/node/the-change-notice-nid."
mapping:
key:
type: ...
...
Theme templates
Add an item with deprecated
key in the deprecated hook_theme
entry. The value should be the deprecation message. For example:
/**
* Implements hook_theme().
*/
function node_theme() {
return [
'old_template' => [
'variables' => ['content' => NULL],
'deprecated' => 'The "old_template" template is deprecated in drupal:10.2.3 and is removed from drupal:11.0.0. Use "new_template" instead. See https://www.drupal.org/node/123456',
],
];
}
Modules and themes
Modules and themes in Drupal core can sometimes be deprecated in their entirety, in order for the module to removed from core in the next release.
There are broadly two routes for a module to be removed:
- The functionality of the module is incorporated in to other core modules or the base system.
- The module in its entirety is moved to a contributed project
In the first case, when a module is incorporated into other core modules, any APIs it provides should be individually deprecated. The module can then be marked obsolete
and an update added to uninstall it. Once a module has been marked obsolete, it can then be removed in the next major release, where the update to uninstall it must be removed (incrementing hook_update_last_removed() to ensure it is run before the module is uninstalled).
An example of this is when the field type provided by entity_reference module was moved to a core field type, leaving the module empty and safe to uninstall.
Follow the steps in Remove a core module by incorporating it other core module to complete the process.
In the second case, when a module or theme is to be moved to a contributed project, the following steps should be taken, this may not be an exhaustive list since each module is different:
- Discuss module removal
- Open an issue against the Drupal core ideas providing justification for removing the module.
- Seek approval from product managers.
- There must be someone willing to maintain the module or theme in contrib (at least to co-ordinate security releases while it is still in a supported core version), and a contributed project should be made with the same namespace
- When the above issue is marked Fixed and the decision is to remove the module from core, then follow the steps in Remove a core module and move it to a contributed project to complete the process.
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion