Creating an Extensible Caching System in PHP

In this part of the series, I demonstrate that the use of the “Plug-in” pattern in the construction of an extensible caching system is actually much simpler than many people might think. [Editor's note: this article was published out of sequence; it's actually the third in the series, and comes before yesterday's "HTML5 Client-Side Cache in PHP."]

If you’re looking for an approach that lets you build highly-scalable applications without having to dive into complex programming concepts, take a peek at the “Plug-in” pattern. This simple, yet powerful programming methodology will permit you to develop truly “pluggable” programs, whose functionality can be easily extended by means of loosely-coupled modules.

What’s more, implementing the model imposed by the “Plug-in” pattern is so simple that it only requires an average background in the foundation and key concepts of object-oriented programming, such as Inheritance, Composition and interfaces. That’s all that you’ll need to know to get the pattern up and running.

Trying to sell you on the benefits of “Plug-in” is a rather pointless task if I don’t show you some functional code samples that demonstrate how to use this paradigm in some concrete cases. In line with this idea, in a previous installment of this series I developed a basic PHP application. It was responsible for rendering different elements on the browser, such as divs, paragraphs and even some JavaScript alert boxes.

Beyond its simplicity, the real beauty of this example relied on the ability of the application to display all sorts of “renderable” objects on screen without having to amend a single section of its source code. Now do you see how easy it is to build extensible PHP programs with the “Plug-in” pattern? I bet you do.

It’s fair to point out, however, that the “Plug-in” approach can be used in situations that resemble more realistic environments. In keeping with this concept, in this third chapter of the series I’m going to start building a simple caching system, which will be able to swap easily between several “pluggable” cache back ends.

Does my proposal sound engaging enough for you? Then jump in and begin reading! 

Start building the sample cache system: defining a caching interface

Even though the idea may sound strange at first, it’s possible to take advantage of the functionality provided by the “Plug-in” pattern and create an caching system capable of swapping different cache back ends at runtime, pretty similar to what many frameworks will let you do nowadays (i.e. Zend Framework, Symfony, CodeIgniter and so forth).   

So, the first step I’m going to take toward the implementation of this “pluggable” caching system will consist of defining an abstract cache backend. This backend, not surprisingly, will be an interface called “Cacheable,” whose definition is shown below:

(Cache/Cacheable.php)

<?php

namespace Cache;

interface Cacheable
{
 public function set($key, $data);
 public function get($key);
 public function delete($key);
 public function clear();     
}

As you can see from the code fragment above, the “Cacheable” interface establishes a contract that must naturally be fulfilled by all of its eventual implementers. The contract is made up of the “set(),” “get(),” “delete()” and “clear()” methods, which define the behavior of a generic cache backend (actually, I could have added a few more, but to keep things clear and understandable, I’m happy to have only the existing ones).

So far, so good. Having already defined an abstract cache backend, the next logical step is to build a concrete one. In this case, this concrete cache will be a simple wrapper for the APC PHP extension, and in the forthcoming section I’ll be showing its corresponding source class.

So, if you want to see how this APC wrapper will be implemented, jump ahead and keep reading.

{mospagebreak title=Building an APC wrapper}

As you may have already guessed, building a wrapper class for the APC PHP extension is a pretty straightforward process. The most relevant point to stress here is that this wrapper must be an implementer of the previous “Cacheable” interface, so it can be easily swapped at runtime by client code consuming different cache back-ends.

Having clarified that concept, the APC class looks like this: 

(Cache/ApcCache.php)

<?php

namespace Cache;

class ApcCache implements Cacheable
{
    /**
     * Save data to the cache
     */
    public function set($key, $data)
    {
        if (!apc_store($key, $data)) {
            throw new ApcCacheException(‘Error saving data with the key ‘ . $key . ‘ to the cache.’);
        }
        return $this;
    }
   
    /**
     * Get the specified data from the cache
     */
    public function get($key)
    {
        if ($this->exists($key)) {
            if (!$data = apc_fetch($key)) {
                throw new ApcCacheException(‘Error fetching data with the key ‘ . $key . ‘ from the cache.’);
            }
            return $data;
        }
        return null;
    }
   
    /**
     * Delete the specified data from the cache
     */
    public function delete($key)
    {
        if ($this->exists($key)) {
            if (!apc_delete($key)) {
                throw new ApcCacheException(‘Error deleting data with the key ‘ . $key . ‘ from the cache.’);
            }
        }
        return $this;
    }
   
    /**
     * Check if the specified cache key exists
     */
    public function exists($key)
    {
        return apc_exists($key);
    }
   
    /**
     * Clear the cache
     */
    public function clear($cacheType = ‘user’)
    {
        return apc_clear_cache($cacheType);
    }       
}

 

(Cache/ApcCacheException.php)

<?php

namespace Cache;

class ApcCacheException extends Exception{}

If you’ve ever worked with the APC extension, then the definition of the above “Apc” class should be pretty familiar to you. As I just mentioned, this class is nothing but a basic wrapper for the most important functions included with the extension, which provides enough functionality for saving, fetching and deleting data from the opcode cache. Of course, it’s possible to improve the implementation of this class; in this case, however, I’m going to keep it simple, so that you can more quickly grasp how it works.

Although it’s valid to note that this concrete cache backend can be used perfectly as a standalone component, my goal here is to demonstrate how to utilize it as a “plug-in.” To do so, it’s necessary to define a client class capable of injecting different cache back-ends according to specific requirements. Given that, in the next segment I’m going to define such a client, which as you’ll see in a moment, will consist of a simple cache manager class.

To learn the full details regarding the definition of this cache manager, just keep reading.

Managing different cache back-ends at runtime: developing a cache handler class

While the name may sounds somewhat intimidating, the truth is that building a “cache manager” class is an extremely simple process that can be accomplished in a snap. The only requirement that this class must meet is that it must be capable of working with multiple cache back-ends without having to modify its original implementation.

Thanks to the model imposed by the “Plug-in” pattern, this can be easily achieved by combining the power of Composition (read Dependency Injection) with the one provided by the “Cacheable” interface previously defined.  

If this explanation doesn’t ring any bells for you yet, take a look at the source code of the following cache handler class; it will hopefully clear things up for you:

(Cache/CacheHandler.php)

<?php

namespace Cache;

class CacheHandler
{
    protected $_cache;
   
    /**
     * Constructor
     */
    public function __construct(Cacheable $cache)
    {
        $this->_cache = $cache;
    }
   
    /**
     * Add the specified data to the cache
     */
    public function set($key, $data)
    {
        return $this->_cache->set($key, $data);
    }
   
    /**
     * Get the specified data from the cache
     */
    public function get($key)
    {
        return $this->_cache->get($key);
    }
   
    /**
     * Delete the specified data from the cache
     */
    public function delete($key)
    {
        $this->_cache->delete($key);
    }         
}
 
There you have it. As you can see from the above code sample, the “CacheHandler” class takes through its constructor a generic cache backend, which is first stored as a protected property and then used within its “set(),” “get()” and “delete()” methods. Even though it’s simple, this implementation is very flexible because it permits you to use different cache back-ends, as long as they implement the  “Cacheable” interface.

Finally, here’s an example that shows how to use this cache handler together with the “Apc” class shown before (don’t worry about the definition of the autoloader below, as it’s the same one we’ve used earlier in this series): 

// include the autoloader
require_once ‘Autoloader.php';
Autoloader::getInstance();

use CacheApcCache as ApcCache,
    CacheCacheHandler as CacheHandler;

// cache some data using APC
$cacheHandler = new CacheHandler(new ApcCache);
$cacheHandler->set(‘fname’, ‘Julie’)
             ->set(‘lname’, ‘Smith’)
             ->set(‘email’, ‘julie@domain.com’);
            
// display the cached data
echo ‘ First Name: ‘ . $cacheHandler->get(‘fname’) .
     ‘ Last Name: ‘ . $cacheHandler->get(‘lname’) .
     ‘ Email: ‘ . $cacheHandler->get(‘email’);

As this example shows, saving and fetching some data from the opcode APC cache is ridiculously simple. Nevertheless, the most important detail to note here is that the APC wrapper can easily be substituted with another cache “plug-in,” be it one that uses the file system or one that relies on shared memory. This is precisely the advantage of using the “Plug-in” pattern!

But I’m getting ahead of myself, since the construction of an additional cache backend will be covered in depth in the next tutorial. Meanwhile, feel free to introduce your own tweaks and improvements to all of the sample classes defined so far, something that will help you acquire a better understanding on this pattern.

Final thoughts

In this third installment of the series, I demonstrated that the use of the “Plug-in” pattern in the construction of an extensible caching system is actually much simpler than many people might think. In fact, to make my point and demonstrate that the system is really “pluggable,” it’s necessary to create another cache backend, aside from the basic APC wrapper that you saw before.

Well, that’s exactly what I will do in another part of the series. This new cache backend will use the features provided by the flashy local storage mechanism included with HTML5.

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort