Generic caching function for Drupal 6

One of the pieces of the Drupal performance puzzle is caching. After weeding out a couple of very demanding sql queries in a busy Drupal site, we decided to make more use of Drupal's caching mechanisms to cut down some of the database load.

Back in 2007, Lullabot's Jeff Eaton wrote a blog post about caching including a sample implementation for a caching function. This function was almost exactly what we wanted, but we wanted something more generic—a function that we could use in multiple places in the same module or template.php file without modification.

But in order to use one function to populate and retrieve different cache entries, we needed a way to call an arbitrary function from inside the caching function. Fortunately, PHP supports variable functions. This meant that we could pass the name and parameters of an arbitrary function to our caching function and, when necessary, populate the cache with the output from that function (alternatively, we could also have used the call_user_func() function or, like Drupal's own theme() function, the func_get_args() function).

 * This function returns the cached value for a given cache entry. If the entry does not exist, or if
 * the entry is empty, this function executes the passed function, caches the result and returns it.
 * @param string $cache_entry The full name of the cache entry--remember the namespace!
 * @param array $function An array with two keys:
 *			string $function['name'] The name of the function
 *			string $function['parameters'] A comma-separated list of parameters
 * @param string $table The name of the cache table to use (see cache_set())
 * @param CACHE_PERMANENT|CACHE_TEMPORARY|unix timestamp $expire Expiry period for this cache entry
 * @param boolean $reset If true, regenerate cache entry; if false, check first
 * @return string|array|object $cache_data The contents of the relevant row in the cache table
 * @see <a href="
</a> * @see <a href="
</a> */
function theme_name_theme_cache($cache_entry, $function, $table = 'cache', $expire = CACHE_PERMANENT, $reset = FALSE) {
  static $cache_data; // Establish the $cache_data variable for this page load...
  if(!isset($cache_data) || $reset) { // If the variable is not set, or if a cache reset has been requested...
  	if(!$reset &amp;&amp; ($cache = cache_get($cache_entry)) &amp;&amp; !empty($cache-&amp;gt;data)) { // If $cache-&amp;gt;data is set and reset not requested...
  		$cache_data = $cache-&amp;gt;data; // Store the current value of $cache-&amp;gt;data...
  	} else { // If we need to (re) generate the cache...
  		$function_name = $function['name']; // Get the function name to call..
  		$function_parameters = $function['parameters']; // Get the parameters for the function...
  		$cache_data = $function_name($function_parameters); // Set cache data to the return value of the function...
  		cache_set($cache_entry, $cache_data, $table, $expire); // Cache the resulting value...
  return $cache_data; // Return the cached value...
} // theme_name_theme_cache()

In the above code sample, we pass an array '$function' with two keys, 'name' and 'parameters' to the function. If the cache is not already populated, we determine the cacheable value with the following line:

$cache_data = $function_name($function_parameters);

We might use this function like this (for illustration purposes only!):

$cache_entry = 'theme_name:website_link';
$function = array(
	'name' => 'l',
	'parameters' => '"Drupal Website", "<a href=""'
$table = 'cache';
$expire = time() + (360*24*7);
$output = theme_name_theme_cache($cache_entry, $function, $table, $expire);

This would result in $output being set to the output of the call to Drupal's l() function, and the cache entry 'theme_name:website_link' in the 'cache' table being set (or updated) to that value, with an expiry time one week in the future.

Of course the use of a function like this&mdash;like the use of the cache in general&mdash;should be limited to queries or calculations that are very resource intensive. The example above would actually add overhead compared to simply calling l() directly.