My review of Code Igniter drew in a lot of email requests for the Smarty caching integration code, so I’ve made an article specifically to address that issue. First of all, my claim of a factor of 15 on the performance gained is not something pulled out of thin air. I monitored Apache’s use of the processor during page load requests. Each page load was for a single Code Igniter driven page which ran a single MySQL query. The request bumped Apache up from 0% processor usage to 3.0% usage (as monitored using top from the console). With caching enabled, a cache miss still only went up to 3.0% processor usage, so there was only negligible overhead for the cache checking, and the cache generation. A cache hit on the same page bumped Apache up to only 0.2% processor usage. Doing the math of 3.0 / 0.2 gives me the factor of 15 that I claimed in the original article.

In order to make the caching the most effective, we want the cache hit to reduce PHP run time to a bare minimum. To this end, I have the cache mechanism running off of a pre_system hook in Code Igniter. My management class is named CacheManager, so my hooks.php file in /system/application/config has in it:

  1. $hook[‘pre_system’] = array(
  2.     ‘class’ => ‘CacheManager’,
  3.     ‘function’ => ‘do_cache’,
  4.     ‘filename’ => ‘CacheManager.php’,
  5.     ‘filepath’ => ‘hooks’,
  6.     ‘params’ => array()
  7. );

The template for the cache manager itself is shown below, and is saved inside of CacheManager.php in the /system/application/hooks directory.

  1. class CacheManager {
  2.     function do_cache() { }
  3. }

do_cache is the method that gets called at the beginning when the system is loaded. So far this is pretty basic and can be taken directly from the Code Igniter manual entry on hooks. The issue we have here is that since Code Igniter itself is not yet created or prepared, we cannot use any of its handy library loading functions to load Smarty and check for the existance of a cached version of the requested page. In fact, we can’t even use Code Igniter to parse the URL and figure out which which page we are checking for the cache on. If I had written a generic cache manager that could apply to all web sites automatically, I could have just provided the file and would have no need for this explanation, so unfortunately, you have to read this tutorial and then build your own cache manager for your particular website.

For this example, we are going to assume a basic blog application. We have a front page accessible through http://myserver/, we have individual blog entries accessible through http://myserver/story/The_Title_Of_The_Article, and we have an admin section where we post and edit stuff accessible from http://myserver/admin. We want the blog entries that other people can see to be cached, so that if we have a popular story, it does not bring our web server to its knees. The admin section will not be cached, because (a) we want only ourselves to see it when we log in, and (b) it is not high traffic enough to warrant the complexity of caching.

The first thing we need to do is load up the Smarty library itself. Since we cannot use Code Igniters methods, we have to load it directly. If you followed the instructions from here for using Smarty with Code Igniter, then the following code will work to load Smarty. It may need tweaking if you have a different method for using Smarty.

  1. $file = dirname(__FILE__).‘/../libraries/Smarty-2.6.18/libs/Smarty.class.php’;
  2. require $file;
  3. if ( !defined(‘BASEPATH’) )
  4.     define(‘BASEPATH’, dirname(__FILE__).‘/../../’;
  5. class CacheManager {
  6.     function do_cache() {
  7.         // Turn on Smarty
  8.         $s = new Smarty();
  9.         $s->caching = true;
  10.         $s->cache_dir = BASEPATH . ‘cache/smcache/’;
  11.         $s->template_dir = BASEPATH . ‘application/views/’;
  12.         $s->compile_dir = BASEPATH . ‘cache/’;
  13.     }
  14. }

Code Igniter already has directory for some caching, but it is limitted to compiling PHP and Smarty templates and such, so I created a directory inside it to hold the results of static HTML caching. So we now have a web server writable directory in /system/cache/smcache/. The above code loads Smarty and sets up all of the proper paths (the defaults used by the previously mentioned Smarty library for Code Igniter). It also turns on caching. Next we need to parse the URL. Since all of this work will be done behind the rewrite rules, we are guaranteed to have an index.php somewhere in the URL, and we can use this to locate ourselves in the URL. We add code to do_cache to make it look like this:

  1. $file = dirname(__FILE__).‘/../libraries/Smarty-2.6.18/libs/Smarty.class.php’;
  2. require $file;
  3. if ( !defined(‘BASEPATH’) )
  4.     define(‘BASEPATH’, dirname(__FILE__).‘/../../’;
  5. class CacheManager {
  6.     function do_cache() {
  7.         // Turn on Smarty
  8.         $s = new Smarty();
  9.         $s->caching = true;
  10.         $s->cache_dir = BASEPATH . ‘cache/smcache/’;
  11.         $s->template_dir = BASEPATH . ‘application/views/’;
  12.         $s->compile_dir = BASEPATH . ‘cache/’;
  13.         // Parse the URL into sections.
  14.         $fields = explode(‘/’, PHP_SELF);
  15.         $base = array_search(‘index.php’$fields) + 1;
  16.     }
  17. }

This breaks the URL down into sections, finds index.php and then starts looking one step ahead of it. If our base URL is http://myserver/index.php, we will then have whatever comes after index.php be the base. For the home page (/), the rest of the array will be empty. For an individual story, we will have two more entries, one with “story” and one with the title of the story. For the admin page, we will have one or more entries, with the first entry being “admin”. By our schema, if we are looking at “admin”, then we are not caching at all. If we are looking at an empty entry, then we know we are on the front page and it is a basic cache. If we have “story” as the entry, we are caching each story individually and the unique ID for the cache entry will be the title itself. With this as the basis, we will have the following code:

  1. $file = dirname(__FILE__).‘/../libraries/Smarty-2.6.18/libs/Smarty.class.php’;
  2. require $file;
  3. if ( !defined(‘BASEPATH’) )
  4.     define(‘BASEPATH’, dirname(__FILE__).‘/../../’;
  5. class CacheManager {
  6.     function do_cache() {
  7.         // Turn on Smarty
  8.         $s = new Smarty();
  9.         $s->caching = true;
  10.         $s->cache_dir = BASEPATH . ‘cache/smcache/’;
  11.         $s->template_dir = BASEPATH . ‘application/views/’;
  12.         $s->compile_dir = BASEPATH . ‘cache/’;
  13.         // Parse the URL into sections.
  14.         $fields = explode(‘/’$_SERVER[‘PHP_SELF’]);
  15.         $base = array_search(‘index.php’$fields) + 1;
  16.         // Base case assuming that nothing beyond index.php is provided
  17.         if ( count($fields) <= $base )
  18.             $fields[$base] = ;
  19.         // Ignore these entries, since they are not cached
  20.         if ( in_array($fields[$base], array(‘admin’)) )
  21.             return;
  22.         // If we have nothing, then this entry is cached with no cache id
  23.         if ( $fields[$base] ==  ) {
  24.             $page = ‘home.tpl’;
  25.             $id = NULL;
  26.         }
  27.         // If we are looking at a story, then entry is cached with a cache id
  28.         if ( $fields[$base] == ‘story’ ) {
  29.             $page = ‘story.tpl’;
  30.             $id = $fields[$base + 1];
  31.         }
  32.         // Check if we have the entry cached
  33.         if ( $page && $s->is_cached($page$id) ) {
  34.             $s->display($page$id);
  35.             exit;
  36.         }
  37.     }
  38. }

From this code we basically have the entirety of the schema. It figures out which template and cache id to use and then checks if it is already cached. If it is, then it displays the cache and exits immediately. If it is not cached, or we do not try to cache it, then it simply returns and Code Igniter continues loading and operating as normal. There are two more parts to make the caching work. We need to make sure caching is enabled in the Smarty object that Code Igniter will use, and we need to generate the cache files. Cache file generation is exactly the same as it normally is with Smarty; the cache is generated when you call $smarty->display(‘page.tpl’, ‘cache_id’), and the id of the cache entry is optional. For a more in depth read on this last part, you can read the Smarty documentation. As for turning on caching in Smarty itself, we need to modify the Mysmarty library that is included from Code Igniter. If you followed the instructions from http://devcha.blogspot.com/2007/12/smarty-as-template-engine-in-code.html for installing Smarty with Code Igniter, then open up /system/application/libraries/Mysmarty.php and below the $this->compile_dir declarations, add in the following lines:

  1. $this->caching = false;
  2. $this->cache_dir = (!emptyempty($config[‘smarty_cache_dir’]) ?
  3.                            $config[‘smarty_cache_dir’]
  4.                          : BASEPATH . ‘cache/smcache/’);

This will configure the caching to use the same directories as described above. If you have used a different method for combining Smarty with Code Igniter, then you are basically on your own, although the same changes will apply.