Sunday, January 24, 2010

Caching data


In this example, we cache data using the following front-ends: core, standard output, class method, function. Data are cached in a file or in a database.

Components used in this example
Working with caches

class MyCache
{

    public static $frontend = array('Core''Class''Object''Function''Output');
    public static $backend = array('File''Sqlite');

Processing the cache request
  • We get the front-end, the back-end, the record identifier, the data or parameter, the tags, and the lifetime.
  • We set the cache.
  • We get the record identifiers currently cached.
  • We perform the action on the cache.
  • We refresh the list of record identifiers currently cached.
  • If we catch an exception, we return the error message.

    public function process()
    {
        // We get the front-end, the back-end, the record identifier,
        // the data or parameter, the tags, and the lifetime.
        list($frontend$backend$action$id$param$tags$lifetime) =
            $this->_getParameters();

        $ids = array();
        $params = array();

        try {
            // We set the cache.
            $cache $this->_setCache($frontend$backend$lifetime);
            // We get the record identifiers currently cached.
            $ids $this->_getIds($cache);

            // We perform the action on the cache.
            switch($action) {
                case 'clean all':
                case 'clean matchingAnyTag':
                case 'clean matchingTag':
                case 'clean notMatchingTag':
                case 'clean old':
                    list(, $mode) = explode(' '$action);
                    $result $cache->clean($mode$tags);
                    break;

                case 'getBackend':
                case 'getFillingPercentage':
                case 'getIds':
                case 'getTags':
                    $result $cache->$action();
                    break;

                case 'getIdsMatchingTags':
                case 'getIdsNotMatchingTags':
                    $result $cache->$action($tags);
                    break;

                case 'getMetadatas':
                case 'load':
                case 'remove':
                case 'test':
                    $result $cache->$action($id);
                    break;

                case 'touch 3':
                case 'touch 10':
                case 'touch 60':
                case 'touch 3600':
                    list(, $extraLifetime) = explode(' '$action);
                    $result $cache->touch($id$extraLifetime);
                    break;

                case 'Cache':
                    $result $this->_cache($cache$frontend$backend$tags,
                        $lifetime$param);
                    $id null;
                    break;

                case 'Set':
                default:
                    $result "The $frontend / $backend cache is set";
            }

            // We refresh the list of record identifiers currently cached.
            $ids $this->_getIds($cache);

        } catch (Exception $e) {
            // If we catch an exception, we return the error message.
            $result $e->getMessage();
        }

        return array($frontend$backend$action$id$param$lifetime$ids$result);
    }

Extraction of the parameters from the GET request

    private function _getParameters()
    {
        $frontend = isset($_GET['frontend'])? $_GET['frontend'] : 'Core';
        $backend = isset($_GET['backend'])? $_GET['backend'] : 'File';
        $id = isset($_GET['id'])? $_GET['id'] : null;
        $param = isset($_GET['param'])? $_GET['param'] : null;
        $tags = isset($_GET['tags'])? $_GET['tags'] : array();
        $lifetime = isset($_GET['lifetime'])? $_GET['lifetime'] : 300;

        isset($_GET['submit']) and $action $_GET['submit'] or
        $action = isset($_GET['action'])? $_GET['action'] : null;

        return array($frontend$backend$action$id$param$tags$lifetime);
    }

Setting the cache

    private function _setCache($frontend$backend)
    {
        $frontendOptions = array(
            'lifetime' => 3600,
            'automatic_serialization' => true,
            'cache_id_prefix' => $this->_setPrefix($frontend),
            );

        switch($frontend) {
            case 'Class':
                $frontendOptions['cached_entity'] = __CLASS__;
                break;

            case 'Object':
                $frontend 'Class';
                $frontendOptions['cached_entity'] = $this;
                break;
        }

        switch($backend) {
            case 'Sqlite':
                $backendOptions['cache_db_complete_path'] = 'data/cache/cache.sqlite';
                break;

            case 'File':
            default:
                $backendOptions['cache_dir'] = 'data/cache';
                break;
        }

        return Zend_Cache::factory($frontend$backend$frontendOptions$backendOptions);
    }

Getting the record identifiers currently cached without prefixes

    private function _getIds($cache)
    {
        $ids $cache->getIds();
        $prefixes implode('|'self::$frontend);
        $fct create_function('$s'"return !preg_match('~($prefixes)~', \$s);");

        return array_filter($ids$fct);
    }

Caching data
  • We cache the data or parameter along with the back-end, the front-end, the tags, the save method, and the lifetime.

    private function _cache($cache$frontend$backend$tags$lifetime$param)
    {
        // We cache the data or parameter along with the back-end, the front-end,
        // the tags, the save method, and the lifetime.

        switch($frontend) {
            case 'Core':
                if (!($isCached $data $cache->load($param))) {
                    $data self::_makeData($frontend$backend$tags'save()',
                        $lifetime$param);
                    $cache->save($data$param$tags$lifetime);
                }
                break;

            case 'Class':
                $method __CLASS__ '::' __FUNCTION__ '()';
                $lifetime and $cache->setSpecificLifetime($lifetime);
                $tags and $cache->setTagsArray($tags);
                $time time();
                $data $cache->staticMethod($frontend$backend$tags$method,
                    $lifetime$param);
                $isCached $data['settings']['timestamp'] < $time;
                break;

            case 'Object':
                $method '$cache->' __FUNCTION__ '()';
                $lifetime and $cache->setSpecificLifetime($lifetime);
                $tags and $cache->setTagsArray($tags);
                $time time();
                $data $cache->nonStaticMethod($frontend$backend$tags$method,
                    $lifetime$param);
                $isCached $data['settings']['timestamp'] < $time;
                break;

            case 'Function':
                $method __FUNCTION__ '()';
                $parameters = array($frontend$backend$tags$method$lifetime$param);
                $time time();
                $data $cache->call('myFunction'$parameters$tags$lifetime);
                $isCached $data['settings']['timestamp'] < $time;
                break;

            case 'Output':
                if (!($isCached $data $cache->start($paramfalsefalse))) {
                    $data self::_makeData($frontend$backend$tags'start()/end()',
                        $lifetime$param);
                    print_r($data);
                    $cache->end($tags$lifetimenullfalse);
                }
                break;
        }

        $result[] = $isCached?
            'THE DATA WAS RETRIEVED FROM THE CACHE.' :
            'THE DATA HAS JUST BEEN ADDED TO THE CACHE.';
        $result[] = $data;

        return $result;
    }

Static method to be cached

    public static function staticMethod($frontend$backend$tags$method,
        $lifetime$param)
    {
        return self::_makeData($frontend$backend$tags$method$lifetime$param);
    }

Non-static method to be cached

    public static function nonStaticMethod($frontend$backend$tags$method,
        $lifetime$param)
    {
        return self::_makeData($frontend$backend$tags$method$lifetime$param);
    }

Putting the data together

    public static function _makeData($frontend$backend$tags$method$lifetime$param)
    {
        $data['DATA'] = $param;
        $data['settings'] = array(
            'date' => date(DATE_COOKIE),
            'lifetime' => $lifetime,
            'frontend' => $frontend,
            'backend' => $backend,
            'method' => $method,
            'timestamp' => time(),
            'tags' => $tags,
        );

        return $data;
    }

Building the prefix of identifiers
  • We store a random number in a cookie to identify a user.
  • We build the prefix with the user identifier and the name of front-end.

    private function _setPrefix($frontend)
    {
        // We store a random number in a cookie to identify a user.
        if (empty($_COOKIE['cache'])) {
            $user rand(10009999);
            setcookie('cache'$usertime() + 3600 24'/');
        } else {
           $user $_COOKIE['cache'];
        }

        // We build the prefix with the user identifier and the name of front-end.
        return "$user$frontend";
    }


}

No comments:

Post a Comment