CRedisUtil.php 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  1. <?php
  2. namespace loyalsoft;
  3. //define('Redis_Debug', true); // 调试redis操作
  4. //require __DIR__ . '/Predis/Autoloader.php';
  5. require __DIR__ . '/predis-2.0.0/src/Autoloader.php'; # 2022.8.16 更新Predis代码到2.0
  6. \Predis\Autoloader::register();
  7. /**
  8. * redis操作封装,
  9. * 关于阿里云redis的支持命令和未开放命令见:
  10. * https://docs.aliyun.com/?spm=5176.7630237.9.3.WfKF0A#/pub/kvstore/quick-start/kvstore-redis-command
  11. * 腾讯云支持的命令:
  12. * https://www.qcloud.com/doc/product/239/%E4%BD%BF%E7%94%A8%E9%99%90%E5%88%B6
  13. * redis相关命令说明见:http://redisdoc.com/
  14. *
  15. * 支持命令集接口见: /phpRedisAdmin/predis/src/ClientInterface.php
  16. * @version <br/>
  17. * 1.0.4 取消公共基类CMemBase,后面可能不太会用到memcache(d)了. gwang 2020年11月23日
  18. * 1.0.3 扩展list相关api的操作方法, 针对values统一做json编解码处理.
  19. * 整理其他数据结构的操作方法, normal/hash/list api内部做json编解码处理,
  20. * set/zset value 默认不做json编解码处理, 针对set/zset的value,不推荐写入太大或复杂的数据.
  21. * gwang 2017年4月28日 <br/>
  22. * 1.0.2 扩充特有数据结构的操作方法, gwang 2016.4.21 <br/>
  23. * 1.0.1 扩充了事务的使用方法.开发竞争类业务,圣树争夺战, gwang 2016.3.21 <br/>
  24. * 1.0.0 pguan,基础功能, 方法与memcache看齐. <br/>
  25. */
  26. class CRedisUtil {
  27. // public $keyDic = null;
  28. /**
  29. *
  30. * @var ClientInterface
  31. */
  32. public $redis = null;
  33. /**
  34. * 自动回收
  35. */
  36. function __destruct() {
  37. $this->close();
  38. }
  39. private static function debug() {
  40. if (defined('Redis_Debug') && Redis_Debug) {
  41. DebugHelper::debug(DebugHelper::get_call_stack());
  42. }
  43. }
  44. // <editor-fold defaultstate="collapsed" desc=" 实现CMemBase操作接口">
  45. /**
  46. * 添加一个值
  47. * @param string $key
  48. * @param string $value
  49. * @param int $ts=0 此值无效
  50. * @return true 成功, false 失败,此key已经存在
  51. */
  52. public function add($key, $value, $ts = 0) {
  53. self::debug();
  54. return $this->setnx($key, JsonUtil::encode($value));
  55. }
  56. /**
  57. * 复制一个变量到另一个key
  58. * @param string $surKey 源数据的key
  59. * @param string $desKey 目的数据的key
  60. */
  61. public function copy($surKey, $desKey) {
  62. self::debug();
  63. return $this->set($desKey, $this->get($surKey));
  64. }
  65. /**
  66. * 一次取多个值
  67. * @param array(string) $keys
  68. * @return array
  69. */
  70. public function getMulti($keys) {
  71. self::debug();
  72. // var_dump($keys);
  73. return $this->mget($keys);
  74. }
  75. /**
  76. * 设置多个值
  77. * @param dic $dict [{"key":value},{"key":value},....]
  78. * @return true 永远成功
  79. */
  80. public function setMutlti($dict) {
  81. self::debug();
  82. $params = array();
  83. foreach ($dict as $key => $val) {
  84. $params[$key] = is_string($val) ? $val : JsonUtil::encode($val);
  85. }
  86. if (count($params) <= 0) {
  87. return TRUE;
  88. }
  89. $this->mset($params);
  90. return true;
  91. }
  92. /**
  93. * 替换某个值
  94. * @param string $key
  95. * @param string $value
  96. * @param int $ts
  97. * @return boolean true 成功,false 失败
  98. */
  99. public function replace($key, $value, $ts = 0) {
  100. self::debug();
  101. $ret = $this->redis->setex($key, $value, $ts);
  102. if (strtolower($ret) == "ok") {
  103. return true;
  104. }
  105. $this->logErr("replace" . $ret);
  106. return false;
  107. }
  108. /**
  109. * 不经过jsonencode直接存储
  110. * @deprecated since version 20160413113950 经过自己考察json_encode对数据的影响并不存在
  111. * @param type $key
  112. * @return any
  113. */
  114. public function getWithoutJson($key) {
  115. self::debug();
  116. return $this->redis->get($key);
  117. }
  118. /**
  119. * 不经过jsondecode直接返回
  120. * @deprecated since version 20160413113950 经过自己考察json_encode对数据的影响并不存在
  121. * @param string $key
  122. * @param string $value
  123. * @param int $ts
  124. * @return boolean
  125. */
  126. public function setWithoutJson($key, $value, $ts = 0) {
  127. self::debug();
  128. $ret = $this->redis->set($key, $value, $ts);
  129. if (strtolower($ret) == "ok") {
  130. return true;
  131. }
  132. $this->logErr("set" . $ret);
  133. return false;
  134. }
  135. /**
  136. * 输出日志
  137. * @param mixed $msg
  138. */
  139. private function logErr($msg) {
  140. CLog::err($msg, "Redis Err:");
  141. }
  142. /**
  143. * 连接
  144. * @param string $host
  145. * @param string $port
  146. * @param string $pwd
  147. * @param string $db 数据库, 不建议输入参数, 如果是购买云实例只有一个db.
  148. * @return \CRedisUtil
  149. */
  150. public function conn($host, $port, $pwd = "", $db = 0) {
  151. // DebugHelper::print_stack_trace();
  152. // $this->redis = new \Predis\Client(array(
  153. // $this->redis = new \Redis();
  154. // $this->redis->connect('127.0.0.1', 6379);
  155. // $this->redis->auth($pwd);
  156. $this->redis = new \Predis\Client(array(
  157. 'scheme' => 'tcp',
  158. 'host' => $host,
  159. 'port' => $port,
  160. 'password' => $pwd,
  161. ));
  162. if ($db != 0) {
  163. $this->redis->select($db);
  164. }
  165. // $this->keyDic = new \stdClass();
  166. return $this;
  167. }
  168. /**
  169. * 关闭连接
  170. */
  171. public function close() {
  172. $this->redis->quit();
  173. }
  174. /**
  175. * 获取指定键值
  176. * @param string $key
  177. * @return any json_decode($result)
  178. */
  179. public function get($key) {
  180. self::debug();
  181. $result = $this->redis->get($key);
  182. if (!$result || $result == "" || $result == null) {
  183. return null;
  184. }
  185. // $this->keyDic->$key = $result;
  186. return JsonUtil::decode($result);
  187. }
  188. /**
  189. * 将字符串值 $val 关联到 key
  190. * @param string $key
  191. * @param any $value
  192. * @param int $ts default(0) 过期时间(单位秒)
  193. * @return boolean 成功true,失败false
  194. */
  195. public function set($key, $value, $ts = 0) {
  196. self::debug();
  197. if ($value === null || $value === "") { # 这里必须用全等符号
  198. return false;
  199. }
  200. if (is_string($value)) { # 使用json序列化后存储
  201. $val = $value;
  202. } else {
  203. $val = JsonUtil::encode($value);
  204. }
  205. if ($ts > 0) { # 将会设置TTL 到期删除 ps: replaced by gwang 增加过期时间
  206. $ret = $this->redis->setex($key, $ts, $val);
  207. } else { # 0:永不过期,如果原来有TTL将会清除TTL.
  208. $ret = $this->redis->set($key, $val);
  209. }
  210. if ("OK" != $ret) {
  211. $this->logErr("Set:" . $ret);
  212. return false;
  213. }
  214. return true;
  215. }
  216. /**
  217. * 安全的写入操作
  218. * @deprecated since version 1.0 云平台暂时不支持
  219. * @param string $key
  220. * @param any $value
  221. * @return boolean 成功true,失败false
  222. */
  223. public function cas($key, $value, $ts = 0) {
  224. if (false) { # ps. 各云平台皆不支持eval方法执行lua脚本.等开放支持的时候才是真正支持cas的时候
  225. $castoken = $this->keyDic->$key;
  226. # # 定义一段脚本
  227. $script = <<<casscript
  228. if redis.call("get",KEYS[1]) == "$castoken"
  229. then
  230. return redis.call("set",KEYS[1],ARGV[1])
  231. else
  232. return 0
  233. end
  234. casscript;
  235. $ret = $this->redis->eval($script, 1, $key, $value); # redis 执行lua脚本
  236. if (0 == $ret) {
  237. return false;
  238. }
  239. return true;
  240. }
  241. // 非线程安全,原因见下
  242. return $this->set($key, $value, $ts);
  243. }
  244. /**
  245. * 删除一个指定键值
  246. * @param string $key
  247. * @return int 删除键的个数
  248. */
  249. public function delete($key) {
  250. self::debug();
  251. my_Assert(is_string($key), "arg key should be a string!");
  252. $infos = array();
  253. array_push($infos, $key);
  254. return $this->redis->del($infos);
  255. }
  256. /**
  257. * 删除一个或多个指定键值
  258. * @param array $keys
  259. * @return int 删除键的个数
  260. */
  261. public function deleteMulti($keys) {
  262. self::debug();
  263. $infos = (array) $keys; # 强制转数据类型为数组
  264. return $this->redis->del($infos);
  265. }
  266. // </editor-fold>
  267. //
  268. // <editor-fold defaultstate="collapsed" desc=" normal 普通对象操作 mget/mset">
  269. /**
  270. * 返回所有(一个或多个)给定 key 的值
  271. * 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。
  272. * @param array $keys
  273. * @return array 一个包含所有给定 key 的值的列表
  274. */
  275. private function mget($keys) {
  276. // self::debug();
  277. $ret = array();
  278. if (count($keys)) {
  279. $ctx = $this->redis->mget($keys);
  280. $mval = count($ctx);
  281. for ($i = 0;
  282. $i < $mval;
  283. $i++) {
  284. $ret[] = JsonUtil::decode($ctx[$i]);
  285. }
  286. }
  287. return $ret;
  288. }
  289. /**
  290. * Add函数拥有相同效果 \n
  291. * 将 key 的值设为 value ,当且仅当 key 不存在。
  292. * 若给定的 key 已经存在,则不做任何动作。
  293. * @param type $key
  294. * @param type $val
  295. * @return boolean 成功true,失败false
  296. */
  297. private function setnx($key, $val) {
  298. self::debug();
  299. $ret = $this->redis->setnx($key, $val);
  300. // 成功返回1, 失败返回0
  301. if (1 == $ret) {
  302. return true;
  303. } else {
  304. $this->logErr("setnx:" . $ret . "(值已存在)");
  305. }
  306. return false;
  307. }
  308. /**
  309. * 同时设置一个或多个 key-value 对
  310. * 如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值
  311. * @param array $dict
  312. * @return mixed
  313. */
  314. private function mset($dict) {
  315. self::debug();
  316. return $this->redis->mset($dict);
  317. }
  318. /**
  319. * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
  320. * 即使只有一个给定 key 已存在, MSETNX 也会拒绝执行所有给定 key 的设置操作
  321. *
  322. * @param array $dict
  323. * @return int
  324. */
  325. public function msetnx($dict) {
  326. self::debug();
  327. $ret = $this->redis->msetnx($dict);
  328. return $ret == 1;
  329. }
  330. /**
  331. * 检测指定键值是否存在
  332. * @param type $key
  333. * @return boolean true 存在, false 不存在
  334. */
  335. public function exists($key) {
  336. self::debug();
  337. return $this->redis->exists($key) == 1;
  338. }
  339. /**
  340. * 将 key 中储存的数字值增一
  341. * 如果 key 不存在,那么 key 的值会先被初始化为 0
  342. * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误
  343. * 本操作的值限制在 64 位(bit)有符号数字表示之内
  344. * @param type $key
  345. * @return int 执行 INCR 命令之后 key 的值
  346. */
  347. public function increment($key) {
  348. self::debug();
  349. return $this->redis->incr($key);
  350. }
  351. /**
  352. * 将 key 中储存的数字值增加 一个int值
  353. * 如果 key 不存在,那么 key 的值会先被初始化为 0
  354. * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误
  355. * 本操作的值限制在 64 位(bit)有符号数字表示之内
  356. * @param string $key
  357. * @param int $i 增加的值
  358. * @return int 执行 INCR 命令之后 key 的值
  359. */
  360. public function incrby($key, $i) {
  361. self::debug();
  362. return $this->redis->incrby($key, $i);
  363. }
  364. // </editor-fold>
  365. //
  366. //======================== 以下部分 added by gwang at 2016-4-14 15:17:25 =================
  367. // <editor-fold defaultstate="collapsed" desc=" 辅助函数 ">
  368. /**
  369. * Server功能,看服务器是否连通
  370. * @return boolean true连通正常
  371. */
  372. public function ping() {
  373. $ret = $this->redis->ping();
  374. if ($ret == "PONG") {
  375. return true;
  376. }
  377. $this->logErr("ping:" . $ret);
  378. return false;
  379. }
  380. /**
  381. * 对数组进行json编码
  382. * @param array $values
  383. * @return array
  384. */
  385. static private function json_encode_arr($values) {
  386. $arr = array();
  387. if (is_array($values)) {
  388. $arr = array_map(function ($v) {
  389. return JsonUtil::encode($v);
  390. }, $values);
  391. } else {
  392. $arr[] = JsonUtil::encode($values);
  393. }
  394. return $arr;
  395. }
  396. /**
  397. * 对数组进行json解码
  398. * @param array $values
  399. * @return array
  400. */
  401. static private function json_decode_arr(array $values) {
  402. $arr = array();
  403. if (is_array($values)) {
  404. $arr = array_map(function ($v) {
  405. return JsonUtil::decode($v);
  406. }, $values);
  407. } else {
  408. $arr[] = JsonUtil::decode($values);
  409. }
  410. return $arr;
  411. }
  412. // </editor-fold>
  413. //
  414. // <editor-fold defaultstate="collapsed" desc=" 事务操作 ">
  415. /**
  416. * 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
  417. * @param string $key
  418. */
  419. public function watch($key) {
  420. $this->redis->watch($key);
  421. }
  422. /**
  423. * 取消监视
  424. */
  425. public function unwatch() {
  426. $this->redis->unwatch();
  427. }
  428. /**
  429. * 开启事务
  430. */
  431. public function multi() {
  432. $this->redis->multi();
  433. }
  434. /**
  435. * 执行事务
  436. * @return type 事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。
  437. */
  438. public function exec() {
  439. return $this->redis->exec();
  440. }
  441. // </editor-fold>
  442. //
  443. // == ↓ 下面是Redis独有的数据类型操作 ↓ ==
  444. //
  445. // <editor-fold defaultstate="collapsed" desc=" set 集合操作 ">
  446. /**
  447. * 集合(set) - 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
  448. * 集合不存在时将会创建集合
  449. * @param string $key
  450. * @param string[] $members (mixed is ok, i will transmit it.)
  451. * @return int
  452. * @expectedException type not set error
  453. */
  454. public function sadd($key, $members) {
  455. self::debug();
  456. if (!is_array($members)) { # 防御: 参数不为数组
  457. if (!is_string($members)) { # 防御: 参数不为字符串
  458. $members = JsonUtil::encode($members);
  459. }
  460. $members = array($members); # 转为字符串数组
  461. }
  462. return $this->redis->sadd($key, $members);
  463. }
  464. /**
  465. * 集合(set) - 将一个或多个 member 元素从集合 key 当中移除
  466. * 集合不存在时将会创建集合
  467. * @param string $key
  468. * @param string[] $members
  469. * @return int
  470. * @expectedException type not set error
  471. */
  472. public function sremove($key, $members) {
  473. self::debug();
  474. return $this->redis->srem($key, $members);
  475. }
  476. /**
  477. * 集合(set) - 返回集合 key 的基数(集合中元素的数量)。
  478. * @param strig $key
  479. * @return int
  480. */
  481. public function scard($key) {
  482. self::debug();
  483. return $this->redis->scard($key);
  484. }
  485. /**
  486. * 集合(set) - 等效于scard: 返回集合 key 的基数(集合中元素的数量)。
  487. * @param string $key
  488. * @return int
  489. *
  490. */
  491. public function slen($key) {
  492. self::debug();
  493. return $this->scard($key);
  494. }
  495. /**
  496. * 集合(set) - 判断某个元素是否属于集合
  497. * @param string $key
  498. * @param string $member
  499. * @return bool
  500. */
  501. public function sismember($key, $member) {
  502. self::debug();
  503. return $this->redis->sismember($key, $member) == 1;
  504. }
  505. /**
  506. * 集合(set) - 返回集合 key 中的所有成员。
  507. * @param type $key
  508. * @return array 不存在的key视为空集合
  509. */
  510. public function smembers($key) {
  511. self::debug();
  512. return $this->redis->smembers($key);
  513. }
  514. /**
  515. * 集合(set) - 如果命令执行时,只提供了 key 参数,那么返回集合中的10个随机元素。
  516. * 如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
  517. * 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
  518. * @param string $key
  519. * @param int $count =10
  520. * @return array
  521. */
  522. public function srandmember($key, $count = 10) {
  523. self::debug();
  524. return $this->redis->srandmember($key, $count);
  525. }
  526. // </editor-fold>
  527. //
  528. // <editor-fold defaultstate="collapsed" desc=" hash 哈希表操作 ">
  529. /**
  530. * 哈希表 - 删除 字段
  531. * @param string $key
  532. * @param array $fields
  533. * @return int 删除的字段的数量
  534. */
  535. public function hdel($key, $fields) {
  536. self::debug();
  537. return $this->redis->hdel($key, $fields);
  538. }
  539. /**
  540. * 哈希表 - 判断field是否存在
  541. * @param string $key
  542. * @param string $field
  543. * @return boolean true=存在,false 找不到
  544. */
  545. public function hexists($key, $field) {
  546. self::debug();
  547. return $this->redis->hexists($key, $field) == 1;
  548. }
  549. /**
  550. * 哈希表 - 获取字段
  551. * @param string $key
  552. * @param string $field 字段名
  553. * @return mixed json_decoded object
  554. */
  555. public function hget($key, $field) {
  556. self::debug();
  557. $ret = $this->redis->hget($key, $field);
  558. return JsonUtil::decode($ret);
  559. }
  560. /**
  561. * 哈希表 - 获取所有字段
  562. * @param string $key
  563. * @return stdclass/associate_array (适用箭头取变量写法)
  564. */
  565. public function hgetall($key) {
  566. self::debug();
  567. $ret = ArrayInit();
  568. $arr = $this->redis->hgetall($key);
  569. foreach ($arr as $k => $v) {
  570. $ret[$k] = JsonUtil::decode($v);
  571. }
  572. // 转为关联数组({})再返回, 方便代码中使用箭头写法
  573. return JsonUtil::decode(JsonUtil::encode($ret));
  574. }
  575. /**
  576. * 哈希表 - 获取表中字段数量
  577. * @param string $key
  578. * @return int
  579. */
  580. public function hlen($key) {
  581. self::debug();
  582. return $this->redis->hlen($key);
  583. }
  584. /**
  585. * 哈希表 - 同时获取多个字段
  586. * @param string $key
  587. * @param array $fields
  588. * @return Array
  589. */
  590. public function hmget($key, $fields) {
  591. self::debug();
  592. $ret = ArrayInit();
  593. if (is_array($fields) && count($fields) > 0) {
  594. $arr = $this->redis->hmget($key, $fields);
  595. foreach ($arr as $k => $v) {
  596. $ret[$k] = JsonUtil::decode($v);
  597. }
  598. }
  599. // 转为关联数组({})再返回, 方便代码中使用箭头写法
  600. return JsonUtil::decode(JsonUtil::encode($ret));
  601. }
  602. /**
  603. * 哈希表 - 同时设置多个字段的值
  604. * @param string $key
  605. * @param assoc_array $dictionary
  606. * @return boolean true成功
  607. */
  608. public function hmset($key, $dictionary) {
  609. self::debug();
  610. $newdic = ArrayInit();
  611. foreach ($dictionary as $k => $v) {
  612. $newdic[$k] = JsonUtil::encode($v);
  613. }
  614. $ret = $this->redis->hmset($key, $newdic);
  615. if (strtolower($ret) == 'ok') {
  616. return true;
  617. }
  618. return false;
  619. }
  620. /**
  621. * 哈希表 - 同时设置多个字段的值(带Not Changed校验)
  622. * @param string $key
  623. * @param HashSaver $dic (要包含$stVer字段用以校验)
  624. * @return boolean 写入是否成功
  625. */
  626. public function hmset_Cas(string $key, HashSaver $dic) {
  627. # # 定义一段脚本
  628. static $script = <<<Lua
  629. local key,casToken,casVer, dic = KEYS[1], KEYS[2], ARGV[1], ARGV[2]
  630. local cv = redis.call("hget", key, casToken)
  631. local t = cjson.decode( dic )
  632. if not cv or tonumber(cv) < tonumber(casVer) then
  633. for k, v in pairs(t) do
  634. if type(v) =="table" then -- 加戏,如果value是对象的话, 继续encode一下.
  635. v=cjson.encode(v)
  636. end
  637. redis.call("hset", key, k, v)
  638. end
  639. return 1
  640. else
  641. return 0
  642. end
  643. Lua;
  644. try {
  645. $castoken = "stVer";
  646. $casVer = $dic->stVer;
  647. // CLog::err($dic->toString());
  648. $ret = $this->redis->eval($script, 2, $key, $castoken, $casVer, $dic->toString()); # redis 执行lua脚本
  649. // CLog::err($dic->toString());
  650. if (0 == $ret) {
  651. return false;
  652. }
  653. } catch (\Exception $e) {
  654. CLog::err($e->getMessage());
  655. return false;
  656. }
  657. return true;
  658. }
  659. /**
  660. * 哈希表 - 设置或新建一个字段的值
  661. * @param string $key
  662. * @param string $field
  663. * @param $mixed $value
  664. * @return int 0 覆盖旧值, 1 新建字段
  665. */
  666. public function hset($key, $field, $value) {
  667. self::debug();
  668. return $this->redis->hset($key, $field, JsonUtil::encode($value));
  669. }
  670. /**
  671. * 哈希表 - 增加一个新的field,已经存在则放弃操作返回false
  672. * @param string $key
  673. * @param string $field
  674. * @param string $value
  675. * @return boolean true成功
  676. */
  677. public function hsetnx($key, $field, $value) {
  678. self::debug();
  679. return $this->redis->hsetnx($key, $field, JsonUtil::encode($value)) == 1;
  680. }
  681. /**
  682. * 哈希表 - 按字段赠加 int值
  683. * @param string $key
  684. * @param string $field
  685. * @param int $increment
  686. * @return int
  687. */
  688. public function hincrby($key, $field, $increment) {
  689. self::debug();
  690. return $this->redis->hincrby($key, $field, $increment);
  691. }
  692. /**
  693. * 哈希表 - 按字段增加 float值
  694. * @param string $key
  695. * @param string $field
  696. * @param string $increment float值用string
  697. * @return string 用string存float值
  698. */
  699. public function hincrbyfloat($key, $field, $increment) {
  700. self::debug();
  701. return $this->redis->hincrbyfloat($key, $field, $increment);
  702. }
  703. /**
  704. * @param string $key
  705. * @return array
  706. */
  707. public function hkeys($key) {
  708. self::debug();
  709. return $this->redis->hkeys($key);
  710. }
  711. // * @method array hscan($key, $cursor, array $options = null)
  712. // * @method array hvals($key)
  713. // </editor-fold>
  714. //
  715. // <editor-fold defaultstate="collapsed" desc=" list 链表操作 ">
  716. //
  717. // <editor-fold defaultstate="collapsed" desc=" 阻塞的链表操作 ">
  718. /**
  719. * 链表 - 阻塞弹出, 当列表中没有元素的时候,命令会阻塞,直到等待超时或者有元素被添加到列表中.
  720. * @param array $keys 可以指定多个数组
  721. * @param int $timeout
  722. * @return array
  723. */
  724. public function blpop(array $keys, $timeout) {
  725. self::debug();
  726. $arr = $this->redis->blpop($keys, $timeout);
  727. $arr[1] = JsonUtil::decode($arr[1]); // 对value进行解包
  728. return $arr;
  729. }
  730. /**
  731. * 链表 - 阻塞队尾弹出, 当列表中没有元素的时候,命令会阻塞,直到等待超时或者有元素被添加到列表中.
  732. * @return array
  733. */
  734. public function brpop(array $keys, $timeout) {
  735. self::debug();
  736. $arr = $this->redis->brpop($keys, $timeout);
  737. $arr[1] = JsonUtil::decode($arr[1]);
  738. return $arr;
  739. }
  740. /**
  741. * 链表 - 阻塞版的 队尾弹出插入到队首. 本操作是原子的.
  742. *
  743. * @return array 假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长。
  744. * 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素的值,第二个元素是等待时长。
  745. */
  746. public function brpoplpush($source, $destination, $timeout) {
  747. self::debug();
  748. $arr = $this->redis > brpoplpush($source, $destination, $timeout);
  749. $arr[0] = JsonUtil::decode($arr[0]);
  750. return $arr;
  751. }
  752. // </editor-fold>
  753. //
  754. /**
  755. * 链表 - 返回下标为index的元素
  756. * @return string
  757. */
  758. public function lindex($key, $index) {
  759. self::debug();
  760. $a = $this->redis->lindex($key, $index);
  761. return JsonUtil::decode($a);
  762. }
  763. /**
  764. * 链表 - 返回 链表key的长度
  765. * @return int
  766. * @param type $key
  767. */
  768. public function llen($key) {
  769. self::debug();
  770. return $this->redis->llen($key);
  771. }
  772. /**
  773. * 链表 - 移除并返回链表的队首元素.
  774. * @return string
  775. * @param type $key
  776. */
  777. public function lpop($key) {
  778. self::debug();
  779. $a = $this->redis->lpop($key);
  780. return JsonUtil::decode($a);
  781. }
  782. /**
  783. * 链表 - 向链表插入一个或多个值,链表不存在时创建链表并插入元素
  784. * @return int key存在却不是链表类型时返回一个错误.
  785. * @param type $key
  786. * @param array $values
  787. */
  788. public function lpush($key, $values) {
  789. self::debug();
  790. $arr = self::json_encode_arr($values);
  791. return $this->redis->lpush($key, $arr);
  792. }
  793. /**
  794. * 链表 - 取链表中指定下标区间的元素
  795. * @return array
  796. * @param string $key
  797. * @param int $start
  798. * @param int $stop
  799. */
  800. public function lrange($key, $start, $stop) {
  801. self::debug();
  802. $values = $this->redis->lrange($key, $start, $stop);
  803. return self::json_decode_arr($values);
  804. }
  805. /**
  806. * 链表 - 移除链表中 count 个值为 value的元素.
  807. * @return int
  808. * @param string $key
  809. * @param int $count
  810. * @param string $value
  811. */
  812. public function lrem($key, $count, $value) {
  813. self::debug();
  814. return $this->redis->lrem($key, $count, JsonUtil::encode($value));
  815. }
  816. /**
  817. * 链表 - 将链表中下标为index的 元素设置为value
  818. * @return mixed
  819. *
  820. * @param type $key
  821. * @param type $index
  822. * @param type $value
  823. */
  824. public function lset($key, $index, $value) {
  825. self::debug();
  826. return $this->redis->lset($key, $index, JsonUtil::encode($value));
  827. }
  828. /**
  829. * 链表 - 对链表进行修剪, 让链表只保留指定下标区间的元素,其余元素删除
  830. * @return bool true 成功, false 失败
  831. * @param string $key
  832. * @param int $start
  833. * @param int $stop
  834. */
  835. public function ltrim($key, $start, $stop) {
  836. self::debug();
  837. // return mixed: ok or fails
  838. $ret = $this->redis->ltrim($key, $start, $stop);
  839. if (strtolower($ret) == 'ok') {
  840. return true;
  841. }
  842. return false;
  843. }
  844. /**
  845. * 链表 - 从链表右侧(尾部)弹出一个元素,并返回该元素
  846. * @param string $key
  847. * @return string 列表的尾元素。当 key 不存在时,返回 nil
  848. */
  849. public function rpop($key) {
  850. self::debug();
  851. return JsonUtil::decode($this->redis->rpop($key));
  852. }
  853. /**
  854. * 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
  855. * @param string $key
  856. * @param array $values
  857. * @return int
  858. */
  859. public function rpush($key, array $values) {
  860. self::debug();
  861. $arr = self::json_encode_arr($values);
  862. return $this->redis->rpush($key, $arr);
  863. }
  864. //
  865. // /**
  866. // * 链表 - 向链表中插入一个或多个值,链表不存在时,什么也不做.
  867. // * @return int
  868. // * @param type $key
  869. // * @param type $value
  870. // */
  871. // public function lpushx($key, $value)
  872. // {
  873. // return $this->redis->lpushx($key, $value);
  874. // }
  875. // /**
  876. // * 链表 - 将值 value 插入到链表 key 中 , 位于pivot之前或之后
  877. // * @return int -1:未找到pivot, 0:链表为空或者不存在.
  878. // *
  879. // * @param string $key 链表名称
  880. // * @param string $whence after/before pivot之前或之后
  881. // * @param string $pivot 插入pivot之前或之后
  882. // * @param string $value 要插入的value
  883. // */
  884. // public function linsert($key, $whence, $pivot, $value)
  885. // {
  886. // $this->redis->linsert($key, $whence, $pivot, $value);
  887. // }
  888. // /**
  889. // * 在一个原子时间内,执行以下两个动作
  890. // * 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
  891. // * 将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素.
  892. // * @return string
  893. // */
  894. // public function rpoplpush($source, $destination)
  895. // {
  896. // throw new Exception("un implemented now!");
  897. // }
  898. // /**
  899. // * 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表。
  900. // * 和 RPUSH 命令相反,当 key 不存在时, RPUSHX 命令什么也不做。
  901. // * @param type $key
  902. // * @param type $value
  903. // * @return int
  904. // */
  905. // public function rpushx($key, $value)
  906. // {
  907. // throw new Exception("un implemented now!");
  908. // }
  909. // </editor-fold>
  910. //
  911. // <editor-fold defaultstate="collapsed" desc=" zset 有序集合 ">
  912. /**
  913. * 有序集合 - 添加或更新元素 对于zset类型不建议写入太长, 太复杂的value
  914. * @param string $key
  915. * @param array $membersAndScoresDictionary array( 'value'=>score,'value'=>score,...), score可以是int或float
  916. * @return int 新增元素数量,不包含更新的元素
  917. */
  918. public function zadd($key, $membersAndScoresDictionary) {
  919. self::debug();
  920. return $this->redis->zadd($key, $membersAndScoresDictionary);
  921. }
  922. /**
  923. * 有序集合 - 给某个元素增加积分
  924. * @param string $key
  925. * @param string $member
  926. * @param int $increment
  927. * @return string
  928. */
  929. public function zincrby($key, $member, $increment = 1) {
  930. self::debug();
  931. return $this->redis->zincrby($key, $increment, $member);
  932. }
  933. /**
  934. * 有序集合 - 元素数量
  935. * @param string $key
  936. * @return int
  937. */
  938. public function zlen($key) {
  939. self::debug();
  940. return $this->redis->zcard($key);
  941. }
  942. /**
  943. * 有序集合 - 统计某区间元素数量
  944. * @param string $key
  945. * @param int/float $min 最低分
  946. * @param int/float $max 最高分
  947. * @return int
  948. */
  949. public function zcount($key, $min, $max) {
  950. self::debug();
  951. return $this->redis->zcount($key, $min, $max);
  952. }
  953. /**
  954. * 有序集合 - 获取集合指定区间内的成员, 第一个成员从0开始
  955. * @param string $key
  956. * @param int $start
  957. * @param int $stop Ps.以 -1 表示最后一个成员, -2 表示倒数第二个成员
  958. * @param boolean $withScore 返回值中是否带score字段
  959. * @return array (index=>member) or assoc_array (member=>score)
  960. */
  961. public function zrange($key, $start, $stop, $withScore = false) {
  962. self::debug();
  963. if ($withScore) {
  964. return $this->redis->zrange($key, $start, $stop, 'WITHSCORES');
  965. } else {
  966. return $this->redis->zrange($key, $start, $stop);
  967. }
  968. if ($withScore) {
  969. $ret = $this->redis->zrange($key, $start, $stop, 'WITHSCORES');
  970. DebugHelper::var_dump($ret);
  971. $ma = count($ret);
  972. $arr = array();
  973. for ($i = 0; $i < $ma; $i += 2) {
  974. $arr[$ret[$i]] = $ret[$i + 1];
  975. }
  976. return $arr;
  977. } else {
  978. $ret = $this->redis->zrange($key, $start, $stop);
  979. DebugHelper::var_dump($ret);
  980. return $ret;
  981. }
  982. }
  983. /**
  984. *
  985. * 有序集合 - 获取集合倒序之后,指定区间内的成员, 第一个成员从0开始
  986. * @param string $key
  987. * @param int $start
  988. * @param int $stop Ps.以 -1 表示最后一个成员, -2 表示倒数第二个成员
  989. * @param boolean $withScore 返回值中是否带score字段 default(false)
  990. * @return array (index=>member) or assoc_array (member=>score) 用foreach(=>)取
  991. */
  992. public function zrevrange($key, $start, $stop, $withScore = false) {
  993. self::debug();
  994. if ($withScore) {
  995. $r = $this->redis->zrevrange($key, $start, $stop, 'WITHSCORES');
  996. return $r;
  997. } else {
  998. return $this->redis->zrevrange($key, $start, $stop);
  999. }
  1000. }
  1001. /**
  1002. *
  1003. * 有序列表 - 获取指定积分区间内的成员
  1004. * @param string $key
  1005. * @param int/float $min
  1006. * @param int/float $max
  1007. * @param boolean $withScore
  1008. * @param bool $limit 是否限制返回值大小
  1009. * @param int $offset 限制返回结果的起始位置
  1010. * @param type $count 返回结果的数量
  1011. * @return type
  1012. */
  1013. public function zrangebyscore($key, $min, $max, $withScore = false, $limit = false, $offset = 0, $count = 10) {
  1014. self::debug();
  1015. if ($limit) {
  1016. if ($withScore) {
  1017. return $this->redis->zrangebyscore($key, $min, $max, 'WITHSCORES', "LIMIT", $offset, $count);
  1018. } else {
  1019. return $this->redis->zrangebyscore($key, $min, $max, 'LIMIT', $offset, $count);
  1020. }
  1021. } else {
  1022. if ($withScore) {
  1023. return $this->redis->zrangebyscore($key, $min, $max, 'WITHSCORES');
  1024. } else {
  1025. return $this->redis->zrangebyscore($key, $min, $max);
  1026. }
  1027. }
  1028. }
  1029. /**
  1030. *
  1031. * 有序列表 - 获取按照倒序排序后指定积分区间内的成员
  1032. * @param string $key
  1033. * @param int/float $max
  1034. * @param int/float $min
  1035. * @param boolean $withScore
  1036. * @param bool $limit 是否限制返回值大小
  1037. * @param int $offset 限制返回结果的起始位置
  1038. * @param type $count 返回结果的数量
  1039. * @return type
  1040. */
  1041. public function zrevrangebyscore($key, $max, $min, $withScore = false, $limit = false, $offset = 0, $count = 10) {
  1042. self::debug();
  1043. if ($limit) {
  1044. if ($withScore) {
  1045. return $this->redis->zrevrangebyscore($key, $max, $min, 'WITHSCORES', "LIMIT", $offset, $count);
  1046. } else {
  1047. return $this->redis->zrevrangebyscore($key, $max, $min, 'LIMIT', $offset, $count);
  1048. }
  1049. } else {
  1050. if ($withScore) {
  1051. return $this->redis->zrevrangebyscore($key, $max, $min, 'WITHSCORES');
  1052. } else {
  1053. return $this->redis->zrevrangebyscore($key, $max, $min);
  1054. }
  1055. }
  1056. }
  1057. /**
  1058. * 有序列表 - 根据元素反查排名
  1059. * @param string $key
  1060. * @param string $member
  1061. * @return int
  1062. */
  1063. public function zrank($key, $member) {
  1064. self::debug();
  1065. return $this->redis->zrank($key, $member);
  1066. }
  1067. /**
  1068. * 有序列表 - 查询某个成员的倒序排名
  1069. * @param string $key
  1070. * @param string $member
  1071. * @return int
  1072. */
  1073. public function zrevrank($key, $member) {
  1074. self::debug();
  1075. return $this->redis->zrevrank($key, $member);
  1076. }
  1077. /**
  1078. * 有序列表 - 根据元素反查积分
  1079. * @param string $key
  1080. * @param string $member
  1081. * @return int/float
  1082. */
  1083. public function zscore($key, $member) {
  1084. self::debug();
  1085. return $this->redis->zscore($key, $member);
  1086. }
  1087. /**
  1088. * 有序列表 - 移除元素
  1089. * @param string $key
  1090. * @param string $member
  1091. */
  1092. public function zrem($key, $member) {
  1093. self::debug();
  1094. $this->redis->zrem($key, $member);
  1095. }
  1096. /**
  1097. * 有序列表 - 移除指定区间的元素
  1098. * @param string $key
  1099. * @param int $start
  1100. * @param int $stop
  1101. */
  1102. public function zremrangebyrank($key, $start, $stop) {
  1103. self::debug();
  1104. $this->redis->zremrangebyrank($key, $start, $stop);
  1105. }
  1106. /**
  1107. * 有序列表 - 移除指定积分区间的元素.
  1108. * @param string $key
  1109. * @param int/float $min
  1110. * @param int/float $max
  1111. */
  1112. public function zremrangebyscore($key, $min, $max) {
  1113. self::debug();
  1114. $this->redis->zremrangebyscore($key, $min, $max);
  1115. }
  1116. /**
  1117. * 有序列表 - 复制有序类表到一个新的key
  1118. * @param string $key_source 源集合
  1119. * @param string $key_new 目标集合
  1120. */
  1121. public function zcopy($key_source, $key_new) {
  1122. self::debug();
  1123. $this->redis->zunionstore($key_new, 1, $key_source);
  1124. }
  1125. // </editor-fold>
  1126. //
  1127. // <editor-fold defaultstate="collapsed" desc=" 其他api ">
  1128. //
  1129. /**
  1130. * 重命名一个key(注意: 与move的区别, move是将数据迁移到其他db,rename相当于在当前db内move)
  1131. * @param type $key
  1132. * @param type $newkey
  1133. * @return boolean
  1134. */
  1135. public function rename($key, $newkey) {
  1136. self::debug();
  1137. return $this->redis->rename($key, $newkey) == "ok";
  1138. }
  1139. /**
  1140. * 给指定的key设置/更新生存时间
  1141. * @param string $key
  1142. * @param number $seconds
  1143. * @return bool
  1144. */
  1145. public function expire($key, $seconds) {
  1146. self::debug();
  1147. return $this->redis->expire($key, $seconds) == "ok";
  1148. }
  1149. // == end of zSet ===
  1150. // * @method string zincrby($key, $increment, $member)
  1151. // * @method int zinterstore($destination, array $keys, array $options = null)
  1152. // * @method array zrevrangebyscore($key, $min, $max, array $options = null)
  1153. // * @method int zunionstore($destination, array $keys, array $options = null)
  1154. // * @method array zscan($key, $cursor, array $options = null)
  1155. // * @method array zrangebylex($key, $start, $stop, array $options = null)
  1156. // * @method int zremrangebylex($key, $min, $max)
  1157. // === end of zSet 操作 =====
  1158. // ======= hash 操作 ======
  1159. //
  1160. // // ======== set 操作 ======
  1161. // * @method array sdiff(array $keys)
  1162. // * @method int sdiffstore($destination, array $keys)
  1163. // * @method array sinter(array $keys)
  1164. // * @method int sinterstore($destination, array $keys)
  1165. //
  1166. // * @method int smove($source, $destination, $member)
  1167. // * @method string spop($key)
  1168. // * @method array sscan($key, $cursor, array $options = null)
  1169. // * @method array sunion(array $keys)
  1170. // * @method int sunionstore($destination, array $keys)
  1171. // ======= 订阅相关api =======s
  1172. // * @method int pfadd($key, array $elements)
  1173. // * @method mixed pfmerge($destinationKey, array $sourceKeys)
  1174. // * @method int pfcount(array $keys)
  1175. // * @method mixed pubsub($subcommand, $argument)
  1176. // * @method int publish($channel, $message)
  1177. // *
  1178. // ======= 事务相关api =======
  1179. // * @method mixed discard()
  1180. public function discard() {
  1181. return $this->redis->discard();
  1182. }
  1183. // * @method array exec()
  1184. // * @method mixed multi()
  1185. // * @method mixed unwatch()
  1186. // * @method mixed watch($key)
  1187. // ======= 执行lua脚本 =======
  1188. // * @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
  1189. // * @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
  1190. // * @method mixed script($subcommand, $argument = null)
  1191. // ======= 服务器操作api ======
  1192. // * @method mixed auth($password)
  1193. // * @method string echo($message)
  1194. // * @method mixed ping($message = null)
  1195. // * @method mixed select($database)
  1196. // * @method mixed bgrewriteaof()
  1197. // * @method mixed bgsave()
  1198. // </editor-fold>
  1199. }