phossa2/cache

View on GitHub
src/Cache/Utility/CachedCallable.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa2\Cache
 * @copyright Copyright (c) 2016 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa2\Cache\Utility;

use Phossa2\Cache\CachePool;
use Phossa2\Cache\CacheItem;
use Phossa2\Shared\Extension\ExtensionAbstract;

/**
 * CachedCallable
 *
 * Cache result of a callable
 *
 * ```php
 * $cachePool->addUtility(new CachedCallable());
 *
 * // get result of callable [$object, 'method'] with parameters ...
 * // cache the result for 3600 sec.
 * $res = $cachePool->callableCache(3600, [$object, 'method'], $param1);
 * ```
 *
 * @package Phossa2\Cache
 * @author  Hong Zhang <phossa@126.com>
 * @see     ExtensionAbstract
 * @see     CacheItem
 * @see     CachePool
 * @version 2.0.0
 * @since   2.0.0 added
 */
class CachedCallable extends ExtensionAbstract
{
    /**
     * default ttl for the cached callable result
     *
     * @var    int
     * @access protected
     */
    protected $ttl = 86400;

    /**
     * {@inheritDoc}
     */
    public function methodsAvailable()/*# : array */
    {
        return ['callableCache'];
    }

    /**
     * Complete cycle of get cached result of a callable
     *
     * 1. if first argument is int, it is a TTL
     * 2. otherwise the first argument is a callable
     * 3. the remaining are the arguments for the callable
     *
     * @return mixed
     * @throws \Exception except from callable execution
     * @access public
     * @api
     */
    public function callableCache()
    {
        // get method arguments
        $args = func_get_args();

        // get ttl and uniq key
        list($ttl, $key) = $this->getTtlAndKey($args);

        /* @var $cache CachePool */
        $cache = $this->server;

        /* @var $item CacheItem */
        $item = $cache->getItem($key);

        if ($item->isHit()) {
            return $item->get();
        } else {
            $func = array_shift($args);
            $val  = call_user_func_array($func, $args);
            $cache->save($item->set($val)->expiresAfter($ttl));
            return $val;
        }
    }

    /**
     * The real boot method
     *
     * @access protected
     */
    protected function bootExtension()
    {
    }

    /**
     * Get TTL and unique key
     *
     * @param  array &$args
     * @return array
     * @access protected
     */
    protected function getTtlAndKey(array &$args)/*# : array */
    {
        if (is_numeric($args[0])) {
            $ttl = (int) array_shift($args);
            $key = $this->generateKey($args);
        } else {
            $key = $this->generateKey($args);
            $ttl = $this->ttl;
        }
        return [$ttl, $key];
    }

    /**
     * Generate key base on input
     *
     * @param  mixed $reference reference data
     * @return string
     * @access protected
     */
    protected function generateKey($reference)/*# : string */
    {
        if (is_array($reference)) {
            $reference = $this->flatReference($reference);
        }
        $md5 = md5(serialize($reference));
        return sprintf("/%s/%s/%s", $md5[0], $md5[1], $md5);
    }

    /**
     * flat the reference array to make it easy for serialize
     *
     * @param  array $reference reference data
     * @return array flattered array
     * @access protected
     */
    protected function flatReference(array $reference)/*# : array */
    {
        reset($reference);
        foreach ($reference as $key => $value) {
            if (is_object($value)) {
                $reference[$key] = get_class($value);
            } elseif (is_array($value)) {
                $reference[$key] = $this->flatReference($value);
            }
        }
        ksort($reference);
        return $reference;
    }
}