是否可以使用PHP检索Twig模板中的所有变量?
示例someTemplate.twig.php:
Hello {{ name }},
your new email is {{ email }}
现在我想做这样的事情:
$template = $twig->loadTemplate('someTemplate');
$variables = $template->getVariables();
$ variables现在应该包含“name”和“email”。
我想这样做的原因是我正在研究CMS系统 我的twig模板和变量由我的用户动态设置 他们还通过API填充变量。
我想将默认值设置为未设置变量,因此我 需要模板中存在的所有变量的列表......
答案 0 :(得分:63)
这对我在当前上下文中获取所有顶级键非常有用:
<ol>
{% for key, value in _context %}
<li>{{ key }}</li>
{% endfor %}
</ol>
答案 1 :(得分:16)
2017年更新
可以使用{{ dump() }}
过滤器。感谢您在评论中指出这一点!
<强>过时强>
这是不可能的。
您可以在树枝模板中查找这些变量,并为其添加|default('your_value')
过滤器。它将检查变量是否已定义且不为空,如果不是 - 将用您的值替换它。
答案 2 :(得分:15)
在过去,这是不可能的。但是因为版本1.5 dump()功能已添加。因此,您可以从当前上下文调用dump()获取所有变量,而不使用任何参数:
<pre>
{{ dump(user) }}
</pre>
但是,您必须在创建Twig环境时明确添加Twig_Extension_Debug扩展名,因为默认情况下dump()
不可用:
$twig = new Twig_Environment($loader, array(
'debug' => true,
// ...
));
$twig->addExtension(new Twig_Extension_Debug());
如果您使用的是Symfony,Silex等,dump()
默认可用。
还可以使用全局变量dump()
引用传递给模板的所有变量(在_context
的上下文之外)。这就是你要找的东西。它是一个将所有变量名称与其值相关联的数组。
You can find some additional info in the Twig documentation.
对于这个特定的问题,最好在同一个伞形变量下收集你所说的所有这些自定义变量,这样检索它们就不会让人头痛。我将是一个名为custom_variables
或其他的数组。
答案 3 :(得分:13)
这是转储所有变量的最佳方式和最简单的方法:
flat_list = [item for row in my_list for item in row]
flat_list.sort()
# If you want to reshape the 1d list with shape(1, 81, 2) to 2d list with shape(9, 9, 2)
result = []
temp = []
count = 0
for item in flat_list:
temp.append(item)
count += 1
if count % 9 == 0:
result.append(temp)
temp = []
count = 0
# You also can use numpy to reshape flat_list to (9, 9, 2)
import numpy to np
result = np.array(flat_list).reshape(9, 9, 2)
来源:https://www.drupal.org/docs/8/theming/twig/discovering-and-inspecting-variables-in-twig-templates
答案 4 :(得分:7)
如果你需要文本中的所有Twig元素,只需使用:
preg_match_all('/\{\%\s*(.*)\s*\%\}|\{\{(?!%)\s*((?:[^\s])*)\s*(?<!%)\}\}/i', $text, $matches);
我遇到一个问题,WSIWYG编辑器在Twig变量中放置了HTML标记。我用以下方法过滤它们:
public function cleanHTML($text)
{
preg_match_all('/\{\%\s*(.*)\s*\%\}|\{\{(?!%)\s*((?:[^\s])*)\s*(?<!%)\}\}/i', $text, $matches);
if (isset($matches[0]) && count($matches[0])) {
foreach ($matches[0] as $match) {
$clean_match = strip_tags($match);
$text = str_replace($match, $clean_match, $text);
}
}
return $text;
}
<强>更新强>
使用此表达式查找所有{{}}和{%}}
preg_match_all('/\{\%\s*([^\%\}]*)\s*\%\}|\{\{\s*([^\}\}]*)\s*\}\}/i', $text, $matches);
答案 5 :(得分:6)
我的方式是
<script>console.log({{ _context | json_encode | raw }});</script>
然后我只使用DevTools检查我的控制台
答案 6 :(得分:5)
在使用duncan的答案很长一段时间后,我终于找到了“正确”的方式来转储模板的所有树枝变量:
{% dump %}
就是这样。模板中可用的所有变量都将被转储到分析器的转储部分,而不是像{{ dump() }}
那样在html的中间。
如果您将dump()
的内容放入变量:
{% set d = dump() %}
你将获得所有变量,但在“dump ready”html中,所以解析它会很痛苦。
希望有所帮助。
答案 7 :(得分:3)
我认为19Gerhard85的答案非常好,虽然它可能需要一些调整,因为它为我匹配了一些空字符串。我喜欢在可能的情况下使用现有功能,这是一种主要使用twig函数的方法。您需要访问应用程序的树枝环境。
/**
* @param $twigTemplateName
* @return array
*/
public function getRequiredKeys($twigTemplateName)
{
$twig = $this->twig;
$source = $twig->getLoader()->getSource($twigTemplateName);
$tokens = $twig->tokenize($source);
$parsed = $twig->getParser()->parse($tokens);
$collected = [];
$this->collectNodes($parsed, $collected);
return array_keys($collected);
}
它唯一的自定义部分是仅收集某些类型节点的递归函数:
/**
* @param \Twig_Node[] $nodes
* @param array $collected
*/
private function collectNodes($nodes, array &$collected)
{
foreach ($nodes as $node) {
$childNodes = $node->getIterator()->getArrayCopy();
if (!empty($childNodes)) {
$this->collectNodes($childNodes, $collected); // recursion
} elseif ($node instanceof \Twig_Node_Expression_Name) {
$name = $node->getAttribute('name');
$collected[$name] = $node; // ensure unique values
}
}
}
答案 8 :(得分:3)
$loader1 = new Twig_Loader_Array([
'blub.html' => '{{ twig.template.code }}',
]);
$twig = new Twig_Environment($loader1);
$tokens = $twig->tokenize($loader1->getSource('blub.html'));
$nodes = $twig->getParser()->parse($tokens);
var_dump($this->getTwigVariableNames($nodes));
function getTwigVariableNames($nodes): array
{
$variables = [];
foreach ($nodes as $node) {
if ($node instanceof \Twig_Node_Expression_Name) {
$name = $node->getAttribute('name');
$variables[$name] = $name;
} elseif ($node instanceof \Twig_Node_Expression_Constant && $nodes instanceof \Twig_Node_Expression_GetAttr) {
$value = $node->getAttribute('value');
if (!empty($value) && is_string($value)) {
$variables[$value] = $value;
}
} elseif ($node instanceof \Twig_Node_Expression_GetAttr) {
$path = implode('.', $this->getTwigVariableNames($node));
if (!empty($path)) {
$variables[$path] = $path;
}
} elseif ($node instanceof \Twig_Node) {
$variables += $this->getTwigVariableNames($node);
}
}
return $variables;
}
玩得开心: - )
答案 9 :(得分:1)
在我花了一个晚上,尝试上述所有答案之后,我意识到,出于某些意想不到的原因,正则表达式根本不适用于我的简单模板。他们返回了垃圾或部分信息。所以我决定通过擦除标签之间的所有内容而不是计算标签^ _ ^。
我的意思是,如果模板是'AAA {{BB}} CC {{DD}} {{BB}} SS'
,我只需在模板的开头添加'}}'
,在最后添加'{{
.... }}
之间的所有内容1}}和{{
我只是剥离,在=&gt; }}{{BB,}}{{DD,}}{{BB,}}{{
之间添加逗号。然后 - 只需删除}}
和{{
。
我花了大约15分钟的时间来编写和测试....但是使用正则表达式我花了大约5个小时没有成功。
/**
* deletes ALL the string contents between all the designated characters
* @param $start - pattern start
* @param $end - pattern end
* @param $string - input string,
* @return mixed - string
*/
function auxDeleteAllBetween($start, $end, $string) {
// it helps to assembte comma dilimited strings
$string = strtr($start. $string . $end, array($start => ','.$start, $end => chr(2)));
$startPos = 0;
$endPos = strlen($string);
while( $startPos !== false && $endPos !== false){
$startPos = strpos($string, $start);
$endPos = strpos($string, $end);
if ($startPos === false || $endPos === false) {
return $string;
}
$textToDelete = substr($string, $startPos, ($endPos + strlen($end)) - $startPos);
$string = str_replace($textToDelete, '', $string);
}
return $string;
}
/**
* This function is intended to replace
* //preg_match_all('/\{\%\s*([^\%\}]*)\s*\%\}|\{\{\s*([^\}\}]*)\s*\}\}/i',
* which did not give intended results for some reason.
*
* @param $inputTpl
* @return array
*/
private function auxGetAllTags($inputTpl){
$inputTpl = strtr($inputTpl, array('}}' => ','.chr(1), '{{' => chr(2)));
return explode(',',$this->auxDeleteAllBetween(chr(1),chr(2),$inputTpl));
}
$template = '<style>
td{border-bottom:1px solid #eee;}</style>
<p>Dear {{jedi}},<br>New {{padawan}} is waiting for your approval: </p>
<table border="0">
<tbody><tr><td><strong>Register as</strong></td><td>{{register_as}}, user-{{level}}</td></tr>
<tr><td><strong>Name</strong></td><td>{{first_name}} {{last_name}}</td></tr>...';
print_r($this->auxGetAllTags($template));
希望它能帮助某人:)
答案 10 :(得分:1)
你必须解析模板,然后浏览它返回的AST:
$loaded = $twig->getLoader()->getSource($template);
var_dump(extractVars($twig->parse($twig->tokenize($loaded)));
function extractVars($node)
{
if (!$node instanceof Traversable) return array();
$vars = array();
foreach ($node as $cur)
{
if (get_class($cur) != 'Twig_Node_Expression_Name')
{
$vars = array_merge($vars, extractVars($cur));
}
else
{
$vars[] = $cur->getAttribute('name');
}
}
return $vars;
}
答案 11 :(得分:0)
创建一个Twig_Extension并使用 needs_context 标志添加一个函数:
class MyTwigExtension extends Twig_Extension{
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('myTwigFunction', array($this, 'myTwigFunction'), array('needs_context' => true)),
);
}
public function myTwigFunction($context)
{
var_dump($context);
return '';
}
}
上下文将作为第一个参数传递给您的函数,包含所有变量。
在Twig模板上,您只需调用该功能:
{{myTwigFunction()}}
如果您在创建Twig扩展时需要帮助,请参阅此文档:
答案 12 :(得分:0)
这个问题有a douplicate - 在那里我发现了一个比上面更有用和更强大的RegEX。这个,我已经改进,以更精确地匹配:
\{\{(?!%)\s* # Starts with {{ not followed by % followed by 0 or more spaces
((?:(?!\.)[^\s])*?) # Match anything without a point or space in it
(\|(?:(?!\.)[^\s])*)? # Match filter within variable
\s*(?<!%)\}\} # Ends with 0 or more spaces not followed by % ending with }}
| # Or
\{%\s* # Starts with {% followed by 0 or more spaces
(?:\s(?!endfor)|(endif)|(else)(\w+))+ # Match the last word which can not be endfor, endif or else
\s*%\} # Ends with 0 or more spaces followed by %}
# Flags: i: case insensitive matching | x: Turn on free-spacing mode to ignore whitespace between regex tokens, and allow # comments.
答案 13 :(得分:0)
我构建了一个Twig2Schema类,以从Twig AST推断变量。要获取文档中的变量,您需要递归地“遍历” Twig AST,并在遇到某些类型的语言节点时制定适当的规则。
如果未始终定义变量,则该类从Node提取变量名,并从ForLoopNodes和IfStatements中使用的值中获取变量。
要使用它,可以为整个模板调用infer
,也可以使用inferFromAst
调用树的子集。
<?php
class Twig2Schema
{
/**
* @param \Twig\Environment $twig - A twig environment containing loaded templates
* @param $twigTemplateName - The name of the template to infer variables from
* @param $config - A configuration object for this function
* @return array
*/
public function infer(\Twig\Environment $twig, $twigTemplateName)
{
$source = $twig->getLoader()->getSourceContext($twigTemplateName);
$tokens = $twig->tokenize($source);
$ast = $twig->parse($tokens);
return $this->inferFromAst($ast);
}
/**
* @param \Twig\Node\ModuleNode $ast - An abstract syntax tree parsed from Twig
* @return array - The variables used in the Twig template
*/
public function inferFromAst(\Twig\Node\ModuleNode $ast)
{
$keys = $this->visit($ast);
foreach ($keys as $key => $value) {
if ($value['always_defined'] || $key === '_self') {
unset($keys[$key]);
}
}
return $keys;
}
/**
* @param \Twig\Node\Node $ast - The tree to traverse and extract variables
* @return array - The variables found in this tree
*/
private function visit(\Twig\Node\Node $ast)
{
$vars = [];
switch (get_class($ast)) {
case \Twig\Node\Expression\AssignNameExpression::class:
case \Twig\Node\Expression\NameExpression::class:
$vars[$ast->getAttribute('name')] = [
'type' => get_class($ast),
'always_defined' => $ast->getAttribute('always_defined'),
'is_defined_test' => $ast->getAttribute('is_defined_test'),
'ignore_strict_check' => $ast->getAttribute('ignore_strict_check')
];
break;
case \Twig\Node\ForNode::class:
foreach ($ast as $key => $node) {
switch ($key) {
case 'value_target':
$vars[$node->getAttribute('name')] = [
'for_loop_target' => true,
'always_defined' => $node->getAttribute('always_defined')
];
break;
case 'body':
$vars = array_merge($vars, $this->visit($node));
break;
default:
break;
}
}
break;
case \Twig\Node\IfNode::class:
foreach ($ast->getNode('tests') as $key => $test) {
$vars = array_merge($vars, $this->visit($test));
}
foreach ($ast->getNode('else') as $key => $else) {
$vars = array_merge($vars, $this->visit($else));
}
break;
default:
if ($ast->count()) {
foreach ($ast as $key => $node) {
$vars = array_merge($vars, $this->visit($node));
}
}
break;
}
return $vars;
}
}