I'm currently wrapping up a fairly large Zend Framework-driven project that mines data from the Amazon Product Advertising API. According to the API license agreement, this data can be cached for a period of up to 24 hours. Therefore, I've created several database tables to store the data, and I employ a number of CRON-driven scripts to rebuild the data store once daily.
However, because the product data will not change during that 24-hour period, it doesn't make sense to repeatedly execute the fairly complex JOIN queries and looping statements required to retrieve and format the data. Instead, I used the Zend_Cache component to cache this data, expiring the cache just ahead of the daily update. In this article I'll show you how to use Zend_Cache to cache your own data, considerably boosting your website performance in the process.

Configuring Zend_Cache

Zend_Cache can be configured to cache several types of output, including the results of function calls, the results of object and static method calls, entire pages, and configuration data. You determine what data is cached using Zend_Cache's frontend. This data can be cached in several ways, including using system memory (RAM), using memcached, within text files, or within a SQLite database. You determine exactly how your data is cached using Zend_Cache's backend.
The Zend_Cache frontend and backend are each configured separately, using a variety of configuration options that you can review by clicking on the aforementioned links, respectively. One of the most important frontend configuration options is the cache lifetime, defined in seconds with a default of 3,600 (one hour). I wanted the data to be cached for 24 hours and 10 minutes (allowing an additional five minutes for the API update to take place, just to be safe). So I configured my Zend_Cache frontend like this:
$frontendOptions = array( 'lifetime' => 1450, 'caching' => true, 'automatic_serialization' => true );
The caching option determines whether caching is enabled. Although not required (it is set to true by default), I include the option in order to easily disable caching during the development phase. The automatic_serialization option must be set to true if you plan on caching data that isn't a string, such as an array or object. I've set this to true because I'm caching arrays of objects.
I'm using text files to cache the data, and so configure my Zend_Cache backend like this:
$backendOptions = array( 'cache_dir' => '/var/www/beta.example.com/cache/' );
Make sure your Web server possesses the necessary permissions to write to/read from the designated cache directory.
Of course, in my actual application I retrieve these configuration values from the application's application.ini file in order to ensure maximum flexibility when migrating the site from the development server to the production server. See my earlier article, Introducing the Zend Framework's Application Configuration Component, for more information about this powerful Zend Framework feature.

Caching Database Results

With the frontend and backend caching configured, you can then declare the types of frontend and backend caches you want to use and begin caching your data. You complete the first task by creating a new Zend_Cache object using its factory method and then passing in the frontend and backend cache types and the respective configuration options:
$cache = Zend_Cache::factory( 'Core', 'File', $frontendOptions, $backendOptions );
Caching works by assigning a unique token to each cached result. You can then use Zend_Cache methods to refer to this token and determine whether it's expired. Therefore, when using Zend_Cache you'll first attempt to load the cached data using the load() method, which will return the cached data if it hasn't yet expired, or false if it has expired. If the cached data has expired, you'll execute whatever mechanism is necessary to rebuild the data, subsequently caching it anew. Keeping this sequence of events in mind, the typical database query result caching process looks like this:
$frontendOptions = array( 'lifetime' => 1450, 'caching' => true, 'automatic_serialization' => true ); 
$backendOptions = array( 'cache_dir' => '/var/www/beta.example.com/cache/' ); 
$cache = Zend_Cache::factory( 'Core', 'File', $frontendOptions, $backendOptions ); 
if ( ($gamesPaginator = $cache->load('games')) === false) {
$gamesPaginator = $platform->retrieveGames($currentPage, $resultsPerPage, $sort); $cache->save('games'); }
In this example, Zend_Cache looks to the cache directory for a cache file associated with the unique token games. If the file exists and has not expired, its contents will be retrieved, unserialized and assigned to the variable $gamesPaginator. Otherwise, my $platform model will retrieve a paginated list of the video games associated with it. That retrieved list is saved to the cache using the games token.

Caching Paginated Results

I simplified the previous example somewhat so as not to get lost in myriad unrelated details. However, now that you understand how the general caching process works, let's dive a bit deeper into how I go about caching paginated data. Because each page within the paginated list is of course unique, repeatedly caching the data using the games token isn't acceptable. Instead, I use the page number to create a dynamic token, like this:
$cacheToken = $this->platform->name."_".$this->page;
Therefore, every time the page changes, the cache token will be updated accordingly, creating a new cache file (e.g. xbox_1, xbox_2, xbox_3...). This token is then passed into the load() method just as before:
... if ( ($gamesPaginator = $cache->load($cacheToken)) === false) { ...

Caching Resources

If you're building a website that will receive any significant amount of traffic, then caching is one of the most effective and easiest ways to boost performance and keep your users happy. Be sure to check out the following resources for more information:

About the Author

Jason Gilmore is the founder of the publishing and consulting firm WJGilmore.com. He also is the author of several popular books, including "Easy PHP Websites with the Zend Framework", "Easy PayPal with PHP", and "Beginning PHP and MySQL, Fourth Edition". Follow him on Twitter at @wjgilmore.