Fixing some problems in Template Lite
November 8, 2007
As a PHP developer probably you are familiar with Smarty, which is maybe the de facto PHP template engine. Smarty is sometimes criticized that it has too many feature and is too big. I think that in an average development Smarty could be a good choice, it gives you a lot of potential, and its speed and memory footprint has no impact on the overall performance of the application. But if you develop a really high traffic website these issues could become very important. Template Lite could be a good compromise between the features of Smarty and the performance. It is a lightweight version of Smarty, it is much faster and has much lower memory footprint. It has almost the same features like Smarty, so you won’t have any problem using it after Smarty. While Template Lite works very well, it needs a few improvements.
The name of the compiled template file
By default the name of the compiled template file is the encoded version of the original template file. If turn off this behavior by setting the $encode_file_name variable to false. The problem is, that in this case Template Lite does not include into the name of the compiled file the $template_dir variable. Consider the following situation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php /* * Template layout: * /templates * /domain1.com * /header.tpl * /index.tpl * /footer.tpl * /templates * /domain2.com * /header.tpl * /index.tpl * /footer.tpl */ // This common code handles both domain. $template = new Template_Lite(); $template->template_dir = $_SERVER['SERVER_NAME']; $template->display('index.tpl'); ?> |
The code looks good, but there is a big problem. If you get the index page on domain1.com, and then the index page on domain2.com, the secondly generated compiled file will overwrite the first one. To solve this problem you should modify the source code in the following way:
1 2 3 | //$name = ($this->encode_file_name) ? md5((($this->_resource_type == 1) ? $this->template_dir.$file : $this->_resource_type . &qout;_&qout; . $file)).'.php' : str_replace(&qout;.&qout;, &qout;_&qout;, str_replace(&qout;/&qout;, &qout;_&qout;, $this->_resource_type . &qout;_&qout; . $file)).'.php'; $name = ($this->encode_file_name) ? md5((($this->_resource_type == 1) ? $this->template_dir.$file : $this->_resource_type . &qout;_&qout; . $file)).'.php' : str_replace(array(&qout;:&qout;, &qout;.&qout;, &qout;/&qout;, &qout;\\&qout;), &qout;_&qout;, $this->_resource_type.&qout;_&qout;.$this->template_dir.$file).'.php'; |
This modification should be done in several places in the source code. To find all the places you should search for the “encode_file_name” string.
Writing out the compiled template file
Template Lite by default opens a new file, and writes the compiled template content to it. This is not a good solution if the site has high traffic, because in the time of writing out the file it is not accessible for other processes which will cause error. A better solution is if you write to a temporary file, and when you finish it rename the temporary file to the final name. The name of the temporary file should be unique, you can achieve this by using a suffix like this: getmypid()."_".microtime(TRUE). Now if we have multiple request on the same time, and there isn’t a compiled version of the template, every request starts to generate the compiled version. The renames will be atomic and are ordered by the OS, so this way we can eliminate this type of error. The only drawback is that in Windows we can’t rename to an existing file, but usually you won’t serve a high traffic site on a Windows machine with PHP, so this is not a big problem.
To get Template Lite to work in this way, we should slightly modify its source (class.template.php) in the following way:
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 | /* // Original code $f = fopen($this->compile_dir.'c_'.$name, &qout;w&qout;); fwrite($f, $output); fclose($f); */ // We write to a temporary file at first, and then we rename it to the final name. // Renaming is atomic so we won't have probelm on concurrent requests. $compiledFile = $this->compile_dir.&qout;c_&qout;.$name; $tempFile = $compiledFile.&qout;_&qout;.getmypid().&qout;_&qout;.microtime(TRUE).&qout;.tmp&qout;; $f = fopen($tempFile, &qout;w&qout;); fwrite($f, $output); fclose($f); // This won't work on Windwos, if the target file exists!!! rename($tempFile, $compiledFile); |
The value of the $this->_file variable
The Template_Lite class holds in this variable the name of the currently used template file. If the currently processed template includes an other template, Template Lite stops the processing of the current template and starts to process the included one (it works like a function call). At this stage the value of the $this->_file will be the name of the included template. Unfortunately when the processing step back to the original template, the value of $this->_file won’t change back. Generally this has no effect, but I sucked a little through it when I tried to extend Template Lite (you can read about this soon).
To correct this issue you should modify the class.template.php file on two places:
749 750 | $prev_file_name = $this->_file; $this->_file = $file; |
796 797 | $this->_file = $prev_file_name; return $output; |
Some other little typos
The following snippet comes from the original template.class.php file:
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 | if($this->_resource_type == 1) { $f = fopen($this->template_dir . $file, &qout;r&qout;); $size = filesize($this->template_dir . $file); if ($size > 0) { $file_contents = fread($f, $size); } } else if($this->_resource_type == &qout;file&qout;) { $f = fopen($file, &qout;r&qout;); $size = filesize($file); if ($size > 0) { $file_contents = fread($f, $size); } } else { call_user_func_array($this->_plugins['resource'][$this->_resource_type][0], array($file, &$file_contents, &$this)); } $this->_file = $file; fclose($f); |
Maybe you notice, that no file is opened in the else statement, but fclose($f) is always called. You should move this command to the if and else if statement.
Another little typo is here:
257 258 259 260 261 | if (in_array($key, $this->_vars)) { // The array key should be $key. unset($this->_vars[$index]); } |
You can download the corrected version. The comments are in Hungarian, but after reading the article you won’t need them, and anyway it is a beautiful language.
Posted in
content rss

September 26th, 2008 at 7:59 pm
You forgot one annoying bug in template lite: http://sourceforge.net/tracker/index.php?func=detail&aid=1804679&group_id=163694&atid=828751
It made me hours to figure out are those newline characters.
April 24th, 2009 at 1:02 pm
Hey, nice tips. I’ll buy a bottle of beer to the person from that chat who told me to go to your site
October 16th, 2009 at 5:52 am
Thanks a lot Gergely! I like Template Lite a lot, but I think I’ve had some issues with the compilation race condition in the past. Your improvement is great! Cheers from Brazil!