PHP 加载包扩展时找不到 Symfony 请求堆栈
PHP Symfony request stack not found on bundle extension load
我正在尝试创建可重用的日志记录包,它可以为日志消息提供自定义格式化程序。
格式化程序在主应用程序的配置文件中设置如下:
custom_logger:
formatter: AppBundle\Services\MessageFormatter
然后在 LoggerBundle/DependencyInjection/CustomLoggerExtension.php
收到此配置后,我正在尝试获取记录器服务并设置格式化程序
class CustomLoggerExtension extends ConfigurableExtension
{
public function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
$class = $mergedConfig['formatter'];
$obj = new $class;
$container->get('custom.logger')->setFormatter($obj);
但问题是记录器使用请求堆栈
services:
custom.logger:
class: LoggerBundle\Services\Logger
arguments: ['@request_stack']
所以当我尝试在 loadInternal
函数中获取服务时,请求堆栈似乎尚未初始化并且我收到错误消息:You have requested a non-existent service "request_stack"
正确的做法是什么?
编辑 (2016-04-21 21:54:11)
这很奇怪。即使我删除了请求堆栈并在 loadInternal
中成功设置了格式化程序,当我从主应用程序中的控制器获取它时,它也不在服务中。
我一定是做错了什么:)
好的,我想我明白了。
当调用 load()
或我的 loadInternal()
方法时,symfony 将全新的容器传递给那里,然后将其合并到主容器中。
因此,如果我请求一个服务并在那里创建它,它不会保存在任何地方,而是保存在临时容器中,并最终被销毁(只有服务定义被传递给主容器)。
由于服务是按需加载(创建)的,因此在捆绑加载时实例化它是不对的。
所以你想要做的是配置 definitions
这是我如何让它工作的:
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\FileLocator;
use LoggerBundle\Formatter\MessageFormatterInterface;
class LoggerBundleExtension extends ConfigurableExtension implements CompilerPassInterface
{
protected $customFormatter;
public function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
if (array_key_exists('formatter', $mergedConfig)) {
$this->customFormatter = $mergedConfig['formatter'];
}
}
public function process(ContainerBuilder $containerBuilder)
{
if ($this->customFormatter) {
$class = $this->customFormatter;
//if formatter is service id
$serviceUsed = $containerBuilder->has($this->customFormatter);
if ($serviceUsed) {
$class = $containerBuilder->getDefinition($this->customFormatter)->getClass();
}
if (!class_exists($class) || !is_a($class, MessageFormatterInterface::class, true)) {
throw new \ErrorException('Invalid logger formatter');
}
if ($serviceUsed) {
$containerBuilder
->getDefinition('custom.logger')
->addMethodCall('setFormatter', array(new Reference($this->customFormatter)));
} else {
$containerBuilder
->getDefinition('custom.logger')
->addMethodCall('setFormatter', array(new Definition($this->customFormatter)));
}
}
}
}
在 loadInternal()
中,我检查是否在主应用程序的配置中设置了格式化程序并保存它。
但主要的事情发生在 process()
函数中(你必须实现 Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface
才能实现 运行)。
来自docs.
As process() is called after all extensions are loaded, it allows you to edit service definitions of other extensions as well as retrieving information about service definitions.
所以,我正在做的是:
- 检查
formatter
选项值是否是 class 名称或服务 ID(如果是这样,我从服务定义中检索 class 名称)
- 验证 class 名称(检查它是否有效 class 并确保它实现了所需的接口)
- 向我的记录器服务添加定义,它告诉容器在创建记录器服务时(首次从容器中检索时)使用提供的参数调用
setFormatter
基本上
$containerBuilder->getDefinition('custom.logger')->addMethodCall('setFormatter', array(new Reference('my_formatter')));
与在您的配置中声明的相同:
services:
custom.logger:
class: LoggerBundle\Services\Logger
arguments: ['@request_stack']
calls:
- [setFormatter, ['@my_formatter']]
我正在尝试创建可重用的日志记录包,它可以为日志消息提供自定义格式化程序。 格式化程序在主应用程序的配置文件中设置如下:
custom_logger:
formatter: AppBundle\Services\MessageFormatter
然后在 LoggerBundle/DependencyInjection/CustomLoggerExtension.php
收到此配置后,我正在尝试获取记录器服务并设置格式化程序
class CustomLoggerExtension extends ConfigurableExtension
{
public function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
$class = $mergedConfig['formatter'];
$obj = new $class;
$container->get('custom.logger')->setFormatter($obj);
但问题是记录器使用请求堆栈
services:
custom.logger:
class: LoggerBundle\Services\Logger
arguments: ['@request_stack']
所以当我尝试在 loadInternal
函数中获取服务时,请求堆栈似乎尚未初始化并且我收到错误消息:You have requested a non-existent service "request_stack"
正确的做法是什么?
编辑 (2016-04-21 21:54:11)
这很奇怪。即使我删除了请求堆栈并在 loadInternal
中成功设置了格式化程序,当我从主应用程序中的控制器获取它时,它也不在服务中。
我一定是做错了什么:)
好的,我想我明白了。
当调用 load()
或我的 loadInternal()
方法时,symfony 将全新的容器传递给那里,然后将其合并到主容器中。
因此,如果我请求一个服务并在那里创建它,它不会保存在任何地方,而是保存在临时容器中,并最终被销毁(只有服务定义被传递给主容器)。
由于服务是按需加载(创建)的,因此在捆绑加载时实例化它是不对的。 所以你想要做的是配置 definitions
这是我如何让它工作的:
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\FileLocator;
use LoggerBundle\Formatter\MessageFormatterInterface;
class LoggerBundleExtension extends ConfigurableExtension implements CompilerPassInterface
{
protected $customFormatter;
public function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
if (array_key_exists('formatter', $mergedConfig)) {
$this->customFormatter = $mergedConfig['formatter'];
}
}
public function process(ContainerBuilder $containerBuilder)
{
if ($this->customFormatter) {
$class = $this->customFormatter;
//if formatter is service id
$serviceUsed = $containerBuilder->has($this->customFormatter);
if ($serviceUsed) {
$class = $containerBuilder->getDefinition($this->customFormatter)->getClass();
}
if (!class_exists($class) || !is_a($class, MessageFormatterInterface::class, true)) {
throw new \ErrorException('Invalid logger formatter');
}
if ($serviceUsed) {
$containerBuilder
->getDefinition('custom.logger')
->addMethodCall('setFormatter', array(new Reference($this->customFormatter)));
} else {
$containerBuilder
->getDefinition('custom.logger')
->addMethodCall('setFormatter', array(new Definition($this->customFormatter)));
}
}
}
}
在 loadInternal()
中,我检查是否在主应用程序的配置中设置了格式化程序并保存它。
但主要的事情发生在 process()
函数中(你必须实现 Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface
才能实现 运行)。
来自docs.
As process() is called after all extensions are loaded, it allows you to edit service definitions of other extensions as well as retrieving information about service definitions.
所以,我正在做的是:
- 检查
formatter
选项值是否是 class 名称或服务 ID(如果是这样,我从服务定义中检索 class 名称) - 验证 class 名称(检查它是否有效 class 并确保它实现了所需的接口)
- 向我的记录器服务添加定义,它告诉容器在创建记录器服务时(首次从容器中检索时)使用提供的参数调用
setFormatter
基本上
$containerBuilder->getDefinition('custom.logger')->addMethodCall('setFormatter', array(new Reference('my_formatter')));
与在您的配置中声明的相同:
services:
custom.logger:
class: LoggerBundle\Services\Logger
arguments: ['@request_stack']
calls:
- [setFormatter, ['@my_formatter']]