Fork Me on GitHub

CacheCache

Caching library for PHP 5.3+

Download this project as a .zip file Download this project as a tar.gz file

Installation

The easiest way to install CacheCache is using Composer with the following requirement:

{
    "require": {
        "maximebf/cachecache": ">=0.1.0"
    }
}

Alternatively, you can download the archive and add the lib/ folder to PHP's include path:

set_include_path('/path/to/lib' . PATH_SEPARATOR . get_include_path());

CacheCache does not provide an autoloader but follows the PSR-0 convention.
You can use the following snippet to autoload CacheCache classes:

spl_autoload_register(function($className) {
    if (substr($className, 0, 10) === 'CacheCache') {
        $filename = str_replace('\\', DIRECTORY_SEPARATOR, trim($className, '\\')) . '.php';
        require_once $filename;
    }
});

Usage

The CacheManager

CacheManager is a static class that can be used to initialize and store multiple instances of Cache objects.

Storing and accessing Cache objects:

$cache = new Cache(new Backends\Memory());
CacheManager::set('mycache', $cache);

// ...

$cache = CacheManager::get('mycache');

Cache objects can be created and initialized using an array with the factory() method. The following options can be defined:

Example:

$cache = CacheManager::factory(array(
    'backend' => 'CacheCache\Backends\Memcache',
    'backend_args' => array(array(
        'host' => 'localhost',
        'port' => 11211
    ))
));

Finally, multiple Cache objects can be created at the same time using the setup() method. It takes as first parameter an array of key/value pairs where keys will be used as the cache name to be used with the get() method and values are an array to be used with factory(). The second argument can be a Monolog\Logger instance to enable logging.

CacheManager::setup(array(
    'array' => 'CacheCache\Backends\Memory',
    'memcache' => array(
        'backend' => 'CacheCache\Backends\Memcache',
        'backend_args' => array(array(
            'host' => 'localhost',
            'port' => 11211
        ))
    )
));

$cache = CacheManager::get('array');

Simple usage

Cache (and backends) objects expose the following methods to interact with the data:

The add() method won't replace any existing value whereas set() will do. $ttl stands for Time To Live and will be the lifetime in seconds of the entry.

If get() is used to retreive a non existing $id, the $default value is returned instead.

Examples:

$cache->set('foo', 'bar');
$cache->exists('foo');
$cache->get('foo');
$cache->delete('foo');

$cache->add('foo', 'bar', 10);
$cache->exists('foo'); // true
sleep(11);
$cache->exists('foo'); // false

if (($foo = $cache->get('foo')) === null) {
    $foo = 'bar';
    $cache->set('foo', $foo);
}

To avoid manually testing entries for their existence you can use the getset() method:

$foo = $cache->getset('foo', function() {
    return 'bar';
});

In this example, the closure is called only when "foo" does not exist. Another way of doing a similar operation without the use of closures is using the load() and save() methods.

if (!($foo = $cache->load('foo'))) {
    $foo = 'bar';
    $cache->save($foo);
}

load() and save() calls can be nested. A currently running operation (after performing a load()) can be cancelled using cancel().

if (!($foo = $cache->load('foo'))) {
    try {
        $foo = 'bar';
        $cache->save($foo);
    } catch (Exception $e) {
        $cache->cancel();
        $foo = 'default value';
    }
}

Caching function calls

The call() function can be used to cache function calls. It behaves the same way as call_user_func_array(). The cache id is generated using the function name and the serialized arguments.

function do_heavy_stuff($arg) {
    sleep(1);
    return $arg;
}

echo $cache->call('do_heavy_stuff', array('foo')); // sleeps 1 sec
echo $cache->call('do_heavy_stuff', array('bar')); // sleeps 1 sec
echo $cache->call('do_heavy_stuff', array('foo')); // won't sleep

Caching object methods

Object methods can be cached by wrapping an object into a special proxy class. The object can be used as usual but all calls to its methods will be cached. The cache id for each method is computed using the method name, the serialized arguments and the serialized public properties of the object. The wrap() method automatically creates a namespace for all cache ids of this object which is, by default, named after the class.

class MyClass {
    public function doHeavyStuff($arg) {
        sleep(1);
        return $arg;
    }
}

$obj = $cache->wrap(new MyClass());
echo $obj->doHeavyStuff('foo'); // sleeps 1 sec
echo $obj->doHeavyStuff('foo'); // won't sleep

Capturing content

CacheCache provides multiple ways to capture echoed content. The easiest one works the same way as load() and save() but uses start() and end().

if (!$cache->start('foo')) {
    echo 'bar';
    $cache->end();
}

cancel() can also be used to cancel a call to start().

The output of a function can also be captured using the capture() method which works the same way as getset().

$foo = $cache->capture('foo', function() {
    echo 'bar';
});

Finally, the whole content of a page can be captured using capturePage(). It must be called before any content has been outputed.

$cache->capturePage();

By default, the cache id will be computed from the REQUEST_URI and the $_REQUEST array and the method calls exit() if there is a hit.

Multiple operations at once and pipelines

Multi get and set operations are available in a similar fashion as with the libmemcached pecl extension.

$cache->setMulti(array(
    'foo' => 'bar',
    'bar' => 'foo'
));

$r = $cache->getMulti(array('foo', 'bar'));
// $r = array('bar', 'foo');

CacheCache also introduces the concept of pipelines inspired by Predis. A pipeline is an object that stack operations and executes them all at the same time. Not all backends have native support for pipelines (only redis for the moment). A simple pipeline implementation based on setMulti() and getMulti() is provided for the other ones.

$r = $cache->pipeline(function($pipe) {
    $pipe->set('foo', 'bar');
    $pipe->set('bar', 'foo');
    $pipe->get('foo');
    $pipe->get('bar');
});

Namespaces

Namespaces allow you to better organize cache ids. A cache namespace is simply a new Cache object bound to a specific namespace. To create a subnamespace of the current one, use the ns() method.

$cache->set('a', 'b'); // id = a

$foo = $cache->ns('foo');
$foo->set('a', 'c'); // id = foo:a

Multiple caches

Multiple backends can be chained together using the MultiCache class. When a value is retreived (get or exists) it will first try in the first backend then in the second if it misses, then the third, etc...
When a key/value pair is modified (insert, add or delete), the operation will be performed on all backends.

$cache = new MultiCache(array($backend1, $backend2));

NOTE: Cache objects are themselves backends!

Logging

Backend usage can be logged using the LoggingBackend class which requires a Monolog\Logger instance.

$logger = new Monolog\Logger();
$backend = new LoggingBackend(new Backends\Memory(), $logger);
$cache = new Cache($backend);