【问题标题】:Replace/decorate `translation.reader`替换/装饰`translation.reader`
【发布时间】:2018-10-16 16:23:37
【问题描述】:

我填充了一个错误,但它接缝我已经关闭:p

我只想用我自己的类替换服务Symfony\Component\Translation\Reader\TranslationReader (translation.reader)。事实上,如果我愿意,我想知道如何替换 SF4 的任何服务

translation.reader::addLoader() 通常由框架调用,但如果我用自己的类进行装饰,addLoader 不会被调用。

你能告诉我如何才能放弃替换我自己的服务吗?

https://github.com/symfony/symfony/issues/28843

受影响的 Symfony 版本:4.1.6

说明
无法修饰translation.reader(我想更改默认的i18n文件加载过程)

如何重现

复制/调整Symfony\Component\Translation\Reader\TranslationReaderApp\Translation\Reader\TranslationReader

关注https://symfony.com/doc/current/service_container/service_decoration.html

修改services.yaml

Symfony\Component\Translation\Reader\TranslationReader: ~

App\Translation\Reader\TranslationReader:
    decorates: Symfony\Component\Translation\Reader\TranslationReader

#translation.reader: '@App\Translation\Reader\TranslationReader'

没有别名:新服务被忽略 使用别名:read() 是触发器,但不是 addLoader()

这里是生成的注入文件getTranslationReaderService.php

<?php

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;

// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the private 'App\Translation\Reader\TranslationReader' shared autowired service.

include_once $this->targetDirs[3].'/vendor/symfony/translation/Reader/TranslationReaderInterface.php';
include_once $this->targetDirs[3].'/src/Translation/Reader/TranslationReader.php';

return $this->privates['App\Translation\Reader\TranslationReader'] = new \App\Translation\Reader\TranslationReader();

默认情况下它看起来像:

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;

// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the private 'translation.reader' shared service.

include_once $this->targetDirs[3].'/vendor/symfony/translation/Reader/TranslationReaderInterface.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Reader/TranslationReader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/LoaderInterface.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/ArrayLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/FileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/PhpFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/YamlFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/XliffFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/PoFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/MoFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/QtFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/CsvFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/IcuResFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/IcuDatFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/IniFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/JsonFileLoader.php';

$this->privates['translation.reader'] = $instance = new \Symfony\Component\Translation\Reader\TranslationReader();

$a = ($this->privates['translation.loader.yml'] ?? $this->privates['translation.loader.yml'] = new \Symfony\Component\Translation\Loader\YamlFileLoader());
$b = ($this->privates['translation.loader.xliff'] ?? $this->privates['translation.loader.xliff'] = new \Symfony\Component\Translation\Loader\XliffFileLoader());

$instance->addLoader('php', ($this->privates['translation.loader.php'] ?? $this->privates['translation.loader.php'] = new \Symfony\Component\Translation\Loader\PhpFileLoader()));
$instance->addLoader('yaml', $a);
$instance->addLoader('yml', $a);
$instance->addLoader('xlf', $b);
$instance->addLoader('xliff', $b);
$instance->addLoader('po', ($this->privates['translation.loader.po'] ?? $this->privates['translation.loader.po'] = new \Symfony\Component\Translation\Loader\PoFileLoader()));
$instance->addLoader('mo', ($this->privates['translation.loader.mo'] ?? $this->privates['translation.loader.mo'] = new \Symfony\Component\Translation\Loader\MoFileLoader()));
$instance->addLoader('ts', ($this->privates['translation.loader.qt'] ?? $this->privates['translation.loader.qt'] = new \Symfony\Component\Translation\Loader\QtFileLoader()));
$instance->addLoader('csv', ($this->privates['translation.loader.csv'] ?? $this->privates['translation.loader.csv'] = new \Symfony\Component\Translation\Loader\CsvFileLoader()));
$instance->addLoader('res', ($this->privates['translation.loader.res'] ?? $this->privates['translation.loader.res'] = new \Symfony\Component\Translation\Loader\IcuResFileLoader()));
$instance->addLoader('dat', ($this->privates['translation.loader.dat'] ?? $this->privates['translation.loader.dat'] = new \Symfony\Component\Translation\Loader\IcuDatFileLoader()));
$instance->addLoader('ini', ($this->privates['translation.loader.ini'] ?? $this->privates['translation.loader.ini'] = new \Symfony\Component\Translation\Loader\IniFileLoader()));
$instance->addLoader('json', ($this->privates['translation.loader.json'] ?? $this->privates['translation.loader.json'] = new \Symfony\Component\Translation\Loader\JsonFileLoader()));

return $instance;

你可以看到我做装饰的时候没有注入加载器...

【问题讨论】:

    标签: php symfony symfony4


    【解决方案1】:

    我不确定这是否是您问题的根源,但这里有一些说明。希望这能帮助您找到解决方案,即使我实际上并没有完整回答您的问题。

    1) Symfony 中的某些翻译服务在缓存预热阶段被调用。每当您更改配置或执行bin/console cache:clear 时,您都会看到这些类正在运行,它们会在您的var/cache/&lt;env&gt;/translations/ 文件夹中生成翻译。

    2) 您可以尝试确保在您的缓存中,var/cache/&lt;env&gt;/Container&lt;...&gt;/getTranslation_ReaderService.php 加载的类是您的,而不是像这样的默认类:

    $this->privates['translation.reader'] = 
        new \Symfony\Component\Translation\Reader\TranslationReader();
    

    3) 我在dev 环境中也遇到了类似的问题,我试图用我自己的服务替换Symfony\Component\Translation\Translator,但一开始并没有设法让我的方法被调用。部分解释是当 Symfony Profiler 被启用时,Symfony 会做这样的事情(src&lt;env&gt;DebugProjectContainer.php&gt;):

    $this->services['translator'] = new \Symfony\Component\Translation\DataCollectorTranslator(
        ($this->privates['translator.default'] ?? $this->getTranslator_DefaultService())
    );
    

    DataCollectorTranslator 本身就是一个包装器,用于将它作为构造函数参数获取的任何翻译器。

    我知道这不是一个完美的答案,但希望这能帮助您找到解决方案。

    【讨论】:

    • 这是一个很好的指针,现在我可以“看到”生成的注入文件,但它(还没有)工作
    【解决方案2】:

    我已经成功了......但请随时发表评论

    我必须创建一个TranslatorPass 来将加载器添加到装饰服务注入文件中。

    <?php
    
    namespace App\Translation\DependencyInjection;
    
    use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\DependencyInjection\Reference;
    use App\Translation\Reader\TranslationReader;
    
    class TranslatorPass implements CompilerPassInterface
    {
        private $readerServiceId;
        private $loaderTag;
    
        public function __construct(string $readerServiceId = TranslationReader::class, string $loaderTag = 'translation.loader')
        {
            $this->readerServiceId = $readerServiceId;
            $this->loaderTag = $loaderTag;
        }
    
        public function process(ContainerBuilder $container)
        {
            $loaders = array();
            $loaderRefs = array();
            foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
                $loaderRefs[$id] = new Reference($id);
                $loaders[$id][] = $attributes[0]['alias'];
                if (isset($attributes[0]['legacy-alias'])) {
                    $loaders[$id][] = $attributes[0]['legacy-alias'];
                }
            }
    
            if ($container->hasDefinition($this->readerServiceId)) {
                $definition = $container->getDefinition($this->readerServiceId);
                foreach ($loaders as $id => $formats) {
                    foreach ($formats as $format) {
                        $definition->addMethodCall('addLoader', array($format, $loaderRefs[$id]));
                    }
                }
            }
        }
    }
    

    我已经把它放在Kernel.php

    protected function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new TranslatorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1000);
    }
    

    然后

    bin/console cache:clear
    

    瞧!

    【讨论】:

    • 我和 Silex 玩了很多年,我不习惯“结构化”注射......我不确定我是否喜欢 SF 的复杂性 :)
    猜你喜欢
    • 1970-01-01
    • 2021-11-20
    • 2019-10-21
    • 2019-08-22
    • 1970-01-01
    • 2021-12-18
    • 2013-05-24
    • 2013-01-16
    • 2020-11-06
    相关资源
    最近更新 更多