417 lines
11 KiB
PHP
417 lines
11 KiB
PHP
<?php
|
|
|
|
namespace dominion\cache;
|
|
|
|
use Yii;
|
|
|
|
class RedisCache
|
|
{
|
|
|
|
/**
|
|
* используется для отключения кеширования
|
|
* например при реиндексе или дебаге
|
|
* @var type
|
|
*/
|
|
protected static $active = true;
|
|
|
|
/**
|
|
* время жизни кеша в секундах
|
|
* @var int
|
|
*/
|
|
protected static $livetime;
|
|
|
|
/**
|
|
* начальная директория для кеша в редисе
|
|
* @var type
|
|
*/
|
|
protected static $prefix;
|
|
|
|
/**
|
|
* Время чкрез ктр удалить кешь
|
|
* @var int
|
|
*/
|
|
protected static $keysDeleteTime = 30;
|
|
protected static $setDeleteKey = false;
|
|
|
|
public static function setActive($active)
|
|
{
|
|
self::$active = $active;
|
|
}
|
|
|
|
public static function getActive()
|
|
{
|
|
return self::$active;
|
|
}
|
|
|
|
public static function setDeleteKeyValue($setDeleteKey)
|
|
{
|
|
self::$setDeleteKey = $setDeleteKey;
|
|
}
|
|
|
|
protected static function livetime()
|
|
{
|
|
if (!self::$livetime)
|
|
{
|
|
self::$livetime = isset(Yii::$app->params['redis'], Yii::$app->params['redis']['livetime']) ? (int) Yii::$app->params['redis']['livetime'] : 300;
|
|
}
|
|
return self::$livetime;
|
|
}
|
|
|
|
protected static function prefix()
|
|
{
|
|
if (!self::$prefix)
|
|
{
|
|
self::$prefix = isset(Yii::$app->params['redis'], Yii::$app->params['redis']['prefix']) ? (string)Yii::$app->params['redis']['prefix'] : '';
|
|
}
|
|
return self::$prefix;
|
|
}
|
|
|
|
protected static function calculateKey($key)
|
|
{
|
|
return self::prefix() . $key;
|
|
}
|
|
|
|
public static function hdel($key, $value)
|
|
{
|
|
if (self::$setDeleteKey)
|
|
{
|
|
self::addDeleteKey($key, $value);
|
|
}
|
|
else
|
|
{
|
|
Yii::$app->redis->hdel(self::calculateKey($key), $value);
|
|
}
|
|
}
|
|
|
|
public static function del($key, $realDelete = false)
|
|
{
|
|
if (self::$setDeleteKey && !$realDelete)
|
|
{
|
|
self::addDeleteKey($key);
|
|
}
|
|
else
|
|
{
|
|
Yii::$app->redis->del(self::calculateKey($key));
|
|
}
|
|
}
|
|
|
|
public static function hset($key, $field, $value)
|
|
{
|
|
return self::getActive() ? Yii::$app->redis->hset(self::calculateKey($key), $field, json_encode($value)) : false;
|
|
}
|
|
|
|
public static function hsetModel($key, $field, $model)
|
|
{
|
|
return self::hset($key, $field, is_object($model) ? $model->attributes : null);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param type $key
|
|
* @param type $field
|
|
* @param type $modelArray
|
|
* @param type $page родительская модель (используется для пагинации)
|
|
* @return type
|
|
*/
|
|
public static function hsetModelArray($key, $field, $modelArray, $page = null)
|
|
{
|
|
$values = [];
|
|
foreach ($modelArray as $model)
|
|
{
|
|
$values[] = $model->attributes;
|
|
}
|
|
if ($page)
|
|
{
|
|
$values['page'] = [
|
|
'totalCount' => $page->totalCount,
|
|
'perPage' => $page->perPage,
|
|
'sort' => $page->sort,
|
|
];
|
|
$field .= ":page:{$page->page}:perPage:{$page->perPage}:sort:{$page->sort}";
|
|
}
|
|
return self::hset($key, $field, $values);
|
|
}
|
|
|
|
public static function hget($key, $field)
|
|
{
|
|
$output = false;
|
|
if(self::getActive())
|
|
{
|
|
$output = json_decode(Yii::$app->redis->hget(self::calculateKey($key), $field), true);
|
|
}
|
|
return $output === null ? false : $output;;
|
|
}
|
|
|
|
public static function hgetModel($key, $field, $className)
|
|
{
|
|
$value = self::hget($key, $field);
|
|
return empty($value) ? $value : (new $className($value));
|
|
}
|
|
|
|
public static function hgetModelArray($key, $field, $className, $page = null)
|
|
{
|
|
if ($page)
|
|
{
|
|
$field .= ":page:{$page->page}:perPage:{$page->perPage}:sort:{$page->sort}";
|
|
}
|
|
$values = self::hget($key, $field);
|
|
if ($values === false)
|
|
{
|
|
return false;
|
|
}
|
|
$ids = [];
|
|
foreach ($values as $keyVal => $value)
|
|
{
|
|
if ($keyVal !== 'page' && isset($value['id']))
|
|
{
|
|
$ids[] = $value['id'];
|
|
}
|
|
}
|
|
$output = [];
|
|
foreach ($values as $keyVal => $value)
|
|
{
|
|
if ($keyVal === 'page')
|
|
{
|
|
if ($page)
|
|
{
|
|
$page->setMeta($value['totalCount'], $value['perPage']);
|
|
$page->sort = $value['sort'];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$model = new $className($value);
|
|
if (isset($model->ids))
|
|
{
|
|
$model->ids = $ids;
|
|
}
|
|
$output[] = $model;
|
|
}
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
public static function delete($key, array $patterns)
|
|
{
|
|
if (self::getActive())
|
|
{
|
|
foreach ($patterns as $pattern)
|
|
{
|
|
$cursor = NULL;
|
|
foreach ($patterns as $pattern)
|
|
{
|
|
$arKeys = [];
|
|
while ($values = Yii::$app->redis->hscan(self::calculateKey($key), $cursor, $pattern))
|
|
{
|
|
foreach ($values as $vkey => $value)
|
|
{
|
|
$arKeys[] = $vkey;
|
|
}
|
|
}
|
|
foreach ($arKeys as $value)
|
|
{
|
|
self::hdel($key, $value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* удаление одного элемента
|
|
* @param type $key
|
|
* @return type
|
|
*/
|
|
public static function deleteAll($key, $realDelete = false)
|
|
{
|
|
if (self::getActive())
|
|
{
|
|
self::del($key, $realDelete);
|
|
}
|
|
}
|
|
|
|
public static function getKeyTypes()
|
|
{
|
|
|
|
$result = Yii::$app->redis->keys(self::calculateKey('*'));
|
|
$output = ['*'];
|
|
foreach ($result as $val)
|
|
{
|
|
$arVal = explode(':', $val);
|
|
if (isset($arVal[1]) && !in_array($arVal[1], $output))
|
|
{
|
|
$output[] = $arVal[1];
|
|
}
|
|
}
|
|
sort($output);
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Удаление всех элементов по шаблоку
|
|
* @param type $type
|
|
* @return boolean
|
|
*/
|
|
public static function deleteType($type)
|
|
{
|
|
if (self::getActive())
|
|
{
|
|
self::deleteAll($type);
|
|
$type = $type == "*" ? "*" : "{$type}:*";
|
|
$result = Yii::$app->redis->keys(self::calculateKey($type));
|
|
foreach ($result as $val)
|
|
{
|
|
self::deleteAll(str_replace(self::prefix(), '', $val));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static function set($key, $value, $options = null)
|
|
{
|
|
$result = false;
|
|
if (self::getActive())
|
|
{
|
|
if ($options === true)
|
|
{
|
|
$options = ['EX' => self::livetime()];
|
|
}
|
|
elseif (is_int($options))
|
|
{
|
|
$options = ['EX' => $options];
|
|
}
|
|
Yii::$app->redis->set(self::calculateKey($key), json_encode($value) .' '. implode(' ', $options));
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public static function setModel($key, $model, $options = true)
|
|
{
|
|
return self::set($key, is_object($model) ? $model->attributes : null, $options);
|
|
}
|
|
|
|
public static function setModelArray($key, $modelArray, $page = null, $options = true)
|
|
{
|
|
$values = [];
|
|
foreach ($modelArray as $model)
|
|
{
|
|
$values[] = $model->attributes;
|
|
}
|
|
if ($page)
|
|
{
|
|
$values['page'] = [
|
|
'totalCount' => $page->totalCount,
|
|
'perPage' => $page->perPage,
|
|
'sort' => $page->sort,
|
|
];
|
|
}
|
|
return self::set($key, $values, $options);
|
|
}
|
|
|
|
public static function get(string $key)
|
|
{
|
|
$result = false;
|
|
if (self::getActive())
|
|
{
|
|
$result = json_decode(Yii::$app->redis->get(self::calculateKey($key)), true);
|
|
}
|
|
return $result === null ? false : $result;
|
|
}
|
|
|
|
public static function getModel($key, $className)
|
|
{
|
|
$value = self::get($key);
|
|
return empty($value) ? $value : (new $className($value));
|
|
}
|
|
|
|
public static function getModelArray($key, $className, $page = null)
|
|
{
|
|
$values = self::get($key);
|
|
if ($values === false)
|
|
{
|
|
return false;
|
|
}
|
|
$ids = [];
|
|
foreach ($values as $keyVal => $value)
|
|
{
|
|
if ($keyVal !== 'page' && isset($value['id']))
|
|
{
|
|
$ids[] = $value['id'];
|
|
}
|
|
}
|
|
$output = [];
|
|
foreach ($values as $keyVal => $value)
|
|
{
|
|
if ($keyVal === 'page')
|
|
{
|
|
if ($page)
|
|
{
|
|
$page->setMeta($value['totalCount'], $value['perPage']);
|
|
$page->sort = $value['sort'];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$model = new $className($value);
|
|
if (isset($model->ids))
|
|
{
|
|
$model->ids = $ids;
|
|
}
|
|
$output[] = $model;
|
|
}
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Доюавляем в лист удаляемый индекс
|
|
* @param type $key
|
|
* @param type $field
|
|
* @return type
|
|
*/
|
|
public static function addDeleteKey($key, $field = '-')
|
|
{
|
|
return self::hset('del:' . $key, $field, time() + self::$keysDeleteTime);
|
|
}
|
|
|
|
/**
|
|
* Удаляет устаревший кеш
|
|
* @return type
|
|
*/
|
|
public static function oldCacheDelete()
|
|
{
|
|
$result = Yii::$app->redis->keys(self::calculateKey('del:*'));
|
|
foreach ($result as $val)
|
|
{
|
|
$cursor = null;
|
|
$key = str_replace(self::prefix(), '', $val);
|
|
$pattern = '*';
|
|
$arKeys = [];
|
|
while ($values = Yii::$app->redis->hscan(self::calculateKey($key), $cursor, $pattern))
|
|
{
|
|
foreach ($values as $vkey => $value)
|
|
{
|
|
if ($value < time())
|
|
{
|
|
$arKeys[] = $vkey;
|
|
}
|
|
}
|
|
}
|
|
$keyCache = str_replace(self::prefix() . "del:", '', $val);
|
|
foreach ($arKeys as $value)
|
|
{
|
|
self::$setDeleteKey = false;
|
|
if ($value == '-')
|
|
{
|
|
self::del($keyCache);
|
|
}
|
|
else
|
|
{
|
|
self::hdel($keyCache, $value);
|
|
}
|
|
self::hdel($key, $value);
|
|
}
|
|
}
|
|
}
|
|
}
|