Requests for comment/Template engine
This page is currently a draft.
|
This proposal was abandoned in favor of Requests for comment/HTML_templating_library.
Template engine | |
---|---|
Component | General |
Creation date | |
Author(s) | User:CSteipp |
Document status | declined |
Background
editSeveral 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
editTwig - "The flexible, fast, and secure template engine for PHP"
The code can be added in includes/libs.
Usage:
- 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 );
- Test1 - Output a <div> with the same id attribute and body 100,000 times
- 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.
- Tests:
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 |