<?php

namespace loyalsoft;

//define('Redis_Debug', true); // 调试redis操作
//require __DIR__ . '/Predis/Autoloader.php';
require __DIR__ . '/predis-2.0.0/src/Autoloader.php';                           # 2022.8.16 更新Predis代码到2.0

\Predis\Autoloader::register();

/**
 * redis操作封装,
 * 关于阿里云redis的支持命令和未开放命令见:
 * https://docs.aliyun.com/?spm=5176.7630237.9.3.WfKF0A#/pub/kvstore/quick-start/kvstore-redis-command
 * 腾讯云支持的命令:
 * https://www.qcloud.com/doc/product/239/%E4%BD%BF%E7%94%A8%E9%99%90%E5%88%B6
 * redis相关命令说明见:http://redisdoc.com/
 *
 * 支持命令集接口见: /phpRedisAdmin/predis/src/ClientInterface.php
 * @version <br/>
 *          1.0.4  取消公共基类CMemBase,后面可能不太会用到memcache(d)了. gwang 2020年11月23日
 *          1.0.3  扩展list相关api的操作方法, 针对values统一做json编解码处理.
 *                 整理其他数据结构的操作方法, normal/hash/list api内部做json编解码处理,
 *                 set/zset value 默认不做json编解码处理, 针对set/zset的value,不推荐写入太大或复杂的数据.
 *                 gwang 2017年4月28日 <br/>
 *          1.0.2  扩充特有数据结构的操作方法, gwang 2016.4.21 <br/>
 *          1.0.1  扩充了事务的使用方法.开发竞争类业务,圣树争夺战, gwang 2016.3.21 <br/>
 *          1.0.0  pguan,基础功能, 方法与memcache看齐. <br/>
 */
class CRedisUtil {
//    public $keyDic = null;

    /**
     *
     * @var ClientInterface
     */
    public $redis = null;

    /**
     * 自动回收
     */
    function __destruct() {
        $this->close();
    }

    private static function debug() {
        if (defined('Redis_Debug') && Redis_Debug) {
            DebugHelper::debug(DebugHelper::get_call_stack());
        }
    }

// <editor-fold defaultstate="collapsed" desc="    实现CMemBase操作接口">

    /**
     * 添加一个值
     * @param string $key
     * @param string $value
     * @param int $ts=0 此值无效
     * @return true 成功, false 失败,此key已经存在
     */
    public function add($key, $value, $ts = 0) {
        self::debug();
        return $this->setnx($key, JsonUtil::encode($value));
    }

    /**
     * 复制一个变量到另一个key
     * @param string $surKey 源数据的key
     * @param string $desKey 目的数据的key
     */
    public function copy($surKey, $desKey) {
        self::debug();
        return $this->set($desKey, $this->get($surKey));
    }

    /**
     * 一次取多个值
     * @param array(string) $keys
     * @return array
     */
    public function getMulti($keys) {
        self::debug();
//        var_dump($keys);
        return $this->mget($keys);
    }

    /**
     * 设置多个值
     * @param dic $dict [{"key":value},{"key":value},....]
     * @return true 永远成功
     */
    public function setMutlti($dict) {
        self::debug();
        $params = array();
        foreach ($dict as $key => $val) {
            $params[$key] = is_string($val) ? $val : JsonUtil::encode($val);
        }
        if (count($params) <= 0) {
            return TRUE;
        }
        $this->mset($params);
        return true;
    }

    /**
     * 替换某个值
     * @param string $key
     * @param string $value
     * @param int $ts
     * @return boolean true 成功,false 失败
     */
    public function replace($key, $value, $ts = 0) {
        self::debug();
        $ret = $this->redis->setex($key, $value, $ts);
        if (strtolower($ret) == "ok") {
            return true;
        }
        $this->logErr("replace" . $ret);
        return false;
    }

    /**
     * 不经过jsonencode直接存储
     * @deprecated since version 20160413113950 经过自己考察json_encode对数据的影响并不存在
     * @param type $key
     * @return any
     */
    public function getWithoutJson($key) {
        self::debug();
        return $this->redis->get($key);
    }

    /**
     * 不经过jsondecode直接返回
     * @deprecated since version 20160413113950 经过自己考察json_encode对数据的影响并不存在
     * @param string $key
     * @param string $value
     * @param int $ts
     * @return boolean
     */
    public function setWithoutJson($key, $value, $ts = 0) {
        self::debug();
        $ret = $this->redis->set($key, $value, $ts);
        if (strtolower($ret) == "ok") {
            return true;
        }
        $this->logErr("set" . $ret);
        return false;
    }

    /**
     * 输出日志
     * @param mixed $msg
     */
    private function logErr($msg) {
        CLog::err($msg, "Redis Err:");
    }

    /**
     * 连接
     * @param string $host
     * @param string $port
     * @param string $pwd
     * @param string $db 数据库, 不建议输入参数, 如果是购买云实例只有一个db.
     * @return \CRedisUtil
     */
    public function conn($host, $port, $pwd = "", $db = 0) {
//        DebugHelper::print_stack_trace();
//        $this->redis = new \Predis\Client(array(
//        $this->redis = new \Redis();
//        $this->redis->connect('127.0.0.1', 6379);
//        $this->redis->auth($pwd);
        $this->redis = new \Predis\Client(array(
            'scheme' => 'tcp',
            'host' => $host,
            'port' => $port,
            'password' => $pwd,
        ));
        if ($db != 0) {
            $this->redis->select($db);
        }
//        $this->keyDic = new \stdClass();
        return $this;
    }

    /**
     * 关闭连接
     */
    public function close() {
        $this->redis->quit();
    }

    /**
     * 获取指定键值
     * @param string $key
     * @return any json_decode($result)
     */
    public function get($key) {
        self::debug();
        $result = $this->redis->get($key);
        if (!$result || $result == "" || $result == null) {
            return null;
        }
//        $this->keyDic->$key = $result;
        return JsonUtil::decode($result);
    }

    /**
     * 将字符串值 $val 关联到 key
     * @param string $key
     * @param any $value
     * @param int $ts default(0)  过期时间(单位秒)
     * @return boolean 成功true,失败false
     */
    public function set($key, $value, $ts = 0) {
        self::debug();
        if ($value === null || $value === "") {    # 这里必须用全等符号
            return false;
        }
        if (is_string($value)) {                   # 使用json序列化后存储
            $val = $value;
        } else {
            $val = JsonUtil::encode($value);
        }
        if ($ts > 0) {                             # 将会设置TTL 到期删除 ps: replaced by gwang 增加过期时间
            $ret = $this->redis->setex($key, $ts, $val);
        } else {                                   # 0:永不过期,如果原来有TTL将会清除TTL.
            $ret = $this->redis->set($key, $val);
        }
        if ("OK" != $ret) {
            $this->logErr("Set:" . $ret);
            return false;
        }
        return true;
    }

    /**
     * 安全的写入操作
     * @deprecated since version 1.0 云平台暂时不支持
     * @param string $key
     * @param any $value
     * @return boolean 成功true,失败false
     */
    public function cas($key, $value, $ts = 0) {
        if (false) {                               # ps. 各云平台皆不支持eval方法执行lua脚本.等开放支持的时候才是真正支持cas的时候
            $castoken = $this->keyDic->$key;
#                                                  # 定义一段脚本
            $script = <<<casscript
if redis.call("get",KEYS[1]) == "$castoken"
then
    return redis.call("set",KEYS[1],ARGV[1])
else
    return 0
end
casscript;
            $ret = $this->redis->eval($script, 1, $key, $value); # redis 执行lua脚本
            if (0 == $ret) {
                return false;
            }
            return true;
        }

// 非线程安全,原因见下
        return $this->set($key, $value, $ts);
    }

    /**
     * 删除一个指定键值
     * @param string $key
     * @return int  删除键的个数
     */
    public function delete($key) {
        self::debug();
        my_Assert(is_string($key), "arg key should be a string!");
        $infos = array();
        array_push($infos, $key);
        return $this->redis->del($infos);
    }

    /**
     * 删除一个或多个指定键值
     * @param array $keys
     * @return int  删除键的个数
     */
    public function deleteMulti($keys) {
        self::debug();
        $infos = (array) $keys;                                                 # 强制转数据类型为数组
        return $this->redis->del($infos);
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="    normal 普通对象操作  mget/mset">

    /**
     * 返回所有(一个或多个)给定 key 的值
     * 如果给定的 key 里面，有某个 key 不存在，那么这个 key 返回特殊值 nil 。因此，该命令永不失败。
     * @param array $keys
     * @return array  一个包含所有给定 key 的值的列表
     */
    private function mget($keys) {
//        self::debug();
        $ret = array();
        if (count($keys)) {
            $ctx = $this->redis->mget($keys);
            $mval = count($ctx);
            for ($i = 0;
                    $i < $mval;
                    $i++) {
                $ret[] = JsonUtil::decode($ctx[$i]);
            }
        }
        return $ret;
    }

    /**
     * Add函数拥有相同效果     \n
     * 将 key 的值设为 value ，当且仅当 key 不存在。
     * 若给定的 key 已经存在，则不做任何动作。
     * @param type $key
     * @param type $val
     * @return boolean 成功true,失败false
     */
    private function setnx($key, $val) {
        self::debug();
        $ret = $this->redis->setnx($key, $val);
// 成功返回1, 失败返回0
        if (1 == $ret) {
            return true;
        } else {
            $this->logErr("setnx:" . $ret . "(值已存在)");
        }
        return false;
    }

    /**
     * 同时设置一个或多个 key-value 对
     * 如果某个给定 key 已经存在，那么 MSET 会用新值覆盖原来的旧值
     * @param array $dict
     * @return mixed
     */
    private function mset($dict) {
        self::debug();
        return $this->redis->mset($dict);
    }

    /**
     * 同时设置一个或多个 key-value 对，当且仅当所有给定 key 都不存在
     * 即使只有一个给定 key 已存在， MSETNX 也会拒绝执行所有给定 key 的设置操作
     *
     * @param array $dict
     * @return int
     */
    public function msetnx($dict) {
        self::debug();
        $ret = $this->redis->msetnx($dict);
        return $ret == 1;
    }

    /**
     * 检测指定键值是否存在
     * @param type $key
     * @return boolean  true 存在, false 不存在
     */
    public function exists($key) {
        self::debug();
        return $this->redis->exists($key) == 1;
    }

    /**
     * 将 key 中储存的数字值增一
     * 如果 key 不存在，那么 key 的值会先被初始化为 0
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误
     * 本操作的值限制在 64 位(bit)有符号数字表示之内
     * @param type $key
     * @return int 执行 INCR 命令之后 key 的值
     */
    public function increment($key) {
        self::debug();
        return $this->redis->incr($key);
    }

    /**
     * 将 key 中储存的数字值增加 一个int值
     * 如果 key 不存在，那么 key 的值会先被初始化为 0
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误
     * 本操作的值限制在 64 位(bit)有符号数字表示之内
     * @param string $key
     * @param int $i 增加的值
     * @return int 执行 INCR 命令之后 key 的值
     */
    public function incrby($key, $i) {
        self::debug();
        return $this->redis->incrby($key, $i);
    }

// </editor-fold>
//
//======================== 以下部分 added by gwang at 2016-4-14 15:17:25  =================
// <editor-fold defaultstate="collapsed" desc="    辅助函数    ">

    /**
     * Server功能,看服务器是否连通
     * @return boolean true连通正常
     */
    public function ping() {
        $ret = $this->redis->ping();
        if ($ret == "PONG") {
            return true;
        }
        $this->logErr("ping:" . $ret);
        return false;
    }

    /**
     * 对数组进行json编码
     * @param array $values
     * @return array
     */
    static private function json_encode_arr($values) {
        $arr = array();
        if (is_array($values)) {
            $arr = array_map(function ($v) {
                return JsonUtil::encode($v);
            }, $values);
        } else {
            $arr[] = JsonUtil::encode($values);
        }
        return $arr;
    }

    /**
     * 对数组进行json解码
     * @param array $values
     * @return array
     */
    static private function json_decode_arr(array $values) {
        $arr = array();
        if (is_array($values)) {
            $arr = array_map(function ($v) {
                return JsonUtil::decode($v);
            }, $values);
        } else {
            $arr[] = JsonUtil::decode($values);
        }
        return $arr;
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="    事务操作   ">

    /**
     * 监视一个(或多个) key ，如果在事务执行之前这个(或这些) key 被其他命令所改动，那么事务将被打断。
     * @param string $key
     */
    public function watch($key) {
        $this->redis->watch($key);
    }

    /**
     * 取消监视
     */
    public function unwatch() {
        $this->redis->unwatch();
    }

    /**
     * 开启事务
     */
    public function multi() {
        $this->redis->multi();
    }

    /**
     * 执行事务
     * @return type 事务块内所有命令的返回值，按命令执行的先后顺序排列。当操作被打断时，返回空值 nil 。
     */
    public function exec() {
        return $this->redis->exec();
    }

// </editor-fold>
//
// ==   ↓ 下面是Redis独有的数据类型操作 ↓  ==
//
// <editor-fold defaultstate="collapsed" desc="    set 集合操作    ">

    /**
     * 集合(set) - 将一个或多个 member 元素加入到集合 key 当中，已经存在于集合的 member 元素将被忽略。
     * 集合不存在时将会创建集合
     * @param string $key
     * @param string[] $members (mixed is ok, i will transmit it.)
     * @return int
     * @expectedException type not set error
     */
    public function sadd($key, $members) {
        self::debug();
        if (!is_array($members)) {                                              # 防御: 参数不为数组
            if (!is_string($members)) {                                         # 防御: 参数不为字符串
                $members = JsonUtil::encode($members);
            }
            $members = array($members);                                         # 转为字符串数组
        }
        return $this->redis->sadd($key, $members);
    }

    /**
     * 集合(set) - 将一个或多个 member 元素从集合 key 当中移除
     * 集合不存在时将会创建集合
     * @param string $key
     * @param string[] $members
     * @return int
     * @expectedException type not set error
     */
    public function sremove($key, $members) {
        self::debug();
        return $this->redis->srem($key, $members);
    }

    /**
     * 集合(set) -  返回集合 key 的基数(集合中元素的数量)。
     * @param strig $key
     * @return int
     */
    public function scard($key) {
        self::debug();
        return $this->redis->scard($key);
    }

    /**
     * 集合(set) - 等效于scard: 返回集合 key 的基数(集合中元素的数量)。
     * @param string $key
     * @return int
     *
     */
    public function slen($key) {
        self::debug();
        return $this->scard($key);
    }

    /**
     * 集合(set) - 判断某个元素是否属于集合
     * @param string $key
     * @param string $member
     * @return bool
     */
    public function sismember($key, $member) {
        self::debug();
        return $this->redis->sismember($key, $member) == 1;
    }

    /**
     * 集合(set) - 返回集合 key 中的所有成员。
     * @param type $key
     * @return array 不存在的key视为空集合
     */
    public function smembers($key) {
        self::debug();
        return $this->redis->smembers($key);
    }

    /**
     * 集合(set) - 如果命令执行时，只提供了 key 参数，那么返回集合中的10个随机元素。
     * 如果 count 为正数，且小于集合基数，那么命令返回一个包含 count 个元素的数组，数组中的元素各不相同。如果 count 大于等于集合基数，那么返回整个集合。
     * 如果 count 为负数，那么命令返回一个数组，数组中的元素可能会重复出现多次，而数组的长度为 count 的绝对值。
     * @param string $key
     * @param int $count =10
     * @return array
     */
    public function srandmember($key, $count = 10) {
        self::debug();
        return $this->redis->srandmember($key, $count);
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="    hash 哈希表操作    ">

    /**
     * 哈希表 - 删除 字段
     * @param string $key
     * @param array $fields
     * @return int 删除的字段的数量
     */
    public function hdel($key, $fields) {
        self::debug();
        return $this->redis->hdel($key, $fields);
    }

    /**
     * 哈希表 - 判断field是否存在
     * @param string $key
     * @param string $field
     * @return boolean true=存在,false 找不到
     */
    public function hexists($key, $field) {
        self::debug();
        return $this->redis->hexists($key, $field) == 1;
    }

    /**
     * 哈希表 - 获取字段
     * @param string $key
     * @param string $field 字段名
     * @return mixed json_decoded object
     */
    public function hget($key, $field) {
        self::debug();
        $ret = $this->redis->hget($key, $field);
        return JsonUtil::decode($ret);
    }

    /**
     * 哈希表 - 获取所有字段
     * @param string $key
     * @return stdclass/associate_array (适用箭头取变量写法)
     */
    public function hgetall($key) {
        self::debug();
        $ret = ArrayInit();
        $arr = $this->redis->hgetall($key);
        foreach ($arr as $k => $v) {
            $ret[$k] = JsonUtil::decode($v);
        }
// 转为关联数组({})再返回, 方便代码中使用箭头写法
        return JsonUtil::decode(JsonUtil::encode($ret));
    }

    /**
     * 哈希表 - 获取表中字段数量
     * @param string $key
     * @return int
     */
    public function hlen($key) {
        self::debug();
        return $this->redis->hlen($key);
    }

    /**
     * 哈希表 - 同时获取多个字段
     * @param string $key
     * @param array $fields
     * @return Array
     */
    public function hmget($key, $fields) {
        self::debug();
        $ret = ArrayInit();
        if (is_array($fields) && count($fields) > 0) {
            $arr = $this->redis->hmget($key, $fields);
            foreach ($arr as $k => $v) {
                $ret[$k] = JsonUtil::decode($v);
            }
        }
// 转为关联数组({})再返回, 方便代码中使用箭头写法
        return JsonUtil::decode(JsonUtil::encode($ret));
    }

    /**
     * 哈希表 - 同时设置多个字段的值
     * @param string $key
     * @param assoc_array $dictionary
     * @return boolean true成功
     */
    public function hmset($key, $dictionary) {
        self::debug();
        $newdic = ArrayInit();
        foreach ($dictionary as $k => $v) {
            $newdic[$k] = JsonUtil::encode($v);
        }
        $ret = $this->redis->hmset($key, $newdic);
        if (strtolower($ret) == 'ok') {
            return true;
        }
        return false;
    }

    /**
     *  哈希表 - 同时设置多个字段的值(带Not Changed校验)
     * @param string $key
     * @param HashSaver $dic (要包含$stVer字段用以校验)
     * @return boolean 写入是否成功
     */
    public function hmset_Cas(string $key, HashSaver $dic) {
#                                                  # 定义一段脚本
        static $script = <<<Lua
local key,casToken,casVer, dic = KEYS[1], KEYS[2], ARGV[1], ARGV[2]
local cv = redis.call("hget", key, casToken)
local t = cjson.decode( dic )
if not cv or tonumber(cv) < tonumber(casVer) then
    for k, v in pairs(t) do
        if type(v) =="table" then                -- 加戏,如果value是对象的话, 继续encode一下.
            v=cjson.encode(v)
        end
        redis.call("hset", key, k, v)
    end
    return 1
else
    return 0
end
Lua;
        try {
            $castoken = "stVer";
            $casVer = $dic->stVer;
            $ret = $this->redis->eval($script, 2, $key, $castoken, $casVer, $dic->toString());  # redis 执行lua脚本
//            CLog::err($dic->toString());
            if (0 == $ret) {
                return false;
            }
        } catch (\Exception $e) {
            CLog::err($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 哈希表 - 设置或新建一个字段的值
     * @param string $key
     * @param string $field
     * @param $mixed $value
     * @return int 0 覆盖旧值, 1 新建字段
     */
    public function hset($key, $field, $value) {
        self::debug();
        return $this->redis->hset($key, $field, JsonUtil::encode($value));
    }

    /**
     * 哈希表 - 增加一个新的field,已经存在则放弃操作返回false
     * @param string $key
     * @param string $field
     * @param string $value
     * @return boolean true成功
     */
    public function hsetnx($key, $field, $value) {
        self::debug();
        return $this->redis->hsetnx($key, $field, JsonUtil::encode($value)) == 1;
    }

    /**
     * 哈希表 - 按字段赠加 int值
     * @param string $key
     * @param string $field
     * @param int $increment
     * @return int
     */
    public function hincrby($key, $field, $increment) {
        self::debug();
        return $this->redis->hincrby($key, $field, $increment);
    }

    /**
     * 哈希表 - 按字段增加 float值
     * @param string $key
     * @param string $field
     * @param string $increment float值用string
     * @return string  用string存float值
     */
    public function hincrbyfloat($key, $field, $increment) {
        self::debug();
        return $this->redis->hincrbyfloat($key, $field, $increment);
    }

    /**
     * @param string $key
     * @return array
     */
    public function hkeys($key) {
        self::debug();
        return $this->redis->hkeys($key);
    }

//     * @method array  hscan($key, $cursor, array $options = null)
//     * @method array  hvals($key)
// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc=" list 链表操作 ">
//
// <editor-fold defaultstate="collapsed" desc=" 阻塞的链表操作 ">

    /**
     *  链表 - 阻塞弹出, 当列表中没有元素的时候,命令会阻塞,直到等待超时或者有元素被添加到列表中.
     * @param array $keys 可以指定多个数组
     * @param int $timeout
     * @return array
     */
    public function blpop(array $keys, $timeout) {
        self::debug();
        $arr = $this->redis->blpop($keys, $timeout);
        $arr[1] = JsonUtil::decode($arr[1]);  // 对value进行解包
        return $arr;
    }

    /**
     * 链表 - 阻塞队尾弹出, 当列表中没有元素的时候,命令会阻塞,直到等待超时或者有元素被添加到列表中.
     * @return array
     */
    public function brpop(array $keys, $timeout) {
        self::debug();
        $arr = $this->redis->brpop($keys, $timeout);
        $arr[1] = JsonUtil::decode($arr[1]);
        return $arr;
    }

    /**
     * 链表 - 阻塞版的 队尾弹出插入到队首. 本操作是原子的.
     *
     * @return array 假如在指定时间内没有任何元素被弹出，则返回一个 nil 和等待时长。
     *        反之，返回一个含有两个元素的列表，第一个元素是被弹出元素的值，第二个元素是等待时长。
     */
    public function brpoplpush($source, $destination, $timeout) {
        self::debug();
        $arr = $this->redis > brpoplpush($source, $destination, $timeout);
        $arr[0] = JsonUtil::decode($arr[0]);
        return $arr;
    }

// </editor-fold>
//

    /**
     * 链表 - 返回下标为index的元素
     * @return string
     */
    public function lindex($key, $index) {
        self::debug();
        $a = $this->redis->lindex($key, $index);
        return JsonUtil::decode($a);
    }

    /**
     * 链表 - 返回 链表key的长度
     * @return int
     * @param type $key
     */
    public function llen($key) {
        self::debug();
        return $this->redis->llen($key);
    }

    /**
     * 链表 - 移除并返回链表的队首元素.
     * @return string
     * @param type $key
     */
    public function lpop($key) {
        self::debug();
        $a = $this->redis->lpop($key);
        return JsonUtil::decode($a);
    }

    /**
     * 链表 - 向链表插入一个或多个值,链表不存在时创建链表并插入元素
     * @return int key存在却不是链表类型时返回一个错误.
     * @param type $key
     * @param array $values
     */
    public function lpush($key, $values) {
        self::debug();
        $arr = self::json_encode_arr($values);
        return $this->redis->lpush($key, $arr);
    }

    /**
     * 链表 - 取链表中指定下标区间的元素
     * @return array
     * @param string $key
     * @param int $start
     * @param int $stop
     */
    public function lrange($key, $start, $stop) {
        self::debug();
        $values = $this->redis->lrange($key, $start, $stop);
        return self::json_decode_arr($values);
    }

    /**
     * 链表 - 移除链表中 count 个值为 value的元素.
     * @return int
     * @param string $key
     * @param int $count
     * @param string $value
     */
    public function lrem($key, $count, $value) {
        self::debug();
        return $this->redis->lrem($key, $count, JsonUtil::encode($value));
    }

    /**
     * 链表 - 将链表中下标为index的 元素设置为value
     * @return mixed
     *
     * @param type $key
     * @param type $index
     * @param type $value
     */
    public function lset($key, $index, $value) {
        self::debug();
        return $this->redis->lset($key, $index, JsonUtil::encode($value));
    }

    /**
     * 链表 - 对链表进行修剪, 让链表只保留指定下标区间的元素,其余元素删除
     * @return bool true 成功, false 失败
     * @param string $key
     * @param int $start
     * @param int $stop
     */
    public function ltrim($key, $start, $stop) {
        self::debug();
// return mixed: ok or fails
        $ret = $this->redis->ltrim($key, $start, $stop);
        if (strtolower($ret) == 'ok') {
            return true;
        }
        return false;
    }

    /**
     * 链表 - 从链表右侧(尾部)弹出一个元素,并返回该元素
     * @param string $key
     * @return string 列表的尾元素。当 key 不存在时，返回 nil
     */
    public function rpop($key) {
        self::debug();
        return JsonUtil::decode($this->redis->rpop($key));
    }

    /**
     * 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
     * @param string $key
     * @param array $values
     * @return int
     */
    public function rpush($key, array $values) {
        self::debug();
        $arr = self::json_encode_arr($values);
        return $this->redis->rpush($key, $arr);
    }

//
//    /**
//     * 链表 - 向链表中插入一个或多个值,链表不存在时,什么也不做.
//     * @return int
//     * @param type $key
//     * @param type $value
//     */
//    public function lpushx($key, $value)
//    {
//        return $this->redis->lpushx($key, $value);
//    }
//    /**
//     * 链表 - 将值 value 插入到链表 key 中 , 位于pivot之前或之后
//     * @return int -1:未找到pivot, 0:链表为空或者不存在.
//     *
//     * @param string $key     链表名称
//     * @param string $whence  after/before pivot之前或之后
//     * @param  string $pivot  插入pivot之前或之后
//     * @param  string $value  要插入的value
//     */
//    public function linsert($key, $whence, $pivot, $value)
//    {
//        $this->redis->linsert($key, $whence, $pivot, $value);
//    }
//    /**
//     * 在一个原子时间内，执行以下两个动作
//     * 将列表 source 中的最后一个元素(尾元素)弹出，并返回给客户端。
//     * 将 source 弹出的元素插入到列表 destination ，作为 destination 列表的的头元素.
//     * @return string
//     */
//    public function rpoplpush($source, $destination)
//    {
//        throw new Exception("un implemented now!");
//    }
//    /**
//     * 将值 value 插入到列表 key 的表尾，当且仅当 key 存在并且是一个列表。
//     *   和 RPUSH 命令相反，当 key 不存在时， RPUSHX 命令什么也不做。
//     * @param type $key
//     * @param type $value
//     * @return int
//     */
//    public function rpushx($key, $value)
//    {
//        throw new Exception("un implemented now!");
//    }
// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc=" zset 有序集合 ">

    /**
     * 有序集合 - 添加或更新元素  对于zset类型不建议写入太长, 太复杂的value
     * @param string $key
     * @param array $membersAndScoresDictionary array( 'value'=>score,'value'=>score,...), score可以是int或float
     * @return int 新增元素数量,不包含更新的元素
     */
    public function zadd($key, $membersAndScoresDictionary) {
        self::debug();
        return $this->redis->zadd($key, $membersAndScoresDictionary);
    }

    /**
     * 有序集合 - 给某个元素增加积分
     * @param string $key
     * @param string $member
     * @param int $increment
     * @return string
     */
    public function zincrby($key, $member, $increment = 1) {
        self::debug();
        return $this->redis->zincrby($key, $increment, $member);
    }

    /**
     * 有序集合 - 元素数量
     * @param string $key
     * @return int
     */
    public function zlen($key) {
        self::debug();
        return $this->redis->zcard($key);
    }

    /**
     * 有序集合 - 统计某区间元素数量
     * @param string $key
     * @param int/float $min 最低分
     * @param int/float $max 最高分
     * @return int
     */
    public function zcount($key, $min, $max) {
        self::debug();
        return $this->redis->zcount($key, $min, $max);
    }

    /**
     * 有序集合 - 获取集合指定区间内的成员, 第一个成员从0开始
     * @param string $key
     * @param int $start
     * @param int $stop Ps.以 -1 表示最后一个成员， -2 表示倒数第二个成员
     * @param boolean $withScore 返回值中是否带score字段
     * @return array (index=>member) or assoc_array (member=>score)
     */
    public function zrange($key, $start, $stop, $withScore = false) {
        self::debug();
        if ($withScore) {
            return $this->redis->zrange($key, $start, $stop, 'WITHSCORES');
        } else {
            return $this->redis->zrange($key, $start, $stop);
        }

        if ($withScore) {
            $ret = $this->redis->zrange($key, $start, $stop, 'WITHSCORES');
            DebugHelper::var_dump($ret);
            $ma = count($ret);
            $arr = array();
            for ($i = 0; $i < $ma; $i += 2) {
                $arr[$ret[$i]] = $ret[$i + 1];
            }
            return $arr;
        } else {
            $ret = $this->redis->zrange($key, $start, $stop);
            DebugHelper::var_dump($ret);
            return $ret;
        }
    }

    /**
     *
     * 有序集合 - 获取集合倒序之后,指定区间内的成员, 第一个成员从0开始
     * @param string $key
     * @param int $start
     * @param int $stop Ps.以 -1 表示最后一个成员， -2 表示倒数第二个成员
     * @param boolean $withScore 返回值中是否带score字段 default(false)
     * @return array (index=>member) or assoc_array (member=>score) 用foreach(=>)取
     */
    public function zrevrange($key, $start, $stop, $withScore = false) {
        self::debug();
        if ($withScore) {
            $r = $this->redis->zrevrange($key, $start, $stop, 'WITHSCORES');
            return $r;
        } else {
            return $this->redis->zrevrange($key, $start, $stop);
        }
    }

    /**
     *
     * 有序列表 - 获取指定积分区间内的成员
     * @param string $key
     * @param int/float $min
     * @param int/float $max
     * @param boolean $withScore
     * @param bool $limit 是否限制返回值大小
     * @param int $offset 限制返回结果的起始位置
     * @param type $count 返回结果的数量
     * @return type
     */
    public function zrangebyscore($key, $min, $max, $withScore = false, $limit = false, $offset = 0, $count = 10) {
        self::debug();
        if ($limit) {
            if ($withScore) {
                return $this->redis->zrangebyscore($key, $min, $max, 'WITHSCORES', "LIMIT", $offset, $count);
            } else {
                return $this->redis->zrangebyscore($key, $min, $max, 'LIMIT', $offset, $count);
            }
        } else {
            if ($withScore) {
                return $this->redis->zrangebyscore($key, $min, $max, 'WITHSCORES');
            } else {
                return $this->redis->zrangebyscore($key, $min, $max);
            }
        }
    }

    /**
     *
     * 有序列表 - 获取按照倒序排序后指定积分区间内的成员
     * @param string $key
     * @param int/float $max
     * @param int/float $min
     * @param boolean $withScore
     * @param bool $limit 是否限制返回值大小
     * @param int $offset 限制返回结果的起始位置
     * @param type $count 返回结果的数量
     * @return type
     */
    public function zrevrangebyscore($key, $max, $min, $withScore = false, $limit = false, $offset = 0, $count = 10) {
        self::debug();
        if ($limit) {
            if ($withScore) {
                return $this->redis->zrevrangebyscore($key, $max, $min, 'WITHSCORES', "LIMIT", $offset, $count);
            } else {
                return $this->redis->zrevrangebyscore($key, $max, $min, 'LIMIT', $offset, $count);
            }
        } else {
            if ($withScore) {
                return $this->redis->zrevrangebyscore($key, $max, $min, 'WITHSCORES');
            } else {
                return $this->redis->zrevrangebyscore($key, $max, $min);
            }
        }
    }

    /**
     * 有序列表 - 根据元素反查排名
     * @param string $key
     * @param string $member
     * @return int
     */
    public function zrank($key, $member) {
        self::debug();
        return $this->redis->zrank($key, $member);
    }

    /**
     * 有序列表 - 查询某个成员的倒序排名
     * @param string $key
     * @param string $member
     * @return int
     */
    public function zrevrank($key, $member) {
        self::debug();
        return $this->redis->zrevrank($key, $member);
    }

    /**
     * 有序列表 - 根据元素反查积分
     * @param string $key
     * @param string $member
     * @return int/float
     */
    public function zscore($key, $member) {
        self::debug();
        return $this->redis->zscore($key, $member);
    }

    /**
     * 有序列表 - 移除元素
     * @param string $key
     * @param string $member
     */
    public function zrem($key, $member) {
        self::debug();
        $this->redis->zrem($key, $member);
    }

    /**
     * 有序列表 - 移除指定区间的元素
     * @param string $key
     * @param int $start
     * @param int $stop
     */
    public function zremrangebyrank($key, $start, $stop) {
        self::debug();
        $this->redis->zremrangebyrank($key, $start, $stop);
    }

    /**
     * 有序列表 - 移除指定积分区间的元素.
     * @param string $key
     * @param int/float $min
     * @param int/float $max
     */
    public function zremrangebyscore($key, $min, $max) {
        self::debug();
        $this->redis->zremrangebyscore($key, $min, $max);
    }

    /**
     * 有序列表 - 复制有序类表到一个新的key
     * @param string $key_source 源集合
     * @param string $key_new    目标集合
     */
    public function zcopy($key_source, $key_new) {
        self::debug();
        $this->redis->zunionstore($key_new, 1, $key_source);
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc=" 其他api ">
//

    /**
     * 重命名一个key(注意: 与move的区别, move是将数据迁移到其他db,rename相当于在当前db内move)
     * @param type $key
     * @param type $newkey
     * @return boolean
     */
    public function rename($key, $newkey) {
        self::debug();
        return $this->redis->rename($key, $newkey) == "ok";
    }

    /**
     * 给指定的key设置/更新生存时间
     * @param string $key
     * @param number $seconds
     * @return bool
     */
    public function expire($key, $seconds) {
        self::debug();
        return $this->redis->expire($key, $seconds) == "ok";
    }

// == end of zSet ===
//     * @method string zincrby($key, $increment, $member)
//     * @method int    zinterstore($destination, array $keys, array $options = null)
//     * @method array  zrevrangebyscore($key, $min, $max, array $options = null)
//     * @method int    zunionstore($destination, array $keys, array $options = null)
//     * @method array  zscan($key, $cursor, array $options = null)
//     * @method array  zrangebylex($key, $start, $stop, array $options = null)
//     * @method int    zremrangebylex($key, $min, $max)
// === end of  zSet 操作 =====
// ======= hash 操作 ======
//
//      // ======== set 操作 ======
//     * @method array  sdiff(array $keys)
//     * @method int    sdiffstore($destination, array $keys)
//     * @method array  sinter(array $keys)
//     * @method int    sinterstore($destination, array $keys)
//
//     * @method int    smove($source, $destination, $member)
//     * @method string spop($key)
//     * @method array  sscan($key, $cursor, array $options = null)
//     * @method array  sunion(array $keys)
//     * @method int    sunionstore($destination, array $keys)
// ======= 订阅相关api =======s
//     * @method int    pfadd($key, array $elements)
//     * @method mixed  pfmerge($destinationKey, array $sourceKeys)
//     * @method int    pfcount(array $keys)
//     * @method mixed  pubsub($subcommand, $argument)
//     * @method int    publish($channel, $message)
//     *
// ======= 事务相关api =======
//     * @method mixed  discard()
    public function discard() {
        return $this->redis->discard();
    }

//     * @method array  exec()
//     * @method mixed  multi()
//     * @method mixed  unwatch()
//     * @method mixed  watch($key)
// ======= 执行lua脚本 =======
//     * @method mixed  eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
//     * @method mixed  evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
//     * @method mixed  script($subcommand, $argument = null)
// ======= 服务器操作api ======
//     * @method mixed  auth($password)
//     * @method string echo($message)
//     * @method mixed  ping($message = null)
//     * @method mixed  select($database)
//     * @method mixed  bgrewriteaof()
//     * @method mixed  bgsave()
// </editor-fold>
}
