"right" 覆盖另一个包的 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 函数从包中加载 twig getAssetUrl 'asset'
/**
* 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 中创建服务配置(您必须在bundle extension 中加载此配置文件)。不要忘记将它放在服务密钥下。
twig.extension.assets:
class: AppBundle\Twig\AssetsExtension
arguments: ["@service_container", "@?router.request_context"]
public: false
tags:
- { name: twig.extension }
现在让我们创建扩展 src/AppBundle/Twig/AssetsExtension.php。这个 class 继承自原始扩展,我将只覆盖一个方法(在模板 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 上测试了这个场景。
另一种使用内置服务的方法是 compiler pass。在编译过程中,你可以修改整个 Symfony 服务容器(删除、替换、修改服务)。 Symfony 就是关于 DIC 的。我希望这是一个足够好的解决方案。
我提供了另一种解决方案,因为从 symfony 3.0 开始,服务的 class 名称不再是可覆盖的。
您必须选择编译器传递解决方案。
假设你想覆盖 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);
}
}
然后创建您的 RoutingExtension 文件。
如果你想为新扩展注入一些服务:
<?php
$definition->addArgument('some_service');
// OR
$definition->addMethodCall('setSomeService' [$container->get('some_service')]);
您可能还想看看 Symfony's "decorate"
# 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
这样原来的服务实际上仍然存在,如果你真的想访问它,你仍然可以访问它
我需要覆盖文件 /vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php
它使用 twig 函数从包中加载 twig getAssetUrl 'asset'
/**
* 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 中创建服务配置(您必须在bundle extension 中加载此配置文件)。不要忘记将它放在服务密钥下。
twig.extension.assets:
class: AppBundle\Twig\AssetsExtension
arguments: ["@service_container", "@?router.request_context"]
public: false
tags:
- { name: twig.extension }
现在让我们创建扩展 src/AppBundle/Twig/AssetsExtension.php。这个 class 继承自原始扩展,我将只覆盖一个方法(在模板 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 上测试了这个场景。
另一种使用内置服务的方法是 compiler pass。在编译过程中,你可以修改整个 Symfony 服务容器(删除、替换、修改服务)。 Symfony 就是关于 DIC 的。我希望这是一个足够好的解决方案。
我提供了另一种解决方案,因为从 symfony 3.0 开始,服务的 class 名称不再是可覆盖的。 您必须选择编译器传递解决方案。
假设你想覆盖 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);
}
}
然后创建您的 RoutingExtension 文件。
如果你想为新扩展注入一些服务:
<?php
$definition->addArgument('some_service');
// OR
$definition->addMethodCall('setSomeService' [$container->get('some_service')]);
您可能还想看看 Symfony's "decorate"
# 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
这样原来的服务实际上仍然存在,如果你真的想访问它,你仍然可以访问它