Good day. The first stable version of the new
Fenom template engine has been released. For the impatient,
Fenom is a lightweight (statistics attached), fast (benchmark attached), flexible (API attached) template engine, which can be a good substitute for Twig or Smarty template engines.
The
Fenom template engine is a vivid example of how the question “interesting, and what is the
tokenizer extension
capable of ?” Could lead to something more than a couple of scripts in the sandbox. Like many projects, the template engine started just-for-fun and was created in one evening. The first version of the template engine occupied only 900 lines of code that parsed primitive templates. To maintain extensibility, we had to add another 2500 lines of code to the template code.
If anyone is interested, I can tell you in a separate article all the interesting points and nuances that I had to face when creating a template.
')
Ideology
When it became clear that the experiment went beyond the usual sandbox game, there was a desire to bring it to a full-fledged OpenSource project that could be used in highload projects and compete with popular PHP template engines. Templating in highload projects is a very complex topic. When you have to deal with hundreds of thousands of user-space templates, all template engines begin to have their own “cockroaches”. By the time Fenom was created, I managed to work with PHP templates such as Smarty2, Smarty3, Quicky, Twig, Latte. My experience with them helped me understand what the “my dream” template should be :)
When developing a template engine, I tried to adhere to the following requirements:
- Maximum flexibility. The flexibility of the template engine should be sufficient to add any functionality or the ability to change the behavior of the template engine if necessary. This is dictated by the fact that the author of the template engine may not be aware of all the requirements for template matching, and therefore must enable the expansion of the template engine.
- Trust but check. Safety of the template engine should include not only protection against various injections, but also protection against the incorrect use of various templates of the template engine itself, which can lead to PHP errors when executing the template code. This is especially true when regular users have access to templates.
- The faster the better. Performance should be comparable to the speed of generating a template written in pure PHP code. And under certain conditions, the template engine should win on speed templates in pure PHP.
I suppose, in order to avoid unnecessary holivars, this point needs to be clarified: If we give the task to the developer to implement some view in pure PHP, the implementation will most likely be through OOP, not to mention duplicating the code. The template engine is devoid of all prejudices and easily creates sheets of valid code optimized for fast execution. An excellent example is the template inheritance algorithm. - Keep it simple and people will reach out to you. The template engine, like any other project, should be based on the KISS principle. The code is as compact and clear as possible, OOP without fanaticism, everywhere traceable logic. This is very important for any project, since the more code in a project, the harder it is to maintain, debug, test and involve third-party developers in such a project. This is one of the reasons why regular expressions are not used at all in Fenom to parse templates.
- An old friend is better than two new ones. It is better to adopt the generally accepted simple syntax of existing template engines than to implement a new one. In Fenom, the Smarty-like syntax of templates is taken as the basis, since there is only one type of tag framed in braces and this syntax is easier to convert to tokens.
- Do not eat a lot. Fenom algorithms are trying to save the resources of the machine to the maximum - both the CPU and RAM. The template engine tries to make all the complex calculations at the template compilation stage so that the execution is as fast as possible and not resource-intensive. As a consequence, the template engine has the ability to stream data (display and export methods) without accumulating in memory.
- Do not swine. Fenom templates are objects that will free up memory if there are no references to them, while Twig has classes, Smarty are functions that you can’t remove from memory, which can add difficulty when processing a large number of templates. In addition, the final PHP code of the template should not be large, since the larger the template, the more op-code, the less memory in op-caches.
Performance
I borrowed a set of tests from a friend of
VEG from the article
Smarty vs. Twig: A performance where Twig performed well. Not a little time has passed since then, so I decided to repeat, the tests cited in the article, with an additional rival - Fenom. Characters: Smarty version 3.1.13, Twig version 1.13.0 + extension twig.so, Fenom version 1.0.3. I really wanted to test the Volt from the Phalcon native framework, but, alas, at the first test it went into an endless loop, so it had to be disqualified.
Each test was run in 3 different modes:
- Cold start - the template is not compiled and, therefore, not loaded into the template engine. A very rare case when the template runs for the first time. However, if a cache flush occurred, a surge in template compilations could ruin the system. The rate of compilation, loading and execution patterns.
- Regular launch - the template is already compiled to the file system, but not loaded into the template engine. Quite a frequent case when the template has already been run before, so it already has its own compilation cache on the file system. The indicator is relevant if the template is used only once per script execution. The value characterizes the speed of loading and execution of templates.
- Hot start - the template is compiled and even already loaded into the template engine. Means that the template is used more than 1 time per script call. The rate of performance of the template.
Tests were performed at 2.6 GHz Intel Core i7, 8 GB 1600 MHz DDR3, MacOS 10.8.4, PHP 5.4.15 from under the CLI. Ubuntu 12.04 also showed a ratio in the results. PHP was run with the -n flag, which disables all external extensions. With twig.so tested separately. The result was taken from the second test run, when the OS has already
cached test files and templating engines. Op-keshery, of course, disabled as all other extensions.
So, the first test is the output of a large number of variables. In fact, this test makes sense since all patterns consist of outputting variables to a pattern.
The output of a large number of variables:Template engines | Cold start | Regular launch | Hot start |
---|
Smarty3 | 4.3394 sec, 15.2 MiB | 0.0239 sec, 9.2 MiB | 0.0015 sec, 9.2 MiB |
Twig | 1.9618 sec, 68.9 MiB | 0.0341 sec, 17.0 MiB | 0.0013 sec, 17.0 MiB |
Fenom | 0.3432 sec, 8.9 MiB | 0.0157 sec, 6.6 MiB | 0.0011 sec, 6.6 MiB |
In the first test, Twig allocated itself - tests dropped due to exceeding the memory limit (by default, 32MB).
The second test is an iteration of a large array, which is also a frequent case in templates.
Iteration of a large array:Template engines | Cold start | Regular launch | Hot start |
---|
Smarty3 | 0.0223 sec, 5.8 MiB | 0.0036 sec, 3.1 MiB | 0.0024 sec, 3.1 MiB |
Twig | 0.0300 sec, 4.0 MiB | 0.0174 sec, 2.7 MiB | 0.0166 sec, 2.7 MiB |
Twig + extension | 0.0225 sec, 4.7 MiB | 0.0064 sec, 3.2 MiB | 0.0060 sec, 3.5 MiB |
Fenom | 0.0080 sec, 3.1 MiB | 0.0022 sec, 2.5 MiB | 0.0017 sec, 2.5 MiB |
The third test is the inheritance of a large number of patterns. I consider this test more contrived because more than 2-3 levels of inheritance are enough for the eyes, nevertheless, 100 level inheritance remains in the test. In spite of all artificiality, the test gives a general idea about the performance of template inheritance.
Inheritance of a large number of patterns:Template engines | Cold start | Regular launch | Hot start |
---|
Smarty3 | 0.4165 sec, 10.1 MiB | 0.0008 sec, 3.1 MiB | 0.0001 sec, 3.1 MiB |
Twig | 0.3626 sec, 11.2 MiB | 0.0252 sec, 6.5 MiB | 0.0021 sec, 6.5 MiB |
Fenom | 0.0569 sec, 3.2 MiB | 0.0005 sec, 2.5 MiB | 0.0000 sec, 2.5 MiB |
Twig stood out here, the test fell due to exceeding the allowable nesting, in this case recursion. An error was thrown by the xDebug extension, which later turned off for tests.
TotalThe obvious winner was the Fenom, which was largely ahead of Smarty and Twig in terms of both speed and resource saving. In second place is Smarty, Twig gets bronze.
What is under the hood?
Below is a table of the fat content of the code:
Template engine | Number of files | Number of classes | Number of lines of code |
---|
Smarty (3.1.13) | 320 | 190 | 55095 |
Twig (1.13.0) | 162 | 131 | 13908 |
Fenom (1.0.4) | 9 | 13 | 3967 |
I will note that:
- Smarty uses the BISON parser generator, which has spawned a cloud of methods. Well, in terms of performance, he showed himself not badly. However, the resulting code is not pretty neat.
- Twig sits tight on regular expressions, but I 've seen worse . The code is pleasant, readable, but, you can consider me Marcus , it is full of entities.
- Fenom makes full use of the basic tokenizer extension (which is also built using BISON).
Where to get?
:
github.com/bzick/fenomPackagist.org:
packagist.org/packages/bzick/fenomComposer:
"fenom/fenom": "1.*"
:
github.com/bzick/fenom/blob/master/docs/readme.md:
github.com/bzick/fenom/issues?
Fenom
.
{parent} ,
in is, . .
P.S., ;)
P.P.S., raw . 1.0.7 .