Skip to content

Instantly share code, notes, and snippets.

@finagin
Last active April 10, 2019 15:03
Show Gist options
  • Save finagin/8681a5e0fb43629f3b45457b457daf02 to your computer and use it in GitHub Desktop.
Save finagin/8681a5e0fb43629f3b45457b457daf02 to your computer and use it in GitHub Desktop.
RunTime Cache
<?php declare(strict_types=1);
namespace Core;
class Cache
{
private $object = null;
private $cache = null;
public function __construct($object)
{
$this->object = $object;
$this->cache = new StaticCache();
}
public function __call($method, $args)
{
$closure = [$this->object, $method];
if (!is_callable($closure)) {
$object = $this->object;
/**
* @var \Closure $closure Замыкание для вызова protected и private методов
*/
$closure = \Closure::bind(function (...$args) use ($object, $method) {
return call_user_func_array([$object, $method], $args);
}, is_object($object) ? $object : null, is_object($object) ? get_class($object) : $object);
}
return $this->cache->cachedCall($closure, [$this->object, $method], $args);
}
}
<?php declare(strict_types=1);
namespace Core;
trait CacheTrait
{
/**
* @var static
*/
private $cache;
/**
* @var static
*/
private static $internalCache;
/**
* @return static
*/
public function cache()
{
if ($this->cache === null) {
$this->cache = new Cache($this);
}
return $this->cache;
}
/**
* @return static
*/
public static function cacheStatic()
{
if (static::$internalCache === null) {
static::$internalCache = new Cache(__CLASS__);
}
return static::$internalCache;
}
}
<?php
class Test
{
use \Core\CacheTrait;
static function sum($a, $b)
{
echo "\n".'calculate';
return $a + $b;
}
}
echo "\n".Test::cacheStatic()
->sum(1, 1);
echo "\n".Test::cacheStatic()
->sum(1, 2);
echo "\n".Test::cacheStatic()
->sum(1, 2);
echo "\n".Test::cacheStatic()
->sum(1, 1);
<?php declare(strict_types=1);
namespace Core;
final class StaticCache implements JsonSerializable
{
/**
* @var \Judy|array
*/
private $cache;
/**
* StaticCacheService constructor.
*/
public function __construct()
{
$this->clear();
}
private function clear()
{
$this->cache = extension_loaded('judy') ? new \Judy(\Judy::STRING_TO_MIXED) : [];
}
public function delete($key)
{
unset($this->cache[$key]);
}
public function set($key, $value)
{
$this->cache[$key] = $value;
}
/**
* @param $key
*
* @return bool
*/
public function exists($key)
{
return extension_loaded('judy')
? $this->cache->offsetExists($key)
: array_key_exists($key, $this->cache);
}
public function get($key)
{
if ($this->exists($key)) {
return $this->cache[$key];
}
throw new \OutOfBoundsException('Key not exists');
}
/**
* Метод, подсчитывающий хеш от параметров.
*
* @param array $params Параметры, от которых нужен хеш
*
* @return null|string Если null, то хеш создать не удалось
*/
public function getKey(array $params)
{
$keys = [];
try {
array_walk_recursive($params, function ($obj) use (&$keys) {
$keys[] = hash('tiger160,3', serialize($obj));
});
} catch (\Exception $e) {
return null;
}
return hash('tiger160,3', implode($keys));
}
/**
* Вызывает $func только в том случае, если этого вызова ещё нет в кеше.
*
* @param callable $func Функция или метод для вызова
* @param mixed $ns Префикс кеша, чаще всего — имя метода/ф-и (используется, чтобы пометить Closure)
* @param mixed $params Параметры, которые будут переданы вызываемому
*
* @return mixed Результат вызова вызываемого или взятый из кеша
*/
public function cachedCall(callable $func, $ns, array $params)
{
$key = $this->getKey([$ns, $params]);
if ($key === null) {
return call_user_func_array($func, $params);
}
if ($this->exists($key)) {
return $this->get($key);
} else {
$result = call_user_func_array($func, $params);
$this->set($key, $result);
return $result;
}
}
public function jsonSerialize()
{
if (extension_loaded('judy')) {
$cache = [];
foreach ($this->cache as $key => $value) {
$cache[$key] = $value;
}
return $cache;
} else {
return $this->cache;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment