CRedisUtil.php 40 KB

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