Requests for comment/Template engine

Request for comment (RFC)
Template engine
Component General
Creation date
Author(s) User:CSteipp
Document status declined

This proposal was abandoned in favor of Requests for comment/HTML_templating_library.


Several extensions that I've reviewed have built their own template engines for reusable UI components (Wikibase, Flow, GWTools). These are usually difficult to review for security, and will likely not be updated if new exploit vectors are discovered. A few projects have started including their own templating library as well (Scholarships and Fundraising are both using Twig).

Proposal (in general)[edit]

  • Come to consensus on a reasonable template engine, along with fairly strict guidelines about how it should be used
  • Include the engine as a library in core

When I've discussed this with other developers, Twig has come up repeatedly, so I'm adding a specific proposal for that, although I don't have a particular preference for any engine in particular (please add other proposals below!).

Engine Proposal: Twig[edit]

Twig - "The flexible, fast, and secure template engine for PHP"

The code can be added in includes/libs.


  • OutputPage would have a method where the developer could pass a template and a set of substitutions, and the rendered html would be added to the output.
  • By policy, we would only allow passing scalar or simple value objects to the template. The template substitutions should never be function calls.
    • The only exception to this might be passing Message objects, so the template can decide the best escaping strategy for the message where it outputs
  • Twig would be setup to autoescape all output using its 'html' escaping
  • Twig would not be used for high-volume pages, unless the caching/performance can be addressed
  • Variables inserted into attribute names should be escaped with e('html_attr'), values should be in quotes.
  • Variables inserted into style values should be escaped with e('css'), style attribute values should be quotes
  • Variables inserted into javascript variable values should be quotes and escpaed with e('js').
  • lots more...

Issues / Concerns:

  • There isn't a good way to pass a url with user-controlled parameters to the template. Either the url has to be in the template and the parameters injected with e('url') filtering, or the parameters have to be urlencoded in php, added into the url, and the entire url is passed to the template.
  • Performance - A repeated concern with any template engine is performance. I wrote a set of scripts to compare different scenarios, and timed the results on fenari. The test was run using MediaWiki's Html::element(), Twig's string loader, and Twig's file loader (with and without caching enabled).
    • Tests:
      • Test1 - Output a <div> with the same id attribute and body 100,000 times
        • MediaWiki: Html::element( 'div', array( 'id' => $vars['id'] ), $vars['body'] );
        • Twig: $twig->render('<div id="{{ id }}">{{ body }}</div>', $vars );
        • Mustache: $html = $engine->render('<div id="{{ id }}">{{ body }}</div>', $vars );
      • Test1b - Output a <div> with a different id attribute, but the same body 100,000 times
      • Test2 - Output a div for each element of a 1,000 element associative array, 1,000 times updating an element of the array on each itteration.
        • MediaWiki: foreach ( $vars['items'] as $key => $item ) { $body .= Html::element( 'div', array( 'id' => $key ), $item ); }
        • Twig: $html = $twig->render('<div id="{{ id }}">{% for key, item in items %} <div id="{{ key }}">{{ item }}</div>{% endfor %}</div>', $vars );
      • Test3 - Output a div for each element of a 1,000 element array, where each element has a different value, and update an element on each itteration.
        • MediaWiki: foreach ( $vars['items'] as $item ) { $body .= Html::element( 'p', array(), $item ); }
        • Twig: $html = $twig->render('<div id="{{ id }}">{% for item in items %}<p>{{ item }}</p>{% endfor %}</div>', $vars );
        • Mustache: $html = $engine->render('<div id="{{ id }}">{{# items }}<p>{{ . }}</p>{{/ items }}</div>', $vars );
    • Results:
      • Twig's String loader was 12% and 11% faster for test1 and test1b than MediaWiki; Twig's file loader was 5% slower than MediaWiki for test1 and test1b, except test1 when using the cache was only 2% slower.
      • Twig was 63% faster for all loaders for test2.
All times in seconds
Engine Test1 Test1b Test2 Test3
MediaWiki 2.0102240801 2.0677008867 20.3320787191 8.9790220022
Twig (string) 1.8975932121 1.8468580008 7.6429627657 4.4318693161
Twig (file, no cache) 2.1228853941 2.1974167109 7.4258337736 4.5181745052
Twig (file, cached) 2.0449380875 2.1520719051 7.2205805063 4.4390556812
Mustache 2.2674158573 2.3222840071 14.0899132729 / 29.8224892616 (using lambda) 4.3561246872