PHP Dynamic Caching with ZendPlatform

I recently had a little fun playing with the dynamic cache available in the Zend Platform. The Zend Platform is a commercial product, which provides a number of cool professional features to a PHP setup - one of these is the option to do dynamic caching.

With dynamic caching you can cache the output of a function for a determined period and instead of doing database queries or web service calls for a feature, you can cache the results, save resources and get faster pages.

If you have access to a PHP setup with Zend Platform, but haven’t looked at dynamic page caching, here’s a little example to get you inspired and started.

Dynamic Caching Example

Let’s suppose you have a busy news website and publish lists of news from categories - on the front page, along side news articles and several other places. to retrieve the most recent articles for each category, we’ve created a class with a function to retrieve the five most recent stories from a category specified by a parameter.

Our first version may look something like this:

class NewsCategory
{
        public function getCurrent($category)
        {
                $newsLines = Array();
		if (intval($category)) {
                $sql = sprintf('SELECT * FROM news WHERE category="%n"
                                     ORDER BY date LIMIT 5', $category);
                /* Asume connection to dbserver exists and is valid */
                $res  = mysql_query('newsdb', $sql);
                $data = mysql_fetch\_array($res);
                $newsLines = Array();
                while ($newsItem = mysql_fetch_array($res)) {
                        array_push($newsLines, $newsItem);
                }
        }
        return $newsLines;
        }
}

Since news doesn’t happen too often and the lists of news are displayed on many pages, we want to cache the results for an hour - and we want to make sure, that the site works, even if we for some reason decide to disable the Zend Platform.

Rewriting the class to utilize the caching API takes a few changes and the Dynamic Cache-enabled version looks like this:

/*
 * SAMPLE CODE; NOT FOR PRODUCTION USE
 */
class NewsCategory
{
        const CACHE_TIME = 3600; // caching time in seconds. Global caching set to 1 hour.
        const CACHE_KEY  = 'NewsCategory_getCurrent_'; // prefix for caching key

        /**
         * Returns array with current newsitems from the specified category.
         *
         * @param integer $category
         * @return array with news items
         */
        public function getCurrent($category)
        {
                if (function_exists('output_cache_fetch')) { // caching available?
                        return unserialize(output_cache_fetch(
                                self::CACHE_KEY . $category,
                                "serialize(self::fetchCurrent($category))",
                                self::CACHE_TIME));
                } else { // No ZendPlatform Available
                        return self::fetchCurrent($category);
                }
        }

        /**
         * Internal function for looking up the current news identified by $category.
         *
         * @param integer $category
         * @return array - returns empty array if no news is found.
         */
        protected function fetchCurrent($category)
        {
                $newsLines = Array();
		if (intval($category)) {
                $sql = sprintf('SELECT * FROM news WHERE category="%n"
                                     ORDER BY date LIMIT 5', $category);
                /* Asume connection to dbserver exists and is valid */
                $res  = mysql_query('newsdb', $sql);
                $data = mysql_fetch_array($res);
                $newsLines = Array();
                while ($newsItem = mysql_fetch_array($res)) {
                        array_push($newsLines, $newsItem);
                }
        }
        return $newsLines;
        }
}
The changes are:

  • The public function from the first version is replaced by a method, that handles the caching. The function doing the work is now hidden in a protected method in the class.
  • If the caching API isn’t available, the new function just acts a proxy passing data through.
  • The new caching functions creates caching keys from the method name and the parameter to make them predictable and avoid namespace clashes.
  • do notice that the cached results are serialized in the cache - the cache can only store string values.

Before going crazy with Dynamic Caching do consider a few things:

  • Is the data suited for caching? (or do you need the most accurate data every time)
  • What level do you expect the cache ratio to be at? (how often is the cache utilized)
  • Are other caching mechanisms in place? (ie. proxy-servers or likewise)