Consolidating Cache Management in Zend Framework

In version 1.10, Zend added an Application Resource to the Zend Framework for easily setting up your Caching resources through configuration files.

resources.cachemanager.cacheCoreFile.frontend.name = "Core"
resources.cachemanager.cacheCoreFile.frontend.options.lifetime = 3600 
resources.cachemanager.cacheCoreFile.frontend.options.cache_id_prefix = APPLICATION_ENV "_cache_core_file" 
resources.cachemanager.cacheCoreFile.frontend.options.automatic_serialization = true 
resources.cachemanager.cacheCoreFile.frontend.options.automatic_cleaning_factor = 1 
resources.cachemanager.cacheCoreFile.backend.name = "File" 
resources.cachemanager.cacheCoreFile.backend.options.cache_dir = APPLICATION_PATH "/data/cache"

The problem is that components like Doctrine, Zend_Translate, Zend_Db and so on all have directives for their own cache configurations.

This makes managing cache times, invalidations, and timeouts very difficult.

A solution to this is to use the cache manager to create your cache and then pass that cache onto your other components through bootstrap ( see example below ).

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { 
  ... 
  $this->_cache = null; 
  
  protected function _initCache() { 
    $this->bootstrap('cachemanager'); 
    $cache = $this->getResource('cachemanager')>getCache('cacheCoreFile'); 
    $this->_cache = $cache; 
    Zend_Registry::set('cacheCoreFile', $cache); 
  } 

  protected function _initTranslateCache() { 
    $this->bootstrap('cache'); 
    $this->bootstrap('translate'); 
    Zend_Translate::setCache($this->_cache); 
  } 

  protected function _initDoctrineCache() { 
    $this->bootstrap('cache'); 
    $this->bootstrap('doctrine'); 
    $doctrineAdapter = new DoctrineAdapter( 
        $this->_cache , 
        'my_doctrine_cache_prefix'
    ); 
    $manager = Doctrine_Manager::getInstance();
    $manager->setAttribute( Doctrine_Core::ATTR_RESULT_CACHE , $doctrineAdapter ); 
  } 
  ... 
}

Most of the Zend components are pretty straightforward in that they accept an instance of Zend_Cache. Doctrine on the other hand defines its own cache drivers. Thanks to Benjamin Steininger we have a Doctrin_Cache driver implementation for Zend_Cache.

I made a small modification to the "save" method to add some custom cache tags, which I'll explain in a minute.

class DoctrineAdapter implements Doctrine_Cache_Interface { 
  ... 
  public function save($id, $data, $lifetime = false) { 
    $id = $this->_prefix . $id; 
    return $this->_cache->save( 
      $data, 
      $id, 
      array( My_CacheTags::TAG_DOCTRINE ), 
      $lifetime 
    ); 
  } 
  ... 
}

Zend_Cache tags allow for the "categorizing" of cached items.

To make my tag management easier I created a class with all the tags I use as static properties. This also allows me to reflect on the class for building my cache admin interfaces.

class My_CacheTags { 
  const TAG_MODULE_DEFAULT = "TAG_MODULE_DEFAULT"; 
  const TAG_MODULE_OTHER = "TAG_MODULE_OTHER"; 
  const TAG_ACTION = "TAG_ACTION"; 
  const TAG_VIEW = "TAG_VIEW"; 
  const TAG_DOCTRINE = "TAG_DOCTRINE"; 
  // Zend Translate automatically adds it's tag Zend_Translate so we'll use it's tag name 
  const TAG_TRANSLATE = "Zend_Translate";
}

Now, emptying specific groups of items from the cache is much easier since we can specify which tags I want to clean from the cache.

$cache->clean( 
  Zend_Cache::CLEANING_MODE_MATCHING_TAG, 
  array( 
    My_CacheTags::TAG_MODULE_DEFAULT , 
    My_CacheTags::TAG_VIEW
  ) 
);

The set-up described gives me much better insight as to how my application is configured, allows for easier changes, and manages my cache much more simpler.