Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I analyze twig templates without rendering them?

For a custom CMS we are working on, we want to use twig templates (because that's what our frontend is built on). The templates may use the "default" set of functionality provided by twig (like including, blocks, tags, functions, you name it), but we also want to provide certain "widgets" to the pages.

A template might for example look like this:

<h1>{{ 'our_products'|translate }}</h1>

{{ widget_search(widget_parameters) }}

All function calls starting with widget_ are our special CMS widgets.

On the backend, we want our editors to fill in the parameters our widgets need. So for a template including a widget_search for example, they should be presented with an input field where they can enter the search term, but they should not be able to change anything else. For each page type, we would create a template, handcrafted by our frontend developers.

My question is now: How can we analyze a twig template and extract a list of all function calls so that we can build the backend GUI (without rendering the template for the backend)?

I looked into Twig_Parser but I'm not sure how I could utilize that? Especially, what happens if it includes other files? Would that be resolved by the Parser?

(Because the Backend GUI is a separate application from the public facing website (but they both know the template files), we can't simply render the template, as the frontend might have Twig functions registered which the backend simply couldn't execute.)

like image 218
Dan Soap Avatar asked Sep 16 '15 16:09

Dan Soap


1 Answers

Before being rendered, Twig code is:

  • loaded (returns a string containing the twig template)
  • tokenized (returns a TokenStream of Tokens, all Twig elements contained in the template)
  • parsed (returns a Node_Module, a tree containing an object representation of the template)
  • compiled (returns a string containing the php code corresponding to the twig template)

The object that will be useful for you is the Node_Module: you can traverse it recursively to find all functions (or anything else) very easily.

Example (with native Twig functions because I don't have yours):

<?php

require("vendor/autoload.php");

$env = new \Twig_Environment(new \Twig_Loader_Array(array()));
$template = "

<h1>{{ 'our_products'|upper }}</h1>

{{ max([1,random(),random()]) }}

";

$tree = $env->parse($env->tokenize($template));

$functions = array();
listFunctionCalls($tree, $functions);

function listFunctionCalls($node, array &$list) {
  if ($node instanceof \Twig_Node_Expression_Function) {
    $name = $node->getAttribute('name');
    if (!in_array($name, $list)) {
       $list[] = $name;
    }
  }
  if ($node) {
    foreach ($node as $child) {
      listFunctionCalls($child, $list);
    }
  }
}

var_dump($functions);

Gives the following result:

$ php test.php 
array(2) {
  [0]=>
  string(3) "max"
  [1]=>
  string(6) "random"
}

You can do anything using the tree, enjoy!

like image 192
Alain Tiemblo Avatar answered Sep 20 '22 15:09

Alain Tiemblo