PHP/Symfony:为什么 TwigExtension 注入的服务实例与 Controller 不同?
PHP/Symfony: Why does a TwigExtension get a different service instance injected than a Controller?
版本:
- Symfony 版本:4.2
- Twig 版本:2.6.2
背景
- 我正在将现有的 PHP 应用程序从相当旧的 symfony 版本 (2.x) 升级到 4.2。
怎么回事?
我有一个 shared 服务,它被注入并在我的 Controller 中使用。
还有一个 TwigExtension,它注入了相同的服务并期望完全相同的服务实例。
- Why does the TwigExtension rely on the service to being the same instance anyway?
- The service contains specific data which the TwigExtension processes. Basically the service seems to be currently used as kind of a request specific global data container or data collector.
- At this point you might argue that this sounds like a questionable practice and ask why I am actually doing something like this.
- I am dealing with an existing application, with existing functionality depending on the described behaviour.
- I'd be happy to get the application working in a first step, and change suspicious workarounds in a second one.
预期行为:
- 我希望共享服务在每个请求中准确实例化一次,因此我也希望 TwigExtension 接收与控制器完全相同的实例。
实际行为:
- TwigExtension 收到一个不同的、新实例化的服务实例。
旧版本中的行为(Symfony 2.x):
- TwigExtension 接收到完全相同的服务实例。
我尝试了什么:
- 我尝试了构造函数注入以及通过在 TwigExtension
中调用 $container->get('service')
来获取服务
- 我用 public 试过了:true/false
- 我在其他地方尝试过,比如事件侦听器,按预期使用相同的服务实例。
我的问题:
- 为什么 TwigExtension 注入的服务实例与 Controller 不同?
- 为什么 TwigExtensions 和事件侦听器之间存在不同的行为?
- 上述预期行为有哪些一般例外情况?
- 你能给我指点任何有用的文档吗? (当然我搜索了很多,并在 symfony 网站上阅读了相应的文档,但也许我错过了什么?)
- 你能推荐一种不同的方法来实现类似的目标吗?
我不知道为什么你没有相同的共享服务实例。根据 Symfony 文档,如果它在同一个请求中,您应该拥有相同的服务实例。
In the service container, all services are shared by default. This
means that each time you retrieve the service, you'll get the same
instance.
如果您找不到解决此问题的方法,您有两种存储数据的方法。 Session 用于简单格式,数据库用于更复杂和长期存储。
原来是为同一个class定义了两个服务。
在 services.yml 中手动定义了一项服务,服务 ID 为 'service'。在 Symfony 2.x.
时代,手动定义服务是定义服务的唯一方法
然后 autowire 出现了,基本上用它们的 class 名称的 id 定义了服务。我们能够使用 "bin/console debug:container" 验证这确实是问题所在。这是升级到 Symfony 3.4+ 并启用自动装配的结果。
除了创建服务,autowire 还允许您通过针对 class 名称的类型提示来注入服务。如果在容器中找到匹配的服务 ID,则注入该服务。因此,在这种情况下,twig 扩展可能更改为使用类型提示,而控制器使用 $container->get 导致注入两种不同的服务。或者反之亦然。
一个解决方法是使用别名:
# services.yml
MyServiceClassName: service
别名基本上会抑制第二个服务的自动生成。
一种 "better" 方法(或至少更推荐的方法)是停止使用 $container->get 并只在需要的地方注入服务。然后,您将完全删除 'service' 定义。
最后一点,如果服务本身需要任何缩放器构造函数参数(字符串或整数),那么自动装配过程将失败并显示错误消息。这些错误最初可能非常令人困惑,因为您已经定义了一个工作服务,但由于服务 ID 不同,自动装配只是向前推进并尝试创建一个新服务。
版本:
- Symfony 版本:4.2
- Twig 版本:2.6.2
背景
- 我正在将现有的 PHP 应用程序从相当旧的 symfony 版本 (2.x) 升级到 4.2。
怎么回事?
我有一个 shared 服务,它被注入并在我的 Controller 中使用。
还有一个 TwigExtension,它注入了相同的服务并期望完全相同的服务实例。
- Why does the TwigExtension rely on the service to being the same instance anyway?
- The service contains specific data which the TwigExtension processes. Basically the service seems to be currently used as kind of a request specific global data container or data collector.
- At this point you might argue that this sounds like a questionable practice and ask why I am actually doing something like this.
- I am dealing with an existing application, with existing functionality depending on the described behaviour.
- I'd be happy to get the application working in a first step, and change suspicious workarounds in a second one.
预期行为:
- 我希望共享服务在每个请求中准确实例化一次,因此我也希望 TwigExtension 接收与控制器完全相同的实例。
实际行为:
- TwigExtension 收到一个不同的、新实例化的服务实例。
旧版本中的行为(Symfony 2.x):
- TwigExtension 接收到完全相同的服务实例。
我尝试了什么:
- 我尝试了构造函数注入以及通过在 TwigExtension 中调用
- 我用 public 试过了:true/false
- 我在其他地方尝试过,比如事件侦听器,按预期使用相同的服务实例。
$container->get('service')
来获取服务
我的问题:
- 为什么 TwigExtension 注入的服务实例与 Controller 不同?
- 为什么 TwigExtensions 和事件侦听器之间存在不同的行为?
- 上述预期行为有哪些一般例外情况?
- 你能给我指点任何有用的文档吗? (当然我搜索了很多,并在 symfony 网站上阅读了相应的文档,但也许我错过了什么?)
- 你能推荐一种不同的方法来实现类似的目标吗?
我不知道为什么你没有相同的共享服务实例。根据 Symfony 文档,如果它在同一个请求中,您应该拥有相同的服务实例。
In the service container, all services are shared by default. This means that each time you retrieve the service, you'll get the same instance.
如果您找不到解决此问题的方法,您有两种存储数据的方法。 Session 用于简单格式,数据库用于更复杂和长期存储。
原来是为同一个class定义了两个服务。
在 services.yml 中手动定义了一项服务,服务 ID 为 'service'。在 Symfony 2.x.
时代,手动定义服务是定义服务的唯一方法然后 autowire 出现了,基本上用它们的 class 名称的 id 定义了服务。我们能够使用 "bin/console debug:container" 验证这确实是问题所在。这是升级到 Symfony 3.4+ 并启用自动装配的结果。
除了创建服务,autowire 还允许您通过针对 class 名称的类型提示来注入服务。如果在容器中找到匹配的服务 ID,则注入该服务。因此,在这种情况下,twig 扩展可能更改为使用类型提示,而控制器使用 $container->get 导致注入两种不同的服务。或者反之亦然。
一个解决方法是使用别名:
# services.yml
MyServiceClassName: service
别名基本上会抑制第二个服务的自动生成。
一种 "better" 方法(或至少更推荐的方法)是停止使用 $container->get 并只在需要的地方注入服务。然后,您将完全删除 'service' 定义。
最后一点,如果服务本身需要任何缩放器构造函数参数(字符串或整数),那么自动装配过程将失败并显示错误消息。这些错误最初可能非常令人困惑,因为您已经定义了一个工作服务,但由于服务 ID 不同,自动装配只是向前推进并尝试创建一个新服务。