8

Is it possible to get all variables used in a twig template Eg: On template

<!DOCTYPE html>
<html>
    <head>
        <title>My Webpage</title>
    </head>
    <body>
        <ul id="navigation">
        {% for item in navigation %}
            <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
        {% endfor %}
        </ul>

        <h1>My Webpage</h1>
        {{ a_variable }}
    </body>
</html>

Now i need to get all variables used in the above as an array like

Array(1=>'navigation',2=>'a_variable')

Its best if it cab be solved by twig itself

Anees v
  • 323
  • 5
  • 13
  • and why is `item` not a variable ? – HamZa May 26 '13 at 09:53
  • 1
    @HamZaDzCyberDeV It is used internally in the template, but it's not a parameter of the template. Whoever invokes `render($op_template)` doesn't care about `item`. –  May 26 '13 at 09:55
  • @delnan This question is tagged as `regex`, regex can't find/detect such things so logically speaking you can't code something universal if you want to take in account those details. Speaking of Twig itself, it seems that it [isn't possible](http://stackoverflow.com/q/12799094/), so we may have to do it with regex, but we need to specify the "rules" to match. – HamZa May 26 '13 at 09:57
  • 1
    @HamZaDzCyberDeV You're jumping to conclusions. That there is a `regex` tag does not mean a solution must be based on finite automata and finite automata only (by the way, `parsing` is another tag and that encompasses more than enough to solve this problem). –  May 26 '13 at 10:01
  • @delnan you're right, my bad ... – HamZa May 26 '13 at 10:02
  • 1
    @HamZa DzCyberDeV & Anees v: it is possible by implementing a custom twig node visitor: https://github.com/fabpot/Twig/tree/master/lib/Twig/NodeVisitor – zerkms May 26 '13 at 10:17
  • @zerkms I got [this far](http://regex101.com/r/wN4gS8) now checking for a way to check if a variable exists in Twig ... – HamZa May 26 '13 at 10:18
  • 1
    @HamZa DzCyberDeV: I wouldn't do that using regex as soon as there is so amazingly written parser available :-) – zerkms May 26 '13 at 10:18
  • @zerkms Is there Any documentation available for github.com/fabpot/Twig/tree/master/lib/Twig/NodeVisitor – Anees v May 26 '13 at 10:58
  • It is best if it can be solved by using twig itself – Anees v May 26 '13 at 10:59
  • 2
    @Anees v: I think it's the only http://twig.sensiolabs.org/doc/internals.html But it's not a big deal in case if you understand how parsers are implemented in general – zerkms May 26 '13 at 11:05
  • @zerkms Thanks i have got it done by using node visitor – Anees v May 30 '13 at 11:35
  • @Anees v: that's impressive!! Keen to share? (don't forget to mention my name, I'm very curious) – zerkms May 30 '13 at 11:36
  • @Zerkms `public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) { if($node instanceof Twig_Node_Expression_Name){ $env->vars[]=$node->getAttribute('name'); } if($node instanceof Twig_Node_Expression_AssignName){ $env->assignName[]=$node->getAttribute('name'); } return $node; }` – Anees v May 31 '13 at 13:20
  • @Anees v: that was easy :-) – zerkms May 31 '13 at 22:18

1 Answers1

27

Yo dawg, I heard you like Twig so I wrote a regex so you can parse while you parse:

regex

\{\{(?!%)\s* # Starts with {{ not followed by % followed by 0 or more spaces
      ((?:(?!\.)[^\s])*) # Match anything without a point or space in it
\s*(?<!%)\}\} # Ends with 0 or more spaces not followed by % ending with }}
| # Or
\{%\s* # Starts with {% followed by 0 or more spaces
      (?:\s(?!endfor)(\w+))+ # Match the last word which can not be endfor
\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.

PHP

$string = '<!DOCTYPE html>
<html>
    <head>
        <title>My Webpage</title>
    </head>
    <body>
        <ul id="navigation">
        {% for item in navigation %}
            <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
        {% endfor %}
        </ul>

        <h1>My Webpage</h1>
        {{ a_variable }}
    </body>
</html>';

preg_match_all('/\{\{(?!%)\s*((?:(?!\.)[^\s])*)\s*(?<!%)\}\}|\{%\s*(?:\s(?!endfor)(\w+))+\s*%\}/i', $string, $m);
$m = array_map('array_filter', $m); // Remove empty values
array_shift($m); // Remove first index [0]
print_r($m); // Print results

Regex online demo PHP online demo

Note: This is just a POC and is never meant to be used on production.

HamZa
  • 14,671
  • 11
  • 54
  • 75