Easy Cache Configuration With StorageCacheAbstractServiceFactory

If you’ve been playing with Zend Framework 2 for some time, specifically the ZF2 Skeleton Application, you still may not have come across some of the pre-registered service manager abstract factory options.

As I was browsing through the Application module’s module.config.php recently, I came across this snippet

'service_manager' => array(
    'abstract_factories' => array(
        'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
        'Zend\Log\Logger\AbstractServiceFactory',
    ),
)

It was at that point I wondered why I’d spent time setting up caching using other methods, when this approach was already there and seemed to do a lot of the heavy lifting for me.

So in this week’s tutorial, I’m going to take you through how to use it, working with the default configuration provided in the manual.

Setting Up The Configuration

In the manual, specifically in the default services section, you’ll find the below configuration option.

 array(
        'Cache\Transient' => array(
            'adapter' => 'redis',
            'ttl'     => 60,
            'plugins' => array(
                'exception_handler' => array(
                    'throw_exceptions' => false,
                ),
            ),
        ),
        'Cache\Persistence' => array(
            'adapter' => 'filesystem',
            'ttl'     => 86400,
        ),
    ),
);

This will set up two caches:

  • Cache\Transient: which connects to a faster, yet potentially volatile, remote (or local) Redis server, using the default configuration of localhost and port 6379
  • Cache\Persistent: which uses the slower, yet less volatile, local filesystem

This can be quite handy and is a bit reminiscent of the 2-stage slow/fast cache configuration, available in Zend Framework 1.

When you have this configuration available, you can retrieve a cache object simply by referring to it in a controller, factory or abstract factory class, as we’ll see soon.

This is because Zend\Cache\Service\StorageCacheAbstractServiceFactory uses the available storage adapters, along with their accompanying options classes, in vendor/zendframework/zendframework/library/Zend/Cache/Storage/Adapter, to manage instantiating a cache object.

There’s a number of storage adapters available, including the following:

  • Apc
  • Blackhole
  • Dba
  • Filesystem
  • Memcache
  • Memory
  • Redis
  • Session
  • WinCache
  • XCache
  • ZendServerDisk
  • ZendServerShm

The benefit of this is that you don’t need to worry about setting it up for yourself, creating custom solutions and configurations. However, if your option of choice isn’t listed, you’ll have to find or create a storage adapter and options class for it.

So let’s step through the process of configuring it. I’ll assume that you already have a project based on the ZF2 Skeleton App repository, that it’s running without configuration issues and that the default Application module’s in place with no major changes.

You’ll need a Redis server for this tutorial. If you don’t have one setup, then either add one to your environment or for a minimum of effort, check out the excellent Laravel Homestead project.

This is an excellent project, which provides a simple, effective and properly working virtual machine designed with PHP developers in mind. I’ll leave you to read over the documentation, but out of the box, it comes with:

What’s more, it has a really, really, simple configuration file and is nicely configured out of the box.

The Cache Configuration

The first thing you want to do is to add the configuration above to a new file called caches.local.php (or caches.global.php) in the config/autoload directory.

That basic configuration will provide for two cache objects. Rename them if you like and feel free to add more or remove one as best you see fit.

Then you need to add in a bit more information, if you’re going to use the Cache\Persistence option. If you attempt to use it as is, it won’t work, as there’s no default directory set in the Filesystem class.

So after ‘ttl’ add in the following extra configuration:

'options' => array(
    'cache_dir' => __DIR__ . '/../../data/cache/'
)

This assumes that you have a directory structure of data/cache off the root of your project directory. If you don’t, or it’s located somewhere else, then change it to suit your needs.

The Cache\Persistence option will work fine, so long as your Redis server’s located on localhost and using the standard port of 6379. If it’s not, you’ll need to add in an extra configuration as follows:

'server' => array(
    'host' => 'localhost',
    'port' => '6379',
)

This took a little bit of hunting through the classes to find. In Zend\Cache\Storage\Adapter\RedisOptions you’ll find a function called setServer, where you’ll see other configuration options.

Accessing The Cache Service

Directly In A Controller

Now I don’t overly advocate this way, but will cover it just so that it’s documented. The simplest and least portable (and documentable) approach is to use the following in a controller action.

$this->getServiceLocator()->get('Cache\Persistence')

This way, whilst easy and quick as well as handy for rapid prototyping isn’t a good solution long term. So please consider the next approach.

Using Dependency Injection

This is my preferred method. Following the previous tutorial on using either ServiceManager factories or abstract factories, in the createServiceWithName method, instantiate your controller as follows:

$sm = $serviceLocator->getServiceLocator();
$cache = array();
if ($sm->has('Cache\Persistence')) {
    $cache['CachePersistence'] = $sm->get('CachePersistence');
}

if ($sm->has('Cache\Transient')) {
    $cache['CacheTransient'] = $sm->get('CacheTransient');
}

$controller = new YourController($cache);

This checks if the two cache items are available and if so, adds them to an array, called $cache, which is then passed to the YourController’s constructor.

Then in the controller use a constructor like the following:

public function __construct($cache = null)
{
    if (!is_null($cache)) {
        $this->cache = $cache;
    }
}

Assuming that you already have a class member variable, called cache, this will initialise it, if the cache argument is set, but not force it to be set.

Using The Cache

After all that’s done, let’s look at the code to use the cache. Taking the simple example below, we’ll store some facts about one of the greatest movies of all time, Ghostbusters!

$cacheObj = $this->cache['Cache\Persistence'];
if (!$cacheObj->hasItem('Ghostbusters')) {
    $cacheObj->addItem('Ghostbusters', 'Harold Ramis Rocks!!!');
} else {
    print $cacheObj->getItem('Ghostbusters');
}

That’s it. You can then use the cache as you would normally, calling getItem, setItem, addItem, hasItem and so on.

Wrapping Up

And that’s one of the simplest ways I’ve found to configure and use caching in Zend Framework 2 applications. Have you tried it already? Share your thoughts in the comments.

ServiceManager