Caching Smarty Database Templates

The Smarty manual explains how to use Templates from other sources such as databases. It doesn’t however give an example of caching templates from these sources. I ran into a spot of trouble when I tried to be clever about checking the freshness of a db based template.
I didn’t want to interogate the db on every request just to check if a template is stale or not. Why have caching when you have to hit the db anyway? The db_get_timestamp() function has to be modified to check the freshness in some other way then.
I used a 0-byte file in a web-server writeable directory. Unfortunately it didn’t work. The is_cached() function would return false and I’d assign Smarty variables to prepare a new version of the web page, but when I called the display() function it displayed the last cached version!
It took me a while to figure out that both is_cached() and display() call the db_get_timestamp() function. As the file I used to flag a refresh was deleted on the first run I had to retain the timestamp somehow. That’s where the global $filetimestamp array comes in.

  1. is_cached() checks the freshness of the cache and returns false.
  2. I prepare the new Smarty variables.
  3. The page is displayed using the Smarty display() function
  4. Use touch scratch/index.tpl to update the template cache.
function db_get_timestamp($tpl_name, &$tpl_timestamp, &$smarty_obj)
{
  global $filetimestamp;

  if( is_file( 'scratch/' . $tpl_name ) == false )
  {
    if( $filetimestamp[ $tpl_name ] != '' )
    {
      $tpl_timestamp = $filetimestamp[ $tpl_name ];
    }
    else
    {
      $tpl_timestamp = mktime (0,0,0,date("m")-1,date("d"), date("Y"));
    }
    return true;
  }
  else
  {
    $tpl_timestamp = filemtime( 'scratch/' . $tpl_name );
    $filetimestamp[ $tpl_name ] = $tpl_timestamp;
    unlink( 'scratch/' . $tpl_name );
  }
  return true;
}
if( $smarty->is_cached( 'db:index.tpl' ) == false )
{
  print "regenerating cache!<br>";
  $smarty->assign( 'date', time() );
}

$smarty->display("db:index.tpl");

PHP – oo stuff

BDKR has a rant here about OO in PHP. I agree with some of what he says. There’s lots of bad code and design out there (I can’t promise to be perfect either!) but that permeates functional and procedural programming too.
I happen to like working with objects and structuring code in that way. It was a hell of a thing to get used to in college though, Before that I had done a lot of work coding in ASM on the C64. Actually having structures to work with was a real boon!
On a related topic, John mentioned a ObjectView, a PDF magazine about OO for developers. May take a look at it later.

PHP 4.3.2 is out

A new release of PHP is out today. It looks like a required update with a HUGE amount of other bug fixes!
Update! I upgraded 2 servers. One with Apache 1.3.27, php_accelerator went fine, but the other with mod_ssl, mod_gzip and php_accelerator refused to work. Every request resulted in a seg fault. I compiled PHP with mm support for mod_ssl (is that necessary?) but haven’t got time to track down where the crash occurs. (I tried disabling both mod_gzip and php_accelerator with no luck)
I’ll give it another go later.

PHP: Process Control Functions

I had quite forgotten that PHP can fork a process just like in C! This might be a useful thing to know for doing background tasks, but to be honest the extra complexity required will probably put me off doing it. That’s where this Thread class comes in handy..

<?php
include ("threadClass.php");

class testThread extends Thread {

  function testThread($name) {
    $this->Thread($name); // calls the parent constructor and assign its name
  }

  function run() {
    while(true) {
      sleep(1);
      print time() ."-" . $this->getName() . " said ok...\n"; // every second we're going to print this line...
    }
  }
}
// Main program. Bring up two instances of the same class (testThread).
// They runs concurrently. It's a multi-thread app with a few lines of code!!!

$test1 = new testThread ("Thread-1");
$test2 = new testThread ("Thread-2");
$test1->start();
$test2->start();

print "This is the main process. Press [CTRL-CANC] to terminate.\n";
while(true) {sleep(1);}

?>

More Benchmarks: FastCGI is faster than ISAPI/CGI on IIS

John conducted benchmarks the other day and found that IIS/WinXP can be faster than Apache /Linux. His initial results show a huge difference, but later he found the difference was only 10%.
What difference would running a PHP accelerator make to the test? That may very well swing the results back in favour. In my own tests, adding a PHP accelerator makes an undeniable difference in speed tests. The acccelerator makers would have you believe the speed up is 10 times, but it’s at least 5-6 times.
How stable is IIS these days? I’ve heard it’s much better than it was, but is it more stable than Apache (1.3 or 2?) on Windows?

Maybe we’re missing an important point here though. Many managers want to use Microsoft in their shops. If they have IIS, this is one very good way of running PHP in that environment without being penalised by cgi performance. Bravo John for doing the tests, I’m certain your site will popup in a Google search when some MSCE is desperately trying to figure out what technology to use! We’re get them to use PHP first, and then when they’re comfortable with this open source lark, move their Win32 servers to Linux or some other Free Unix! *grin*

MVC, PHP and Smarty Caching…

Moving a purely procedural program to an OO design is a mammoth task, especially when that program is several years old and had bits added on over the while.
I’ve pruned out a lot of the extra features that are not needed now, and some that are simply silly, but now I’m coming to a stumbling block.

Smarty can cache content. The beauty of that is I can avoid a whole load of expensive operations like database queries, information lookups from remote sources, code loops, etc. In an MVC application, where should the check for cache freshness be?

  • Should it be in the controller class so it can call the functions of the model to create the view?
  • Should it be in the model, so the controller doesn’t have to know about the model? The model is where the expensive operations are anyway.
  • Should it be in the view, because that’s the locality of freshness information? Isn’t caching an element of the display procedure?

If it’s in the controller, then the controller has to know about caching, is that bad?
If it’s in the model, then the controller is ignorant, and therefore not a controller.
If it’s in the view, the controller is ignorant, and the view is now “controlling” if functions are called in the model.
You can see where my reasoning is going with this..
The controller should probably have access to the cache information. It shouldn’t talk directly to the Smarty instance in the view however. It should talk to a function that takes data from the model, checks the cached views against that data and any timeouts and returns true or false.
For example:

class exampleController
{
  function exampleController()
  {
    // skip unimportant bits.
    // create model and view
    if( $this->view->is_cached() == false )
    {
      // do some expensive operations in
      // the model to create the view.
    }
  }
}

Or should that is_cached() function be in the model? Please comment if you have any insights!