7

我有一个包的语义配置,需要在同一包的编译器传递期间进行解释。

是否可以在不将其存储在中间容器变量中的情况下访问它?

4

7 回答 7

8

是的,有点:

<?php

namespace Acme\DemoBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class CompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $configs = $container->getExtensionConfig('acme_demo');
    }
}

从我可以看到$configs的是一组未合并的配置,并且不包括默认值(由配置 TreeBuilder 定义的值)。

这里这里

于 2013-07-22T15:25:18.417 回答
6

只是为了@Peter 的回答的完整性:getExtensionConfig返回一个数组数组,应该使用对应的数组进行处理,Configuration以便能够访问默认值。

<?php

namespace Acme\DemoBundle\DependencyInjection;

use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;

class CompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $configs = $container->getExtensionConfig('acme_demo');
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        /// You can safely work with $config now
    }

    private function processConfiguration(ConfigurationInterface $configuration, array $configs)
    {
        $processor = new Processor();

        return $processor->processConfiguration($configuration, $configs);
    }
}
于 2015-09-14T11:38:11.757 回答
4

我意识到这是一篇旧帖子,但我一直在寻找相同的信息,最终发现这适用于单个参数:

$cfgVal = $container
  ->getParameterBag()
  ->resolveValue( $container->getParameter( 'param_name' ));

当然,这个功能可能是在原始帖子之后添加的。

于 2015-07-09T20:13:50.957 回答
4

我一直在寻找如何读取已处理的配置,而无需设置自定义参数以稍后在 CompilerPass 中检索,并遇到了以下解决方案。

查看内核中的操作顺序Kernel::prepareContainer,我们可以看到它调用Bundle::getContainerExtension()后跟Bundle::build() 这意味着Extension::load在运行编译器之前调用了方法并处理了配置。

由于 Symfony 还在容器中注册了扩展,因此您只需从CompilerPass.

然而,由于 Symfony 也会清空processedConfigs数组,因此在加载扩展后,您需要使CompilerPass.

namespace AppBundle;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use AppBundle\DependencyInjection\Compiler\AppCompilerPass;

class AppBundle extends Bundle
{
    public function build()
    {
        $container->addCompilerPass(new AppCompilerPass());
    }

    /** demonstration of the default functionality in Bundle
    public function getContainerExtension()
    {
        if (null === $this->extension) {
            $this->extension = new AppBundle\DependencyInjection\AppExtension();
        }

        return $this->extension;
    } */
}
namespace AppBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;

class AppExtension extends Extension
{
    const ALIAS = 'app'; //just for demonstration and reference in CompilerPass

    private $config = array();

    public function load(array $configs, ContainerBuilder $container): void
    {
        //make the processed configuration available
        $this->config = $this->processConfiguration(new Configuration(), $configs);
        //...
    }

    public function getConfig()
    {
        try {
            return $this->config;
        } finally {
            //erases the config after it is retrieved, for security and performance reasons
            $this->config = array();
        }
    }

    /** demonstration of default functionality in Extension
    public function getAlias()
    {
        return self::ALIAS;
    } */
}
namespace AppBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use AppBundle\DependencyInjection\AppExtension;

class AppCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        //...
        if (!$container->hasExtension(AppExtension::ALIAS)) {
            //always make sure the extension exists
            return;
        }
        $config = $container->getExtension(AppExtension::ALIAS)->getConfig();
        dump($config);
        //...
    }
}

您可以将扩展名传递给编译器传递,而不是从容器中检索扩展名。

class AppBundle extends Bundle
{
    public function build()
    {
        $container->addCompilerPass(new AppCompilerPass($this->getContainerExtension()));
    }
}
use AppBundle\DependencyInjection\AppExtension;

class AppCompilerPass implements CompilerPassInterface
{
    private $extension;

    public function __construct(AppExtension $extension = null)
    {
        $this->extension = $extension;
    }


    public function process(ContainerBuilder $container)
    {
        //...
        if (!$this->extension) {
            //always make sure the extension exists
            return;
        }
        $config = $this->extension->getConfig();
        dump($config);
        //...
    }
}
于 2018-12-21T17:59:52.743 回答
1

我有点晚了,但这是我习惯于在编译器传递中检索配置的方式(这应该让每个人都开心;)。

首先,让我们在一个参数中设置配置(或其中的一部分):

<?php

namespace Me\Bundle\MyBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Yaml\Yaml;

class MyBundleExtension extends Extension
{
    const CONFIG_PATH = __DIR__.'/../Resources/config';

    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs);

        $loader = new YamlFileLoader($container, new FileLocator(self::CONFIG_PATH));

        $container->setParameter('my_bundle.config', $config);
    }
}

然后,您可以像在编译器传递中那样使用它:

<?php

namespace Me\Bundle\MyBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;

class UseConfigPass implements CompilerPassInterface
{
    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        // ...

        $config = $container->getParameter('my_bundle.config');

        // ...
    }
}

这样,config 只在扩展文件中处理,您不需要询问第一个元素!

请注意,如果您希望更改另一个捆绑包的配置,您可能需要查看prepend extension

于 2018-08-01T09:35:06.763 回答
1

这是一个老问题,但到目前为止我正在使用这个:

use Symfony\Component\Config\Definition\Processor;
...

public function process(ContainerBuilder $container)
{
    $config = $this->getConfiguration($container);
    ...
}

/**
 * @param ContainerBuilder $container
 * @return array
 */
private function getConfiguration(ContainerBuilder $container)
{
    $parameterBag = $container->getParameterBag();
    $processor = new Processor();
    $configuration = new Configuration();

    return $processor->processConfiguration($configuration, $parameterBag->resolveValue($container->getExtensionConfig('YOUR_EXTENSION')));
}

是的,对于每个 CompilerPass,它都会处理配置,但就我而言,我只有一个,所以没什么大不了的。

于 2020-04-29T09:08:21.067 回答
1

@xPheRe 在编译器传递中加载配置不是一个好的解决方案。这意味着如果您有多个编译器,您将多次加载配置。配置应该在扩展中加载一次。

正如@Peter 所说,如果您确定配置存在,请执行以下操作:

$config = $container->getExtensionConfig('acme_demo')[0];

然后你可以做这样的事情:

$definition = new Definition('your_service_id');
$definition->setArgument(0, $config);
于 2018-04-07T02:09:16.637 回答