从另一个捆绑包覆盖symfony扩展的“正确”方法是什么?


What's the "right" way to override a symfony extension from another bundle

我需要覆盖文件/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php

它使用 twig 函数 'asset' 从捆绑包中加载 twig getAssetUrl

/**
 * Returns a list of functions to add to the existing list.
 *
 * @return array An array of functions
 */
public function getFunctions()
{
    return array(
        new 'Twig_SimpleFunction('asset', array($this, 'getAssetUrl')),
        new 'Twig_SimpleFunction('assets_version', array($this, 'getAssetsVersion')),
    );
}

我想在不接触核心文件的情况下覆盖 asset() twig 函数,否则下一个 symfony 更新会覆盖它

假设您的代码位于 AppBundle 命名空间中。首先在 app/config.yml 或 src/AppBundle/Resources/config/something.yml 中创建服务配置(您必须在捆绑扩展中加载此配置文件)。不要忘记将其放在服务密钥下。

twig.extension.assets:
    class:     AppBundle'Twig'AssetsExtension
    arguments: ["@service_container", "@?router.request_context"]
    public: false
    tags:
        - { name: twig.extension }

现在让我们创建扩展 src/AppBundle/Twig/AssetsExtension.php。这个类继承自原始扩展,我将只覆盖一个方法(在模板 asset()中)。

<?php
namespace AppBundle'Twig;
class AssetsExtension extends 'Symfony'Bundle'TwigBundle'Extension'AssetsExtension
{
    public function getAssetUrl($path, $packageName = null, $absolute = false, $version = null)
    {
        return parent::getAssetUrl('/something/' . $path, $packageName, $absolute, $version);
    }
}

现在,重新加载后,您的所有资产都应该不正确,并且默认情况下以/something/为前缀。如果出现问题,您可以尝试删除Symfony缓存。我在Symfony 2.5.5上测试了这个场景。

使用内置服务的另一种方法是编译器传递。在编译器传递中,你可以修改整个Symfony服务容器(删除,替换,修改服务)。Symfony是关于DIC的。我希望这是足够好的解决方案。

我提供了另一种解决方案,因为从symfony 3.0开始,服务的类名不再可覆盖。您必须选择编译器传递解决方案。

假设你想在 twig 中覆盖 url() 函数,你应该为它创建一个编译器传递并更改定义:

<?php
// AppBundle/AppBundle.php
namespace AppBundle;
use AppBundle'DependencyInjection'Compiler'TwigRoutingExtensionPass;
use Symfony'Component'DependencyInjection'ContainerBuilder;
use Symfony'Component'HttpKernel'Bundle'Bundle;
class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new TwigRoutingExtensionPass());
    }
}

然后创建 TwigRoutingExtensionPass 文件:

<?php
// AppBundle/DependencyInjectoin/Compiler/TwigRoutingExtensionPass.php    
namespace AppBundle'DependencyInjection'Compiler;
use AppBundle'Twig'Extension'RoutingExtension;
use Symfony'Component'DependencyInjection'ContainerBuilder;
use Symfony'Component'DependencyInjection'Compiler'CompilerPassInterface;
class TwigRoutingExtensionPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (false === $container->hasDefinition('twig.extension.routing')) {
            return;
        }
        $definition = $container->getDefinition('twig.extension.routing');
        $definition->setClass(RoutingExtension::class);
    }
}

然后创建路由扩展文件。

如果要将一些服务注入到新扩展:

<?php
    $definition->addArgument('some_service');
    // OR
    $definition->addMethodCall('setSomeService' [$container->get('some_service')]);

你可能还想看看Symfony的"装饰"

# If it is a named service
My'CoreBundle'Twig'OverrideItWithThisExtension:
    decorates: 'some.other.twig_extension'
# If the service has a "FQCN"
My'CoreBundle'Twig'OverrideItWithThisExtension:
    decorates: Some'Other'TwigExtension

这样,原始服务实际上仍然存在,如果您真的想要,您仍然可以访问它