Php - How to use Cache  screenshot

Php - How to use Cache

We teach you how we cache in PHPNUKE

Use Xcache in PHP in order to create a simple and comfortable cache system. Recovering database values is usually a tedious task, so it is common to use intermediate caches which avoid making connections to the database.

For this tutorial we recommend you to previously study:

  • Database access
  • Anonymous Functions

In Phpnuke we use what we have called 'cascading cache', which consists in querying firstly the fastest caches in order to see if the data that we are looking for is there.

If we don’t find it, we go to slower storage areas, until finally we connect to the database if we haven’t found it before.

We have created a function called 'cache_get_callback', which searches in the cache, and if the data doesn’t exist, this function creates it.

<?php
$this->subdomain_row = cache_get_callback("employee_$id", function () use ($id) {
    $id = mysql_real_escape_string($id);
    $dbConnector = DbConnector::getInstance();
    $queryRes = $dbConnector->query("select * from employees where id=$id");
    return $dbConnector->fetch($queryRes);
});
?>

This function will try to recover the employee_$id code of the different caches, and if the code doesn’t find it, it will call to the anonymous function that we show.

If you look carefully, the anonymous function doesn’t receive any argument, since it will be invoked from cache_get_callback and this function doesn’t want to know anything about arguments, it only wants to call one function that creates the data.

Thus, we produce the anonymous function with the 'use' modifier, for this function to be generated with the id data internally.

cache_get_callback

<?php
function cache_get_callback($name, $callback, $ttl = 7200) {
    $key = "call$name";
    $result = cache_get($key);
    if (empty($result)) {
        $result = array(
            "data" => $callback() ,
            "time" => time()
        );
        cache_set($key, $result, $ttl);
    }
    return $result["data"];
}
?>

Now, it only remains to create cache_get cache_set functions, which are the ones that will read and write in the different caches we use.

cache_get and cache_set

<?php
function cache_get($cache_key) {
    if (isset($_GLOBALS[$cache_key])) {
        return $_GLOBALS[$cache_key];
    }
    if (xcache_isset($cache_key)) {
        return xcache_get($cache_key);
    }
    return false;
}
function cache_set($cache_key, $value, $ttl) {
    $_GLOBALS[$cache_key] = $value;
    xcache_set($cache_key, $value, $ttl);
    return false;
}
?>

This cascading cache stores simultaneously in GLOBALS (for the whole REQUEST) and in Xcache, but you can easily configure it for this cache to use other cache systems.

The first and the easiest one of the improvements you can do is to keep the old cache for more time in case the connection to the database is failing (or generates a temporary timeout).

cache_get_callback managed database errors

<?php
function cache_get_callback($name, $callback) {
    $refresh_timeout = 60;
    $xcache_ttl = 7600;
    $key = "call$name";
    $result = cache_get($key);
    if (empty($result) | |time() - $result['time'] > $refresh_timeout) {
        $Data = $callback();
        if (!$data) $data = $result['data'];
        $Result = array(
            "data" => $callback() ,
            "time" => time()
        );
        cache_set($key, $result, $xcache_ttl);
    }
    return $result['data'];
}
?>

Finally, we will show you how to cache multiple elements of a list in order to query the database for only the elements that we lack in the cache, and ask for all of them at the same time.

<?php
class bookpublic static function get($ids) {
    if (is_numeric($ids)) {
        $result = self::get(array(
            $ids
        ));
        return isset($result[0]) ? $result[0] : false;
    }
    if (!is_array($ids)) return false;
    $result = array();
    $pending = array();
    foreach ($ids as $id) {
        $row = cache_get("books$id");
        if ($row) {
            $result[$id] = $row
        } else {
            $pending[] = intval($id);
        }
    }
    if (count($pending) > 0) {
        foreach (query("select * from books where id in ($pending_string)") as $row) {
            $id = $row["id"]$pending_string = implode(",", $pending);
            $result[$id] = $row;
            cache_set("books$id", $row);
        }
    }
    return $result;
}
?>

As you can see, firstly we try to recover all the books of the cache and then we add the ones that we lack to the $pending list. After that, if any book isn’t in the cache, we ask the database for it, and finally we turn the results back.

This last function doesn’t use cache_get_callback because we want to recover all the books at the same time without doing many searches in the database, although with a little ingenuity, you will be able to create a cache_get_callback useful for your tasks.

These techniques are easily transformable to use other cache such as couchbase, memcached or apc.

Have a happy caching!