Function output caching with PHP

I've been working on optimizing of a system written in PHP, which started to perform slowly, and I had to implement content caching so I could, say, calculate access stats once a day and then just output cached content to the user, or have top page regenerated every 15 minutes.. Stuff like that.

I thought I'd share my approach (which may not be the best around, but is a working one and can save somebody some time). So here goes….

First, we need to create a table to store cached information (I store cached information as well as system-wide settings in that table)

CREATE TABLE `settings` (
`key` varchar(250) NOT NULL default ",
`data` text NOT NULL,
PRIMARY KEY (`key`)
)

Next, we need accessors to the data in the settings table (just put these functions somewhere accessible by your code):

function settingsGet($key)
{
$key = addslashes($key);
$qstring = "select * from settings where `key`='$key'";
$result = queryDB($qstring);
$stuff = mysql_fetch_array($result);
return $stuff[data];
}
function settingsSet($key, $value)
{
$key = addslashes($key);
$value = addslashes($value);

$qstring = "select * from settings where `key`='$key'";
$result = queryDB($qstring);
if (mysql_num_rows($result)>0) {
//updating
$qstring = "update settings set data='$value' where `key`='$key'";
} else {
//inserting new
$qstring = "insert into settings (`key`, data) values ('$key', '$value')";
}
queryDB($qstring);
}

Next, we'll setup function to get and set cached data, which in turn use settings table accessors we created above. Again, just place them anywhere accessible by your code.

function get_cachedData($data_id, $expireSeconds = 3600) {
//expiring every hour by default

$data = unserialize(settingsGet("data_cache_{$data_id}"));

$lag = time() - $data["ts"];

if ($lag > $expireSeconds) {
return false;
}
return $data[value];
}
function set_cachedData($data_id, $value) {
$ts = time();
$data = array();
$data["ts"] = $ts;
$data["value"] = $value;
settingsSet("data_cache_{$data_id}", serialize($data));
}

Basically, we just use (hard-coded) prefix "data_cache_" to store data in the settings table (useful if you store something else in the settings table as well), as well as "data_id" variable by which you identify the "name" or id of data you wish to cache.

We will also need some nice wrappers:

function startCaching($data_id) {
if ($data = get_cachedData($data_id)) {
echo $data; return true;
}
ob_start();
return false;
}

function endCaching($data_id) {
$data = ob_get_contents();
ob_end_clean();
set_cachedData($data_id, $data);
echo $data;
}

Usage example:

Before caching:

function print_access_rating() {
/*
some code which requires a lot of time to get generated
*/
}

After caching was added:

function print_access_rating() {
if (startCaching("xxxxxxxx)) return;
/*
some code which requires a lot of time to get generated
*/
endCaching("xxxxxxxx");
}

The "xxxxxxxx" would identify the chunk of data you wish to cache. Say, you want to cache latest news section on your top page. For the forementioned usage example you can use print_access_rating as the data_id parameter. Or you can just pass the __FUNCTION__ php 's variable which is always set to the name of current function, if you want.

That's basically all. Please notice that you can use optional second argument of get_cachedData function to set number of seconds for which to cache a particular chunk of data.

Don't know how usable it will be for anyone except for myself, but here it is.. :) Modify to suit your needs.

Leave a Reply