One of the features I like when working with multiple inheriting services in a Symfony project is that it allows a developer to define parent and derived services. That means that two services can share the service configuration definition inside the Symfony Dependency Injection Container (aka DIC).
Instead of copying the same configuration lines for each related service, a developer can define a parent service and then just inherit the configuration in the services extending the parent. A benefit of this technique is that it makes cleaner and easier to manage service definitions.
The Problem
In a recent project I was working on I found myself in need to override the arguments of an abstract parent service definition. Although the Symfony documentation is informative and up-to-date with all the framework’s features, the only proposed solution I was able to find for this scenario was by modifying the service configuration using PHP code. Of course, there was the possibility of writing a CompilerPass and changing the arguments but that meant writing a lot of unnecessary code for my case.
So, I started to wonder if we could achieve the same using only YAML or XML definitions. Looks like it’s possible.
Going under the hood
One thing that really stuck with me along the years is that whenever I need a good, clean solution for a programming problem I need to get my hands “dirty” and get deeper into the code.
After a bit of debugging through the DIC component’s internals I found that the DefinitionDecorator::replaceArgument()
method is called from the ResolveDefinitionTemplatesPass::resolveDefinition()
method if the arguments in the inheriting service definition have a key starting with index_
.
In simple terms, whenever we need to override the argument of a parent service simply add a key starting with ‘index_’ and the index number of the argument. Keep in mind that both Yaml and XML “translate” to PHP code, which means that the arguments index will start at 0 (zero).
The downside of this solution is that it is limited to __constructor()
dependency injection. If you need to replace a setter or property injected dependency, you will have to use their documented solutions.
Quick example
Below you can find an example configuration for both Yaml as well as XML for overriding the abstract service ‘mail_manager’ second argument’s definition:
Conclusion
If you need to quickly override a parent service arguments definition, then this solution might help you accomplish that task faster. Using this technique, we remove the need for creating compiler passes. It provides a cleaner and easier solution to managing different dependencies in similar or inheriting services.
Article written by Denis Rendler