symfony: [DependencyInjection] Overriding services autowired by name under _defaults bind not working

Symfony version(s) affected: 4.1.4

Description
Autowiring allows to bind certain services by name in the _defaults section, under bind in services.yaml. As follow:

services:
    _defaults:
        autowire: true
        bind:
            $isDebug: false

It is common to override base service definitions in the services_test.yaml file for example. So I would have expected to be able to override these default bind too. Like:

services:
    _defaults:
        autowire: true
        bind:
            $isDebug: true

But if you do so, then the container binding pass will throw the following exception: Unused binding "$isDebug" in service "App\Tests\Service\MySuperServiceMock"..

Possible Solution
I tracked it down and this happens because the two definitions of $isDebug are considered as distinct bindings. Consequently ResolveBindingPass::processValue() will bind the first definition, and the second one remains unbound.
The solution would be to make the second definition override the first one earlier on in the process, so there is only one binding when getting to ResolveBindingPass.

Workaround
My current workaround is to only define the binding in the main service file and make it point to a service alias; so that services_test.yaml can override that alias. As follow:

services:
    _defaults:
        autowire: true
        bind:
            $superService: '@app.service.super_service'

    app.services.super_service:
        alias: 'App/Service/SuperService'

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 25 (24 by maintainers)

Commits related to this issue

Most upvoted comments

defaults are per-file. So any default defined in services_test.yaml will only affect services defined in services_test.yaml.

If what you want is to change the debug flag injected in your normal services, a solution is to use a DI parameter in your binding, and changing the value of the parameter in test (parameters are global in the container):

# services.yaml
parameters:
    app.debug: false

services:
    _defaults:
        autowire: true
        bind:
            $isDebug: '%app.debug%'
# services_test.yaml
parameters:
    app.debug: true

implemented in #29597 if you want to have a look.