Contributed by
Nicolas Grekas
in #26499.

在 Symfony 3.4 中我们令 所有服务默认为私有,意味着你不能够再在控制器中调用 $this->get('my_service_id') 来快速获取某些服务。

做出如此改变,是因为直接使用服务容器被认为是一个不好的实践,因为它藏匿了你的类的依赖,对外部配置来说强化了耦合,更令测试变得困难,也难以读懂。

任何时候当我们删除类似功能时,提供一个备选方案被认为是较合理的,而且,如果可能,要像之前的一样简单。这就是为什么 controller 允许在action方法以及constructor中 利用type hints(类型提示)注入服务

"默认为private services" 的唯一欠点在于,测试比以前困难了。一些开发者甚至在 test 环境下定义了一些配置信息来让所有服务在测试时公有。在 Symfony 4.1 中我们做了同样的事,现在,默认是 测试时可以取出私有服务

实践中,基于 WebTestCaseKernelTestCase 的测试,现在可以通过 $client->getContainer()static::$container 属性访问到特殊的容器,它们允许取出 non-removed private services(不会被删除的私有服务):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Console\Tester\CommandTester;
 
class AddUserCommandTest extends WebTestCase
{
    private function assertUserCreated()
    {
        self::bootKernel();
 
        // returns the real and unchanged service container
        // 返回真正的未有变化的服务容器
        $container = self::$kernel->getContainer();
 
        // returns the special container that allows fetching private services
        // 返回特殊容器,其允许取出私有服务
        $client = static::createClient();
        $container = $client->getContainer();
        // alternative way to get the special container:
        // 另一个拿到特殊容器的方式
        // $container = self::$container;
 
        $user = self::$container->get('doctrine')->getRepository(User::class)->findOneByEmail('...');
        $this->assertTrue(self::$container->get('security.password_encoder')->isPasswordValid($user, '...');
        // ...
}