Nicolas Grekas
Contributed by Nicolas Grekas in #24238 and #24104

In Symfony applications, services and aliases are public by default. This means that when you have the container at hand, you can easily get services, like in controllers that extend the Symfony base controller ($this->get('app.my_service')) or when injecting the service_container into your classes ($container->get('app.my_service')).

As easy as this seams to be, using the container directly is not considered a good practice because it hides the dependencies of your classes, making them coupled to external configuration, thus harder to test, harder to review, etc.

Starting with Symfony 3.3, we added new dependency injection primitives that had the potential to completely replace the cases where injecting the service container was required (e.g. to achieve laziness, or break some circular references).

Slowly but steadily, we're now ready to move away from having any need to inject the container in user-land classes. That's why in Symfony 3.4 services and aliases will be private by default. This means that even if you manage to have the container, you will not be able to get() them anymore by default. Instead, you should use regular dependency injection.

If you use autowiring in your application, you won't have to make many changes because you are already injecting the services instead of getting them via their public ID. That works for regular classes, but also for controllers and commands, since they are services themselves by default now.

Alternatively, you can update your application to mark services as public. You'll then need to be explicit about it. If you use YAML to configure services:

1
2
3
4
5
6
7
services:
    # this makes public all the services defined in this file
    _defaults: { public: true }

    # you can also make public individual services
    App\Manager\UserManager:
        public: true

If you use XML to configure services:

1
2
3
4
5
6
7
<services>
    <!-- this makes public all the services defined in this file -->
    <defaults public="true" />

    <!-- you can also make public individual services -->
    <service id="App\Manager\UserManager" public="true"></service>
</services>

If you use PHP, add the ->setPublic(true) call to the appropriate service definition.

This change will also impact the third-party bundles used in your projects. If your favorite bundle uses the container internally, please send them pull requests to fix that: starting with 3.4, there is almost never a reason to play with the container anymore.

In Symfony core we've already done that and we made all services and aliases private, except a few selected ones, that are required at bootstrap time. In fact, bootstrapping is the last and only legitimate use case for using the container directly.

So, should we deprecate the possibility to inject the service_container entirely alongside with ContainerAware*? That's a possibility that the community might consider when preparing Symfony 5.

Published in #Living on the edge