我不确定我是否理解你为什么需要这个,但我认为这是一个抽象问题的例子:
如何跟踪和扩展 Symfony2 的核心功能?
寻找功能
您似乎无法找到此 render_esi 的执行位置,所以让我们解决这个问题!
这似乎不是一个标准的 Twig 功能,所以它必须是一个扩展,就像你正在创建的一样。
它应该位于 Symfony2 核心文件的某个位置,所以我们开始查看 vendor/symfony/src 文件夹。因为我们已经知道我们正在处理 Twig 的扩展,所以Component 文件夹是不可能的(因为 Twig 是一个独立于 Symfony2 核心组件的库)。
所以我们将其范围缩小到Bridge 和Bundle。如果我们查看它们内部,我们会看到 Bundle/TwigBundle 或 Bridge/Twig。我们也知道 Symfony2 开发人员遵循严格的代码/架构风格,所以我们确切地知道要查找哪个文件夹 - Extension。现在只需检查它们。
长话短说,我们在vendor/symfony/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension 中找到了我们正在寻找的内容,我们在其中看到了render_* 函数。大奖!
扩展功能
在改变任何东西之前,我们需要先模拟已经存在的东西,所以我们创建这样的东西:
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
class NewTwigFunction extends \Twig_Extension
{
private $handler;
public function __construct(FragmentHandler $handler)
{
$this->handler = $handler;
}
public function getFunctions()
{
return array(
'new_func' => new \Twig_Function_Method($this, 'newFunction', array('is_safe' => array('html')) )
);
}
public function newFunction($uri, $options = array())
{
return $this->handler->render($uri, 'esi', $options);
}
public function getName()
{
return "new_func";
}
}
现在当你打电话时
{{ new_func(url(route-name)) }}
您应该看到与
相同的结果
{{ render_esi(url(route-name)) }}
但是我们仍然需要去掉url 部分。
很简单,我们只需将router 服务添加到我们的扩展程序中!现在我们的扩展看起来像这样:
use Symfony\Component\Routing\Router;
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
class NewTwigFunction extends \Twig_Extension
{
private $handler;
private $router;
public function __construct(FragmentHandler $handler, Router $router)
{
$this->handler = $handler;
$this->router = $router;
}
public function getFunctions()
{
return array(
'new_func' => new \Twig_Function_Method($this, 'newFunction', array('is_safe' => array('html')) )
);
}
public function newFunction($routeName, $options = array())
{
$uri = $this->router->generate($routeName);
return $this->handler->render($uri, 'esi', $options);
}
public function getName()
{
return "new_func";
}
}
{{ new_func(route-name) }} 应该可以按预期工作。
夹在中间
按照我的理解,您需要与render_esi 几乎相同的功能,但对输出稍作更改。
所以这意味着我们需要在 return 和 $this->handler->render($uri, $strategy, $options); 之间的某个地方进行挂钩。
我们需要走多深的兔子洞取决于变化。
例如,如果您想在 Response 对象变成实际的 html string 之前对其进行更改,您需要首先找到它被转换的位置。一个不错的选择是查看FragmentHandler:
protected function deliver(Response $response)
{
if (!$response->isSuccessful()) {
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->request->getUri(), $response->getStatusCode()));
}
if (!$response instanceof StreamedResponse) {
return $response->getContent();
}
$response->sendContent();
}
知道了!现在您只需要扩展 FragmentHandler::deliver() 并将您的实现传递到您的 twig 扩展中。
跟踪配置
你要明白Symfony2的核心代码和你在日常生活中写的并没有什么不同,它仍然遵守着自己的规则。
例如,通常在 Symfony2 中创建 Twig 扩展时,您需要将其配置为服务,对吗?好吧,Symfony2 核心扩展的配置方式相同。您只需要找到配置文件所在的位置即可。
按照扩展功能中的逻辑,我们确定它们不在Component 中。 Bridge 实际上是 design pattern 的名称 - 不是您放置服务配置的地方 :)
所以我们只剩下Bundle - 显然我们可以在这里找到我们需要的所有信息:vendor/symfony/src/Bundle/TwigBundle/Resources/config/twig.xml
现在我们只需查看原始HttpKernelExtension 是如何配置的,并按照它的引导:
<service id="twig.extension.httpkernel" class="%twig.extension.httpkernel.class%" public="false">
<argument type="service" id="fragment.handler" />
</service>
将其转换为更常用的.yml 格式,我们的扩展配置可能如下所示:
new_func:
class: Acme\Bundle\MyBundle\Twig\NewTwigFunction
arguments:
- "@fragment.handler"
# Uncomment when implementing code from 2nd example
# - "@router"
tags:
- { name: twig.extension }
public: false