web-dev-qa-db-ja.com

Twigテンプレートからすべての変数を取得する方法?

PHPでTwigテンプレート内のすべての変数を取得することは可能ですか?

SomeTemplate.twig.phpの例:

Hello {{ name }}, 
your new email is {{ email }}

今、私はこのようなことをしたいです:

$template = $twig->loadTemplate('someTemplate');
$variables = $template->getVariables();

$ variablesに「名前」と「電子メール」が含まれるようになりました。

私がこれをしたいのは、ユーザーがtwigテンプレートと変数を動的に設定し、APIを介して変数を入力するCMSシステムで作業しているためです。

デフォルト値を未設定変数に設定したいので、テンプレート内に存在するすべての変数のリストが必要です…

38
Stefan Neubig

2017年更新

{{ dump() }}フィルターを使用することで可能です。コメントで指摘してくれてありがとう!


[〜#〜] outdated [〜#〜]

それは不可能。

これらの変数をtwigテンプレートで探して、それらに|default('your_value')フィルターを追加します。変数が定義され、空でないかどうかをチェックし、ない場合は置き換えます。あなたの価値で。

15
Vitalii Zurian

これは、現在のコンテキストで使用可能なすべてのトップレベルキーを取得するのに役立ちます。

<ol>
    {% for key, value in _context  %}
      <li>{{ key }}</li>
    {% endfor %}
</ol>

https://www.drupal.org/node/190678 に感謝

64
duncan

2015年に追加された回答

過去には不可能でした。ただし、バージョン1.5以降では dump() 関数が追加されました。したがって、パラメータなしでdump()を呼び出す現在のコンテキストからすべての変数を取得できます。

_<pre>
    {{ dump(user) }}
</pre>
_

ただし、Twig環境を作成するときは、デフォルトでdump()が使用できないため、 Twig_Extension_Debug 拡張機能を明示的に追加する必要があります。

_$twig = new Twig_Environment($loader, array(
    'debug' => true,
    // ...
));
$twig->addExtension(new Twig_Extension_Debug());
_

Symfony、Silexなどを使用している場合、dump()はデフォルトで利用可能です。

編集:

グローバル変数__context_を使用して、テンプレートに渡されるすべての変数(dump()のコンテキスト外)を参照することもできます。これはあなたが探していたものです。これは、すべての変数名をそれらの値に関連付ける配列です。

Twig documentation。)に追加情報があります。

ただし、この特定の質問については、これらのカスタム変数をすべて同じ傘変数の下で収集するのがおそらく最善です。そうすれば、それらを取得するのは頭痛の種になりません。私は_custom_variables_と呼ばれる配列になります。

16
felipsmartins

すべての変数をダンプする最良の方法と最も簡単な方法を次に示します。

{{ dump () }}

出典: https://www.drupal.org/docs/8/theming/twig/discovering-and-inspecting-variables-in-twig-templates

13
ReaperSoon

テキスト内のすべてのTwig要素が必要な場合は、以下を使用します。

preg_match_all('/\{\%\s*(.*)\s*\%\}|\{\{(?!%)\s*((?:[^\s])*)\s*(?<!%)\}\}/i', $text, $matches);

WSIWYGエディターがHTMLタグをTwig変数内に配置した問題を抱えていました。

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;
}

[〜#〜] update [〜#〜]

この式を使用して、すべての{{}}および{%%}を検索します

preg_match_all('/\{\%\s*([^\%\}]*)\s*\%\}|\{\{\s*([^\}\}]*)\s*\}\}/i', $text, $matches);
8
19Gerhard85

私のやり方は

<script>console.log({{ _context | json_encode | raw }});</script>

そして、DevToolsを使用してコンソールを確認するだけです

6
Luke Madhanga

ダンカンの答えをかなりの時間使用した後、すべてのtwigテンプレートの変数をダンプする「正しい」方法をようやく見つけました。

_{% dump %}
_

それでおしまい。テンプレートで使用可能なすべての変数は、{{ dump() }}のようにhtmlの中央ではなく、プロファイラーのダンプセクションにダンプされます。

dump()の内容を変数に入れた場合:

_{% set d = dump() %}
_

すべての変数を取得しますが、「ダンプ準備完了」htmlにあるので、解析するのは面倒です。

お役に立てば幸いです。

5
Mawcel

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
        }
    }
}
4
mickadoo
$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;
}

楽しんで :-)

3
Lars

テンプレートを解析し、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, call_user_func(__FUNCTION__, $cur));
        }
        else if ($cur->getAttribute('always_defined') == false)
        {
            // List only predefined variables expected by template, 
            // filtering out `v` and leaving `arr` from `{% for v in arr%}`
            $vars[] = $cur->getAttribute('name');
        }
    }

    return $vars;
}
2
devicenull

上記のすべての答えを試してかなり夜を過ごした後、予期しない理由で、単純なテンプレートでは正規表現がまったく機能しないことに気付きました。彼らはジャンクまたは部分的な情報を返しました。そこで、タグをカウントするのではなく、タグ間のすべてのコンテンツを消去することにしました^ _ ^。

つまり、テンプレートが'AAA {{BB}} CC {{DD}} {{BB}} SS'である場合、テンプレートの先頭に'}}'を追加し、末尾に'{{を追加します。..}}間のすべてのコンテンツと{{ => }}{{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));

それが誰かを助けることを願っています:)

1
Alexey Abraham

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拡張機能の作成について支援が必要な場合は、このドキュメントを参照してください。

http://twig.sensiolabs.org/doc/2.x/advanced.html

Twig2Schemaクラスを作成して、Twig ASTから変数を推測します。ドキュメント内の変数を取得するには、Twig = ASTそして、特定のタイプの言語ノードに遭遇したときにルールを設定します。

このクラスは、常に定義されていない場合はノードから変数名を抽出し、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;
    }
}
0
Cameron Wilby

この質問には 複製 があります。そこで、上記よりも便利で、より強力な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.
0
Martin Winter