Altering existing services, providing dynamic services
This documentation needs review. See "Help improve this page" in the sidebar.
There are several advantages of the service container. Since each service is accessed / instantiated using a single string key and has a defined interface, it can be swapped out with a different implementation. To modify existing services, implement a class extending ServiceProviderBase and the alter() method. If you want to extend existing services to add functionality, you may also consider decorating the service.
Drupal 10 and 11 note: Drupal 10 runs on Symfony 6.x and Drupal 11 runs on Symfony 7.x. When you alter services, you are working with a compiled Symfony container, so Symfony concepts apply: aliases (including interface aliases), service decoration, tags, compiler passes, and container optimisation.
Note that if you want this service alteration to be recognized automatically, the name of this class is required to be a PascalCase version of your module's machine name followed by ServiceProvider, it is required to be in your module's top-level namespace Drupal\your_module, and it must implement \Drupal\Core\DependencyInjection\ServiceModifierInterface (which ServiceProviderBase does).
Swapping (replacing) a service is powerful, but it is also the least compatible approach. If multiple modules replace the same service, the winner depends on container build order and cannot be relied upon. For Drupal 10 and 11, prefer extension points first (events, hooks, plugins), and prefer service decoration when you need to wrap or extend behaviour. Replace a service only when you fully control the behaviour and can maintain it across core updates.
For example, define my_module/src/MyModuleServiceProvider.php for a module named my_module:
namespace Drupal\my_module;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
// @note: You only need Reference, if you want to change service arguments.
use Symfony\Component\DependencyInjection\Reference;
/**
* Modifies the language manager service.
*/
class MyModuleServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
// Overrides language_manager class to test domain language negotiation.
// Adds entity_type.manager service as an additional argument.
// Note: it's safest to use hasDefinition() first, because getDefinition() will
// throw an exception if the given service doesn't exist.
if ($container->hasDefinition('language_manager')) {
$definition = $container->getDefinition('language_manager');
// Use your module’s class that implements the expected contract.
$definition->setClass('Drupal\my_module\MyLanguageManager')
->addArgument(new Reference('entity_type.manager'));
}
}
}
Recommendation: If your goal is to add behaviour around an existing service (for example logging, feature flags, fallbacks), prefer decorating the service rather than replacing its class. Decoration keeps the original service available and is less likely to break across Drupal 10/11 minor releases.
Other examples of where service swapping may be useful is to swap out the string translation service (the one behind t()). A common feature request that Drupal core does not provide is to have regional language variants or informal/formal languages, where the translation differences may be minimal. By swapping the string translation service, this is easy to resolve in a contributed module.
Finally, it is also possible to implement register() to add compiler passes or register services programmatically. This should be rare in Drupal 10 and 11, because it makes service definitions harder to audit. Remember this runs during container build and affects the cached compiled container, not per request.
Avoid overriding core services via module_name.services.yml
You can override a service definition in YAML, but overriding core services by changing the class is typically brittle in Drupal 10 and 11. Constructor signatures and internal behaviour can change across minor releases. Prefer service decoration or other extension points unless you fully own the replacement and test it continuously.
The YAML snippet below is shown only to illustrate syntax, not as a recommended pattern for core services.
language_manager:
class: Drupal\module_name\Service\MyLanguageManager
arguments: ['@language.default']Custom compiler pass
It's useful to add a custom compiler pass to extend Drupal services properly or propose an extension point.
A compiler pass is an extension point to the container. With it, you can alter service definition to override or add functionality to existing services. You can plug your module on existing tagged services and/or your module can offer an extension point (tagged service) to others community modules.
During the compilation, declaration files are parsed, parameters and configuration are resolved, an "alias" is created and tagged services are processed.
Tip: Aliases are often used to map an interface to the concrete service that implements it. This is especially important when you want other modules to depend on an interface while still allowing the implementation to be swapped or decorated.
Most of the time, you won't need to create a compiler pass but it's important to understand how they work.
How Drupal compiles its container?
First, Drupal is instantiated inside a "front controller" (index.php most of the time). The DrupalKernel class is instantiated and the method handle() is called.
Here is the (simple) workflow made to bootstrap a Drupal application with a focus on DIC (Dependency Injection Container):
- Instantiation of DrupalKernel
- Call handle() method to "handle" the current request
- Drupal boots its environment with the call to static::bootEnvironment()
- Settings are initialized with the call to initializeSettings()
- The boot() method is called
- The container is initialized with the call to initializeContainer(). Drupal launches the compilation of the container with the call to compileContainer()
- Services definitions are declared and autodiscovered (initializeServiceProviders() -> discoverServiceProviders()). Inside the discoverServiceProviders(), services are loaded from Yaml files and PHP files. If your module defines the "MyModuleServiceProvider.php" file, you can manipulate container definition and add your own compiler pass into the register() method
- Container is compiled (call to compile())
- Synthetics services are attached (attachSynthetic()). More information about synthetic services on Symfony doc.
- The response is returned
- \o/
Some steps, like registering error handler, exception handler, and detect sites ... are voluntarily omitted.
How to create a custom compiler pass?
- Create a file with "ServiceProvider" suffix into the name, for example, MyModuleServiceProvider.php
- You must implement the register() method which is provided by ServiceProviderInterface or extend the ServiceProviderBase class.
- Add your own compiler pass!
namespace Drupal\my_module;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Drupal\my_module\Compiler\MyCustomExtensionPass;
class MyModuleServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
$container->addCompilerPass(new MyCustomExtensionPass());
}
}
namespace Drupal\my_module\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class MyCustomExtensionPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
... your work
}
}
You can learn more about creating your own compiler passes from the Symfony doc.
Creating Separate Compiler Passes
3rd party resources:
Using Symfony Service Decorators in Drupal 8 by Mike Potter @ Phase2
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