In this article I will discuss how I obtained a 5x speedup on a Smarty driven website on a Lighttpd Web server. This is achieved by enabling Lighttpd to a directly access and serve the cached files directly from the file system, rather than calling into Smarty.
Smarty’s caching engine does a great job at compiling the templates at the correct interval and this creates a drastic speedup compared to recompiling the template on each page request.
However, even when Smarty is serving up cached pages, there is a lot of overhead added to each request when compared to the Web server directly serving the cached page. This is because PHP is still being loaded, the Smarty library is being included, and a small amount of logic is being performed within Smarty before the cached page is finally being passed along.
In cases where the cache life must be very short due to frequent changes to the data being rendered, the approach I will explain in this article may not be viable. However, in these cases it would be still possible to use a push method to remove cache when the data changes, versus adding the overhead of checking for changes on each page request. In my opinion, this push approach to caching reduces the cost to the lowest possible value, so if the need for performance is of the utmost importance, then it makes sense to implement this approach.
In my case, the data changes occur infrequently and a combination of clearing the cache on a scheduled interval plus a method to manually force a recompilation of a specific page is adequate, and a worthwhile trade-off for the performance increase. Also, I am a performance junkie.
Since Smarty is a flexible library, every implementation is unique. So while I cannot give imperitive instructions on how you can implement this lighttpd cache, can explain how I did it.
This was my lighttpd configuration for the site prior to implementing the cache. It has a few basic rewrite rules so request for a (htm|html) file gets passed to the index.php. This file acts as a handler to determine the actual smarty template to load, enabling the use of friendly URLs.
$HTTP["host"] == "www.site.com" {
server.document-root = "/var/www/site/html"
url.rewrite = (
"/(.*)\.(htm|html)(\?.*)??$" => "/index.php?p=$1",
"/(.*)/(\?.*)??$" => "/index.php?p=$1",
"/(.*)/(.*)/$" => "/index.php?p=$1/$2"
)
}
And after implementing the cache, this is the lighttpd host configuration:
$HTTP["host"] =~ "www.site.com" {
server.document-root = "/var/www/site/html"
magnet.attract-physical-path-to = ("/var/www/site/html/rewrite.lua")
}
Below is the code for rewrite.lua (referenced in the lighttpd conf above) which implements to handle the rewrite rules. It checks the file system to determine if a cached file exists, and if so, it serves that file. Otherwise, it rewrites to the index.php handler so smarty can generate the new cache file.
The cache_path variable in the rewrite.lua script is my smarty cache dir ( $smarty->cache_dir ), plus the $smarty->cache_id (mine is blank, be sure to append it to the cache_path variable as a subdir of your smarty cache_dir)
At this point, lighttpd is rewriting all requests to my index.php handler since it will not find a cached copy in the the cache_path dir. I now have to work with the index.php handler file so Smarty will save the compiled HTML files into the cache_path defined in rewrite.lua. To do this, I wrote a custom cache handler function for smarty and stuck it in the index.php file. So far, my Smarty setup is looking like this:
The main thing here is that the caching is enabled, each request will recompile the template, the compile_id is blank, and the cache_dir matches what is set for cache_path in rewrite.lua.
The function server_rewrite_cache_handler() overrides the default smarty cache read/write/clear logic so that the file is saved in the correct directory structure that matches the request that was rewritten from lighttpd.
The one last thing is to disable several lines of code in the smarty/internals/core.write_cache_file.php, as by default Smarty will add some serialized data to the top of the cache data it passes to our custom cache handler function. The changes are shown below and occur around line 65 and 66 of the core.write_cache_file.php file.
#$_cache_info = serialize($smarty->_cache_info);
#$params['results'] = strlen($_cache_info) . "\n" . $_cache_info . $params['results'];
That is all, you should see a drastic speedup at this point.
Tags: lighttpd, lua, performance, PHP, smarty
This plugin for Smarty enables in-template access to SOAP services.
It requires the NuSOAP PHP class in your PHP include folder (ie: includes_path/nusoap/nusoap.php) or edit the path in the soap smarty plugin.
You may need to download the NuSOAP php class and place it in your PHP include path.
The smarty soap plugin function is shown after the fold.
Example SOAP Plugin Usage in Smarty Templates:
{soap assign=soapResponse endpoint=http://site.com/resource.soap call=resource..search var=(string)Test debug=true|false}
This is a simple plugin for Smarty to retrieve and parse JSON data into an object in Smarty. Requires the PECL JSON.php class or PHP 5.2.0.
Instructions:
JSON Plugin Usage in Smarty Templates Files:
{json url=http://site.com/data.json resource=result}
The $result variable now contains the parsed JSON data.
This is not meant to bash in any way - I think it is a great library and the developers behind it obviously did an excellent job. That being said, it is not the right fit for every job. I have personally stopped using Smarty in database driven applications I develop for the following reasons.
1. Reduced Performance
Performance is my single biggest issue. Most of my applications are database driven and 99% of the pages must be completely refreshed on each view. So using Smarty’s caching features is out of the picture. This means I am including a huge library to generate and include Smarty generated PHP code, rather than a hand coded version that I could optimize and tweak. All template engines create additional includes and processing and Smarty is no exception.
2. Large Footprint
As of this writing, the Smarty library consists of 71 files consuming nearly 1/2 MB on disk. I am building a framework to be used by a number of applications, most of which are rather small. I wish to avoid this huge footprint, as I want to be able to drop my framework onto a webserver via FTP or a small archive in a minute, not an hour. This huge footprint ties back into my performance issues as well. More files = more includes = increased IO requirements.
3. Separates me from my Programming Language of Choice
I chose to develop my framework and applications in PHP, it is my language of choice and I am most comfortable working in it. I don’t want to have it’s power restricted by the limited featureset of a separate technology, or being forced to write a plugin for Smarty in order to do something natively available in PHP.
4. Relearning is a Waste of my Time
I cannot count the times I wanted to do something that would be so simple in PHP but was either unsupported by Smarty or required some methodology that felt completely hacky. It is a waste of time to learn a second method of coding something unless it is an improved method, and I don’t feel that letting Smarty generate my PHP code for me is an improved method. If it were then all the more reason I should be coding in PHP more! The same goes for anyone I hire to do further development on a project.
5. Doesn’t Separate Logic from Design
Being able to use {$var} vs. <?php echo $var ?> (tag replacement) is convenient, but does not justify the large footprint for me. Using Smarty for anything other than simple tag replacement (ie: if,else,foreach,assign) means the logic is NOT separated from the design.
6. One more Dependency
Using calls to native PHP functions is dependency-free. For me, Smarty amounts to a huge dependency that must be maintained and included, which can be time consuming when dealing with many instances of an application.
7. Extra Configuration Required
Smarty requires a compile directory - this is one more thing to have to create, CHMOD, etc… Once again, when installing multiple instances of an application this increases the time required.
Conclusion
Smarty still has it’s place - I use it for templating purposes on several websites where full caching can be performed. It would still be faster to generate the static HTML and then serve it without ever calling into PHP, which is a topic I will address in the future.
So for me, Smarty will not ever be used in a high-volume database driven application. It still has a place for building cached webpages though. I build backend services that can scale to high volumes of requests, and on the websites where that data is displayed, use Smarty to cache the webpage containing the data from the service.