我正在尝试执行一个位于Twig模板上的数组内的闭包。您可以在下面找到我正在尝试的简化代码段:
//Symfony controller
...
$funcs = array(
"conditional" => function($obj){
return $obj->getFoo() === $obj::TRUE_FOO
}
);
$this->render('template_name', array('funcs' => $funcs));
{# Twig template #}
{# obj var is set #}
...
{% if funcs.conditional(obj)%}
<p>Got it</p>
{% endif %}
当Twig呈现模板时,抛出一个抱怨数组到字符串转换的异常
An exception has been thrown during the rendering of a template ("Notice: Array to string conversion") in "template_name.html.twig".
500 Internal Server Error - Twig_Error_Runtime
1 linked Exception: ContextErrorException »
感谢您的帮助。
谢谢!
答案 0 :(得分:1)
您无法在Twig模板中直接执行闭包。但是,如果您需要在模板中调用某些PHP,则应使用create a Twig Extension并在其中包含您的逻辑。
答案 1 :(得分:1)
Twig不允许直接这样做。您可以向Twig添加一个简单的函数来处理闭包的执行,或者将闭包封装在一个类中以便能够使用Twig的属性函数(因为直接调用attribute(_context, 'myclosure', args)
将触发致命错误,因为Twig将返回直接闭包并忽略给定的参数,因为_context
是一个数组。)
实现此目的的简单Twig扩展对于Symfony 2.8+来说就像这样。 (对于Symfony 4,请参阅new documentation)
// src/AppBundle/Twig/Extension/CoreExtensions.php
namespace AppBundle\Twig\Extension;
class CoreExtensions extends \Twig_Extension
{
public function getFunctions()
{
return [
new \Twig_SimpleFunction('execute', [$this, 'executeClosure'])
];
}
public function executeClosure(\Closure $closure, $arguments)
{
return $closure(...$arguments);
}
public function getName()
{
return 'core_extensions_twig_extension';
}
}
然后,在模板中,您只需调用execute:
{{ execute(closure, [argument1, argument2]) }}
没有扩展Twig,解决这个问题的一种方法是使用一个类作为闭包的包装器并使用Twig的attribute
函数,因为它可以用来调用一个方法。对象
// src/AppBundle/Twig/ClosureWrapper.php
namespace AppBundle\Twig;
/**
* Wrapper to get around the issue of not being able to use closures in Twig
* Since it is possible to call a method of a given object in Twig via "attribute",
* the only purpose of this class is to store the closure and give a method to execute it
*/
class ClosureWrapper
{
private $closure;
public function __construct($closure)
{
$this->closure = $closure;
}
public function execute()
{
return ($this->closure)(...func_get_args());
}
}
然后,你只需要在渲染时给你的模板一个ClosureWrapper实例而不是闭包本身:
use AppBundle\Twig\ClosureWrapper;
class MyController extends Controller
{
public function myAction()
{
$localValue = 2;
$closure = new ClosureWrapper(function($param1, $param2) use ($localValue) {
return $localValue + $param1 + $param2;
});
return $this->render('mytemplate.html.twig', ['closure' => $closure]);
}
...
最后,在您的模板中,您需要使用attribute
来执行您在控制器中定义的闭包:
// Displays 12
{{ attribute(closure, 'execute', [4, 6]) }}
然而,这有点多余,因为internally,Twig的attribute
函数也解包了给定的参数。通过使用上面的代码,对于每个调用,参数将被连续解压缩,打包并再次解压缩。
答案 2 :(得分:1)
如果您使用的是闭包,则可以使用闭包的call方法
http://php.net/manual/en/closure.call.php
您最终会得到这样的东西
{{ funcs.conditional.call(obj, obj) }}
由于第一个参数也必须是“ this”将引用的对象,因此我将传递与第一个参数相同的对象。
没有树枝扩展名,也没有多余的PHP代码可做;)