PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/PhpFastCache/_extensions/SSDB.php

https://gitlab.com/dleonov/my-framework-two
PHP | 576 lines | 548 code | 6 blank | 22 comment | 77 complexity | dcc94c825db20fd2434ab7d147cd3ca9 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright (c) 2012, ideawu
  4. * All rights reserved.
  5. * @author: ideawu
  6. * @link: http://www.ideawu.com/
  7. *
  8. * SSDB PHP client SDK.
  9. */
  10. class SSDBException extends Exception
  11. {
  12. }
  13. class SSDBTimeoutException extends SSDBException
  14. {
  15. }
  16. /**
  17. * All methods(except *exists) returns false on error,
  18. * so one should use Identical(if($ret === false)) to test the return value.
  19. */
  20. class SimpleSSDB extends SSDB
  21. {
  22. function __construct($host, $port, $timeout_ms=2000){
  23. parent::__construct($host, $port, $timeout_ms);
  24. $this->easy();
  25. }
  26. }
  27. class SSDB_Response
  28. {
  29. public $cmd;
  30. public $code;
  31. public $data = null;
  32. public $message;
  33. function __construct($code='ok', $data_or_message=null){
  34. $this->code = $code;
  35. if($code == 'ok'){
  36. $this->data = $data_or_message;
  37. }else{
  38. $this->message = $data_or_message;
  39. }
  40. }
  41. function __toString(){
  42. if($this->code == 'ok'){
  43. $s = $this->data === null? '' : json_encode($this->data);
  44. }else{
  45. $s = $this->message;
  46. }
  47. return sprintf('%-13s %12s %s', $this->cmd, $this->code, $s);
  48. }
  49. function ok(){
  50. return $this->code == 'ok';
  51. }
  52. function not_found(){
  53. return $this->code == 'not_found';
  54. }
  55. }
  56. class SSDB
  57. {
  58. private $debug = false;
  59. public $sock = null;
  60. private $_closed = false;
  61. private $recv_buf = '';
  62. private $_easy = false;
  63. public $last_resp = null;
  64. function __construct($host, $port, $timeout_ms=2000){
  65. $timeout_f = (float)$timeout_ms/1000;
  66. $this->sock = @stream_socket_client("$host:$port", $errno, $errstr, $timeout_f);
  67. if(!$this->sock){
  68. throw new SSDBException("$errno: $errstr");
  69. }
  70. $timeout_sec = intval($timeout_ms/1000);
  71. $timeout_usec = ($timeout_ms - $timeout_sec * 1000) * 1000;
  72. @stream_set_timeout($this->sock, $timeout_sec, $timeout_usec);
  73. if(function_exists('stream_set_chunk_size')){
  74. @stream_set_chunk_size($this->sock, 1024 * 1024);
  75. }
  76. }
  77. function set_timeout($timeout_ms){
  78. $timeout_sec = intval($timeout_ms/1000);
  79. $timeout_usec = ($timeout_ms - $timeout_sec * 1000) * 1000;
  80. @stream_set_timeout($this->sock, $timeout_sec, $timeout_usec);
  81. }
  82. /**
  83. * After this method invoked with yesno=true, all requesting methods
  84. * will not return a SSDB_Response object.
  85. * And some certain methods like get/zget will return false
  86. * when response is not ok(not_found, etc)
  87. */
  88. function easy(){
  89. $this->_easy = true;
  90. }
  91. function close(){
  92. if(!$this->_closed){
  93. @fclose($this->sock);
  94. $this->_closed = true;
  95. $this->sock = null;
  96. }
  97. }
  98. function closed(){
  99. return $this->_closed;
  100. }
  101. private $batch_mode = false;
  102. private $batch_cmds = array();
  103. function batch(){
  104. $this->batch_mode = true;
  105. $this->batch_cmds = array();
  106. return $this;
  107. }
  108. function multi(){
  109. return $this->batch();
  110. }
  111. function exec(){
  112. $ret = array();
  113. foreach($this->batch_cmds as $op){
  114. list($cmd, $params) = $op;
  115. $this->send_req($cmd, $params);
  116. }
  117. foreach($this->batch_cmds as $op){
  118. list($cmd, $params) = $op;
  119. $resp = $this->recv_resp($cmd, $params);
  120. $resp = $this->check_easy_resp($cmd, $resp);
  121. $ret[] = $resp;
  122. }
  123. $this->batch_mode = false;
  124. $this->batch_cmds = array();
  125. return $ret;
  126. }
  127. function request(){
  128. $args = func_get_args();
  129. $cmd = array_shift($args);
  130. return $this->__call($cmd, $args);
  131. }
  132. private $async_auth_password = null;
  133. function auth($password){
  134. $this->async_auth_password = $password;
  135. return null;
  136. }
  137. function __call($cmd, $params=array()){
  138. $cmd = strtolower($cmd);
  139. if($this->async_auth_password !== null){
  140. $pass = $this->async_auth_password;
  141. $this->async_auth_password = null;
  142. $auth = $this->__call('auth', array($pass));
  143. if($auth !== true){
  144. throw new Exception("Authentication failed");
  145. }
  146. }
  147. if($this->batch_mode){
  148. $this->batch_cmds[] = array($cmd, $params);
  149. return $this;
  150. }
  151. try{
  152. if($this->send_req($cmd, $params) === false){
  153. $resp = new SSDB_Response('error', 'send error');
  154. }else{
  155. $resp = $this->recv_resp($cmd, $params);
  156. }
  157. }catch(SSDBException $e){
  158. if($this->_easy){
  159. throw $e;
  160. }else{
  161. $resp = new SSDB_Response('error', $e->getMessage());
  162. }
  163. }
  164. if($resp->code == 'noauth'){
  165. $msg = $resp->message;
  166. throw new Exception($msg);
  167. }
  168. $resp = $this->check_easy_resp($cmd, $resp);
  169. return $resp;
  170. }
  171. private function check_easy_resp($cmd, $resp){
  172. $this->last_resp = $resp;
  173. if($this->_easy){
  174. if($resp->not_found()){
  175. return NULL;
  176. }else if(!$resp->ok() && !is_array($resp->data)){
  177. return false;
  178. }else{
  179. return $resp->data;
  180. }
  181. }else{
  182. $resp->cmd = $cmd;
  183. return $resp;
  184. }
  185. }
  186. function multi_set($kvs=array()){
  187. $args = array();
  188. foreach($kvs as $k=>$v){
  189. $args[] = $k;
  190. $args[] = $v;
  191. }
  192. return $this->__call(__FUNCTION__, $args);
  193. }
  194. function multi_hset($name, $kvs=array()){
  195. $args = array($name);
  196. foreach($kvs as $k=>$v){
  197. $args[] = $k;
  198. $args[] = $v;
  199. }
  200. return $this->__call(__FUNCTION__, $args);
  201. }
  202. function multi_zset($name, $kvs=array()){
  203. $args = array($name);
  204. foreach($kvs as $k=>$v){
  205. $args[] = $k;
  206. $args[] = $v;
  207. }
  208. return $this->__call(__FUNCTION__, $args);
  209. }
  210. function incr($key, $val=1){
  211. $args = func_get_args();
  212. return $this->__call(__FUNCTION__, $args);
  213. }
  214. function decr($key, $val=1){
  215. $args = func_get_args();
  216. return $this->__call(__FUNCTION__, $args);
  217. }
  218. function zincr($name, $key, $score=1){
  219. $args = func_get_args();
  220. return $this->__call(__FUNCTION__, $args);
  221. }
  222. function zdecr($name, $key, $score=1){
  223. $args = func_get_args();
  224. return $this->__call(__FUNCTION__, $args);
  225. }
  226. function zadd($key, $score, $value){
  227. $args = array($key, $value, $score);
  228. return $this->__call('zset', $args);
  229. }
  230. function zRevRank($name, $key){
  231. $args = func_get_args();
  232. return $this->__call("zrrank", $args);
  233. }
  234. function zRevRange($name, $offset, $limit){
  235. $args = func_get_args();
  236. return $this->__call("zrrange", $args);
  237. }
  238. function hincr($name, $key, $val=1){
  239. $args = func_get_args();
  240. return $this->__call(__FUNCTION__, $args);
  241. }
  242. function hdecr($name, $key, $val=1){
  243. $args = func_get_args();
  244. return $this->__call(__FUNCTION__, $args);
  245. }
  246. private function send_req($cmd, $params){
  247. $req = array($cmd);
  248. foreach($params as $p){
  249. if(is_array($p)){
  250. $req = array_merge($req, $p);
  251. }else{
  252. $req[] = $p;
  253. }
  254. }
  255. return $this->send($req);
  256. }
  257. private function recv_resp($cmd, $params){
  258. $resp = $this->recv();
  259. if($resp === false){
  260. return new SSDB_Response('error', 'Unknown error');
  261. }else if(!$resp){
  262. return new SSDB_Response('disconnected', 'Connection closed');
  263. }
  264. if($resp[0] == 'noauth'){
  265. $errmsg = isset($resp[1])? $resp[1] : '';
  266. return new SSDB_Response($resp[0], $errmsg);
  267. }
  268. switch($cmd){
  269. case 'dbsize':
  270. case 'ping':
  271. case 'qset':
  272. case 'getbit':
  273. case 'setbit':
  274. case 'countbit':
  275. case 'strlen':
  276. case 'set':
  277. case 'setx':
  278. case 'setnx':
  279. case 'zset':
  280. case 'hset':
  281. case 'qpush':
  282. case 'qpush_front':
  283. case 'qpush_back':
  284. case 'qtrim_front':
  285. case 'qtrim_back':
  286. case 'del':
  287. case 'zdel':
  288. case 'hdel':
  289. case 'hsize':
  290. case 'zsize':
  291. case 'qsize':
  292. case 'hclear':
  293. case 'zclear':
  294. case 'qclear':
  295. case 'multi_set':
  296. case 'multi_del':
  297. case 'multi_hset':
  298. case 'multi_hdel':
  299. case 'multi_zset':
  300. case 'multi_zdel':
  301. case 'incr':
  302. case 'decr':
  303. case 'zincr':
  304. case 'zdecr':
  305. case 'hincr':
  306. case 'hdecr':
  307. case 'zget':
  308. case 'zrank':
  309. case 'zrrank':
  310. case 'zcount':
  311. case 'zsum':
  312. case 'zremrangebyrank':
  313. case 'zremrangebyscore':
  314. case 'ttl':
  315. case 'expire':
  316. if($resp[0] == 'ok'){
  317. $val = isset($resp[1])? intval($resp[1]) : 0;
  318. return new SSDB_Response($resp[0], $val);
  319. }else{
  320. $errmsg = isset($resp[1])? $resp[1] : '';
  321. return new SSDB_Response($resp[0], $errmsg);
  322. }
  323. case 'zavg':
  324. if($resp[0] == 'ok'){
  325. $val = isset($resp[1])? floatval($resp[1]) : (float)0;
  326. return new SSDB_Response($resp[0], $val);
  327. }else{
  328. $errmsg = isset($resp[1])? $resp[1] : '';
  329. return new SSDB_Response($resp[0], $errmsg);
  330. }
  331. case 'get':
  332. case 'substr':
  333. case 'getset':
  334. case 'hget':
  335. case 'qget':
  336. case 'qfront':
  337. case 'qback':
  338. if($resp[0] == 'ok'){
  339. if(count($resp) == 2){
  340. return new SSDB_Response('ok', $resp[1]);
  341. }else{
  342. return new SSDB_Response('server_error', 'Invalid response');
  343. }
  344. }else{
  345. $errmsg = isset($resp[1])? $resp[1] : '';
  346. return new SSDB_Response($resp[0], $errmsg);
  347. }
  348. break;
  349. case 'qpop':
  350. case 'qpop_front':
  351. case 'qpop_back':
  352. if($resp[0] == 'ok'){
  353. $size = 1;
  354. if(isset($params[1])){
  355. $size = intval($params[1]);
  356. }
  357. if($size <= 1){
  358. if(count($resp) == 2){
  359. return new SSDB_Response('ok', $resp[1]);
  360. }else{
  361. return new SSDB_Response('server_error', 'Invalid response');
  362. }
  363. }else{
  364. $data = array_slice($resp, 1);
  365. return new SSDB_Response('ok', $data);
  366. }
  367. }else{
  368. $errmsg = isset($resp[1])? $resp[1] : '';
  369. return new SSDB_Response($resp[0], $errmsg);
  370. }
  371. break;
  372. case 'keys':
  373. case 'zkeys':
  374. case 'hkeys':
  375. case 'hlist':
  376. case 'zlist':
  377. case 'qslice':
  378. if($resp[0] == 'ok'){
  379. $data = array();
  380. if($resp[0] == 'ok'){
  381. $data = array_slice($resp, 1);
  382. }
  383. return new SSDB_Response($resp[0], $data);
  384. }else{
  385. $errmsg = isset($resp[1])? $resp[1] : '';
  386. return new SSDB_Response($resp[0], $errmsg);
  387. }
  388. case 'auth':
  389. case 'exists':
  390. case 'hexists':
  391. case 'zexists':
  392. if($resp[0] == 'ok'){
  393. if(count($resp) == 2){
  394. return new SSDB_Response('ok', (bool)$resp[1]);
  395. }else{
  396. return new SSDB_Response('server_error', 'Invalid response');
  397. }
  398. }else{
  399. $errmsg = isset($resp[1])? $resp[1] : '';
  400. return new SSDB_Response($resp[0], $errmsg);
  401. }
  402. break;
  403. case 'multi_exists':
  404. case 'multi_hexists':
  405. case 'multi_zexists':
  406. if($resp[0] == 'ok'){
  407. if(count($resp) % 2 == 1){
  408. $data = array();
  409. for($i=1; $i<count($resp); $i+=2){
  410. $data[$resp[$i]] = (bool)$resp[$i + 1];
  411. }
  412. return new SSDB_Response('ok', $data);
  413. }else{
  414. return new SSDB_Response('server_error', 'Invalid response');
  415. }
  416. }else{
  417. $errmsg = isset($resp[1])? $resp[1] : '';
  418. return new SSDB_Response($resp[0], $errmsg);
  419. }
  420. break;
  421. case 'scan':
  422. case 'rscan':
  423. case 'zscan':
  424. case 'zrscan':
  425. case 'zrange':
  426. case 'zrrange':
  427. case 'hscan':
  428. case 'hrscan':
  429. case 'hgetall':
  430. case 'multi_hsize':
  431. case 'multi_zsize':
  432. case 'multi_get':
  433. case 'multi_hget':
  434. case 'multi_zget':
  435. case 'zpop_front':
  436. case 'zpop_back':
  437. if($resp[0] == 'ok'){
  438. if(count($resp) % 2 == 1){
  439. $data = array();
  440. for($i=1; $i<count($resp); $i+=2){
  441. if($cmd[0] == 'z'){
  442. $data[$resp[$i]] = intval($resp[$i + 1]);
  443. }else{
  444. $data[$resp[$i]] = $resp[$i + 1];
  445. }
  446. }
  447. return new SSDB_Response('ok', $data);
  448. }else{
  449. return new SSDB_Response('server_error', 'Invalid response');
  450. }
  451. }else{
  452. $errmsg = isset($resp[1])? $resp[1] : '';
  453. return new SSDB_Response($resp[0], $errmsg);
  454. }
  455. break;
  456. default:
  457. return new SSDB_Response($resp[0], array_slice($resp, 1));
  458. }
  459. return new SSDB_Response('error', 'Unknown command: $cmd');
  460. }
  461. private function send($data){
  462. $ps = array();
  463. foreach($data as $p){
  464. $ps[] = strlen($p);
  465. $ps[] = $p;
  466. }
  467. $s = join("\n", $ps) . "\n\n";
  468. if($this->debug){
  469. echo '> ' . str_replace(array("\r", "\n"), array('\r', '\n'), $s) . "\n";
  470. }
  471. try{
  472. while(true){
  473. $ret = @fwrite($this->sock, $s);
  474. if($ret === false || $ret === 0){
  475. $this->close();
  476. throw new SSDBException('Connection lost');
  477. }
  478. $s = substr($s, $ret);
  479. if(strlen($s) == 0){
  480. break;
  481. }
  482. @fflush($this->sock);
  483. }
  484. }catch(Exception $e){
  485. $this->close();
  486. throw new SSDBException($e->getMessage());
  487. }
  488. return $ret;
  489. }
  490. private function recv(){
  491. $this->step = self::STEP_SIZE;
  492. while(true){
  493. $ret = $this->parse();
  494. if($ret === null){
  495. try{
  496. $data = @fread($this->sock, 1024 * 1024);
  497. if($this->debug){
  498. echo '< ' . str_replace(array("\r", "\n"), array('\r', '\n'), $data) . "\n";
  499. }
  500. }catch(Exception $e){
  501. $data = '';
  502. }
  503. if($data === false || $data === ''){
  504. if(feof($this->sock)){
  505. $this->close();
  506. throw new SSDBException('Connection lost');
  507. }else{
  508. throw new SSDBTimeoutException('Connection timeout');
  509. }
  510. }
  511. $this->recv_buf .= $data;
  512. # echo "read " . strlen($data) . " total: " . strlen($this->recv_buf) . "\n";
  513. }else{
  514. return $ret;
  515. }
  516. }
  517. }
  518. const STEP_SIZE = 0;
  519. const STEP_DATA = 1;
  520. public $resp = array();
  521. public $step;
  522. public $block_size;
  523. private function parse(){
  524. $spos = 0;
  525. $epos = 0;
  526. $buf_size = strlen($this->recv_buf);
  527. // performance issue for large reponse
  528. //$this->recv_buf = ltrim($this->recv_buf);
  529. while(true){
  530. $spos = $epos;
  531. if($this->step === self::STEP_SIZE){
  532. $epos = strpos($this->recv_buf, "\n", $spos);
  533. if($epos === false){
  534. break;
  535. }
  536. $epos += 1;
  537. $line = substr($this->recv_buf, $spos, $epos - $spos);
  538. $spos = $epos;
  539. $line = trim($line);
  540. if(strlen($line) == 0){ // head end
  541. $this->recv_buf = substr($this->recv_buf, $spos);
  542. $ret = $this->resp;
  543. $this->resp = array();
  544. return $ret;
  545. }
  546. $this->block_size = intval($line);
  547. $this->step = self::STEP_DATA;
  548. }
  549. if($this->step === self::STEP_DATA){
  550. $epos = $spos + $this->block_size;
  551. if($epos <= $buf_size){
  552. $n = strpos($this->recv_buf, "\n", $epos);
  553. if($n !== false){
  554. $data = substr($this->recv_buf, $spos, $epos - $spos);
  555. $this->resp[] = $data;
  556. $epos = $n + 1;
  557. $this->step = self::STEP_SIZE;
  558. continue;
  559. }
  560. }
  561. break;
  562. }
  563. }
  564. // packet not ready
  565. if($spos > 0){
  566. $this->recv_buf = substr($this->recv_buf, $spos);
  567. }
  568. return null;
  569. }
  570. }