params['redis'])) { throw new Exception('Отсутствует конфигурация для redis'); } if(empty(Yii::$app->params['redis']['host'])) { throw new Exception('Отсутствует конфигурация для redis host'); } if(empty(Yii::$app->params['redis']['port'])) { throw new Exception('Отсутствует конфигурация для redis port'); } if(empty(Yii::$app->params['redis']['prefix'])) { throw new Exception('Отсутствует конфигурация для redis prefix'); } $redis = new Redis; $redis->connect(Yii::$app->params['redis']['host'], Yii::$app->params['redis']['port']); $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); $redis->setOption(Redis::OPT_PREFIX, Yii::$app->params['redis']['prefix']); $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); self::$redis = $redis; self::$livetime = isset(Yii::$app->params['redis']['livetime']) ? (int)Yii::$app->params['redis']['livetime'] : self::$livetime; self::$prefix = Yii::$app->params['redis']['prefix']; } return self::$redis; } public static function hdel($key, $value, $connect) { if(self::$setDeleteKey) { self::addDeleteKey($key, $value); } else { $prefix = self::$prefix; $raw = "HDEL {$prefix}{$key} {$value}"; Yii::beginProfile($raw, 'yii\db\Command::query'); $connect->hdel($key, $value); Yii::endProfile($raw, 'yii\db\Command::query'); } } public static function del($key, $realDelete = false) { if(self::$setDeleteKey && !$realDelete) { self::addDeleteKey($key); } else { $prefix = self::$prefix; $raw = "DEL {$prefix}{$key}"; Yii::beginProfile($raw, 'yii\db\Command::query'); try { self::connection()->del($key); } catch (\Exception $exc) { self::connection(true)->del($key); } Yii::endProfile($raw, 'yii\db\Command::query'); } } public static function hset($key, $field, $value) { $result = false; if(self::getActive()) { $prefix = self::$prefix; $raw = "HSET {$prefix}{$key} {$field}"; Yii::beginProfile($raw, 'yii\db\Command::query'); try { $result = self::connection()->hset($key, $field, $value); } catch (\Exception $exc) { $result = self::connection(true)->hset($key, $field, $value); } Yii::endProfile($raw, 'yii\db\Command::query'); } return $result; } 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) { $result = false; if(self::getActive()) { $prefix = self::$prefix; $raw = "HGET {$prefix}{$key} {$field}"; Yii::beginProfile($raw, 'yii\db\Command::query'); try { $result = self::connection()->hget($key, $field); } catch (\Exception $exc) { $result = self::connection(true)->hget($key, $field); } Yii::endProfile($raw, 'yii\db\Command::query'); } return $result; } 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()) { $prefix = self::$prefix; $connect = self::connection(); foreach($patterns as $pattern) { $cursor = NULL; foreach ($patterns as $pattern) { $arKeys = []; $raw = "HSCAN {$prefix}{$key} {$cursor} MATCH {$pattern}"; Yii::beginProfile($raw, 'yii\db\Command::query'); while($values = $connect->hscan($key, $cursor, $pattern)) { foreach($values as $vkey => $value) { $arKeys[] = $vkey; } } Yii::endProfile($raw, 'yii\db\Command::query'); foreach($arKeys as $value) { self::hdel($key, $value, $connect); } } } } } /** * удаление одного элемента * @param type $key * @return type */ public static function deleteAll($key, $realDelete = false) { if(self::getActive()) { self::del($key, $realDelete); } } public static function getKeyTypes() { $prefix = self::$prefix; $raw = "KEYS {$prefix}*"; Yii::beginProfile($raw, 'yii\db\Command::query'); try { $result = self::connection()->keys('*'); } catch (\Exception $exc) { $result = self::connection(true)->keys('*'); } Yii::endProfile($raw, 'yii\db\Command::query'); $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}:*"; $prefix = self::$prefix; $raw = "KEYS {$prefix}{$type}"; Yii::beginProfile($raw, 'yii\db\Command::query'); try { $result = self::connection()->keys($type); } catch (\Exception $exc) { $result = self::connection(true)->keys($type); } Yii::endProfile($raw, 'yii\db\Command::query'); foreach ($result as $val) { self::deleteAll(str_replace($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]; } $addStr = ''; foreach((array)$options as $okey => $oval) { $addStr .= "{$okey} {$oval}"; } $prefix = self::$prefix; $json = json_encode($value); $raw = "SET {$prefix}{$key} {$json} {$addStr}"; Yii::beginProfile($raw, 'yii\db\Command::query'); try { $result = self::connection()->set($key, $json, $options); } catch (\Exception $exc) { $result = self::connection(true)->set($key, $json, $options); } Yii::endProfile($raw, 'yii\db\Command::query'); } 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()) { $prefix = self::$prefix; $raw = "GET {$prefix}{$key}"; Yii::beginProfile($raw, 'yii\db\Command::query'); try { $result = self::connection()->get($key); } catch (\Exception $exc) { $result = self::connection(true)->get($key); } Yii::endProfile($raw, 'yii\db\Command::query'); } return $result ? json_decode($result, true) : $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() { $connect = self::connection(); $prefix = self::$prefix; $raw = "KEYS {$prefix}del:*"; Yii::beginProfile($raw, 'yii\db\Command::query'); $result = $connect->keys('del:*'); Yii::endProfile($raw, 'yii\db\Command::query'); foreach ($result as $val) { $cursor = null; //$key = str_replace("{$prefix}del:", '', $val); $key = str_replace($prefix, '', $val); $pattern = '*'; $arKeys = []; $raw = "HSCAN {$prefix}{$key} {$cursor} MATCH {$pattern}"; Yii::beginProfile($raw, 'yii\db\Command::query'); while($values = $connect->hscan($key, $cursor, $pattern)) { foreach($values as $vkey => $value) { if($value < time()) { $arKeys[] = $vkey; } } } $keyCache = str_replace("{$prefix}del:", '', $val); Yii::endProfile($raw, 'yii\db\Command::query'); foreach($arKeys as $value) { self::$setDeleteKey = false; if($value == '-') { self::del($keyCache, $connect); } else { self::hdel($keyCache, $value, $connect); } self::hdel($key, $value, $connect); } } } }