PageRenderTime 32ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/library.c

https://gitlab.com/oytunistrator/phpredis
C | 1732 lines | 1286 code | 260 blank | 186 comment | 269 complexity | c98cd6f8d7cbc54ae17b6a63d3a92398 MD5 | raw file
  1. #ifdef HAVE_CONFIG_H
  2. #include "config.h"
  3. #endif
  4. #include "common.h"
  5. #include "php_network.h"
  6. #include <sys/types.h>
  7. #ifndef _MSC_VER
  8. #include <netinet/tcp.h> /* TCP_NODELAY */
  9. #include <sys/socket.h>
  10. #endif
  11. #include <ext/standard/php_smart_str.h>
  12. #include <ext/standard/php_var.h>
  13. #ifdef HAVE_REDIS_IGBINARY
  14. #include "igbinary/igbinary.h"
  15. #endif
  16. #include <zend_exceptions.h>
  17. #include "php_redis.h"
  18. #include "library.h"
  19. #include "redis_commands.h"
  20. #include <ext/standard/php_math.h>
  21. #include <ext/standard/php_rand.h>
  22. #define UNSERIALIZE_NONE 0
  23. #define UNSERIALIZE_KEYS 1
  24. #define UNSERIALIZE_VALS 2
  25. #define UNSERIALIZE_ALL 3
  26. #define SCORE_DECODE_NONE 0
  27. #define SCORE_DECODE_INT 1
  28. #define SCORE_DECODE_DOUBLE 2
  29. #ifdef PHP_WIN32
  30. # if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4
  31. /* This proto is available from 5.5 on only */
  32. PHP_REDIS_API int usleep(unsigned int useconds);
  33. # endif
  34. #endif
  35. extern zend_class_entry *redis_ce;
  36. extern zend_class_entry *redis_exception_ce;
  37. extern zend_class_entry *spl_ce_RuntimeException;
  38. /* Helper to reselect the proper DB number when we reconnect */
  39. static int reselect_db(RedisSock *redis_sock TSRMLS_DC) {
  40. char *cmd, *response;
  41. int cmd_len, response_len;
  42. cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber);
  43. if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
  44. efree(cmd);
  45. return -1;
  46. }
  47. efree(cmd);
  48. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
  49. return -1;
  50. }
  51. if (strncmp(response, "+OK", 3)) {
  52. efree(response);
  53. return -1;
  54. }
  55. efree(response);
  56. return 0;
  57. }
  58. /* Helper to resend AUTH <password> in the case of a reconnect */
  59. static int resend_auth(RedisSock *redis_sock TSRMLS_DC) {
  60. char *cmd, *response;
  61. int cmd_len, response_len;
  62. cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", redis_sock->auth,
  63. strlen(redis_sock->auth));
  64. if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
  65. efree(cmd);
  66. return -1;
  67. }
  68. efree(cmd);
  69. response = redis_sock_read(redis_sock, &response_len TSRMLS_CC);
  70. if (response == NULL) {
  71. return -1;
  72. }
  73. if (strncmp(response, "+OK", 3)) {
  74. efree(response);
  75. return -1;
  76. }
  77. efree(response);
  78. return 0;
  79. }
  80. /* Helper function that will throw an exception for a small number of ERR codes
  81. * returned by Redis. Typically we just return FALSE to the caller in the event
  82. * of an ERROR reply, but for the following error types:
  83. * 1) MASTERDOWN
  84. * 2) AUTH
  85. * 3) LOADING
  86. */
  87. static void redis_error_throw(char *err, size_t err_len TSRMLS_DC) {
  88. /* Handle stale data error (slave syncing with master) */
  89. if (err_len == sizeof(REDIS_ERR_SYNC_MSG) - 1 &&
  90. !memcmp(err,REDIS_ERR_SYNC_KW,sizeof(REDIS_ERR_SYNC_KW)-1))
  91. {
  92. zend_throw_exception(redis_exception_ce,
  93. "SYNC with master in progress or master down!", 0 TSRMLS_CC);
  94. } else if (err_len == sizeof(REDIS_ERR_LOADING_MSG) - 1 &&
  95. !memcmp(err,REDIS_ERR_LOADING_KW,sizeof(REDIS_ERR_LOADING_KW)-1))
  96. {
  97. zend_throw_exception(redis_exception_ce,
  98. "Redis is LOADING the dataset", 0 TSRMLS_CC);
  99. } else if (err_len == sizeof(REDIS_ERR_AUTH_MSG) -1 &&
  100. !memcmp(err,REDIS_ERR_AUTH_KW,sizeof(REDIS_ERR_AUTH_KW)-1))
  101. {
  102. zend_throw_exception(redis_exception_ce,
  103. "Failed to AUTH connection", 0 TSRMLS_CC);
  104. }
  105. }
  106. PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) {
  107. if (!redis_sock->persistent) {
  108. php_stream_close(redis_sock->stream);
  109. } else {
  110. php_stream_pclose(redis_sock->stream);
  111. }
  112. }
  113. PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC)
  114. {
  115. int eof;
  116. int count = 0;
  117. if (!redis_sock->stream) {
  118. return -1;
  119. }
  120. /* NOITCE: set errno = 0 here
  121. *
  122. * There is a bug in php socket stream to check liveness of a connection:
  123. * if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) {
  124. * alive = 0;
  125. * }
  126. * If last errno is EWOULDBLOCK and recv returns 0 because of connection closed, alive would not be
  127. * set to 0. However, the connection is close indeed. The php_stream_eof is not reliable. This will
  128. * cause a "read error on connection" exception when use a closed persistent connection.
  129. *
  130. * We work around this by set errno = 0 first.
  131. *
  132. * Bug fix of php: https://github.com/php/php-src/pull/1456
  133. * */
  134. errno = 0;
  135. eof = php_stream_eof(redis_sock->stream);
  136. for (; eof; count++) {
  137. if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) {
  138. /* too many failures */
  139. if(redis_sock->stream) { /* close stream if still here */
  140. redis_stream_close(redis_sock TSRMLS_CC);
  141. redis_sock->stream = NULL;
  142. redis_sock->mode = ATOMIC;
  143. redis_sock->status = REDIS_SOCK_STATUS_FAILED;
  144. redis_sock->watching = 0;
  145. }
  146. if(!no_throw) {
  147. zend_throw_exception(redis_exception_ce, "Connection lost",
  148. 0 TSRMLS_CC);
  149. }
  150. return -1;
  151. }
  152. if(redis_sock->stream) { /* close existing stream before reconnecting */
  153. redis_stream_close(redis_sock TSRMLS_CC);
  154. redis_sock->stream = NULL;
  155. redis_sock->mode = ATOMIC;
  156. redis_sock->watching = 0;
  157. }
  158. // Wait for a while before trying to reconnect
  159. if (redis_sock->retry_interval) {
  160. // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
  161. long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval));
  162. usleep(retry_interval);
  163. }
  164. redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */
  165. if(redis_sock->stream) { /* check for EOF again. */
  166. errno = 0;
  167. eof = php_stream_eof(redis_sock->stream);
  168. }
  169. }
  170. /* We've connected if we have a count */
  171. if (count) {
  172. /* If we're using a password, attempt a reauthorization */
  173. if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) {
  174. return -1;
  175. }
  176. /* If we're using a non-zero db, reselect it */
  177. if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) {
  178. return -1;
  179. }
  180. }
  181. /* Success */
  182. return 0;
  183. }
  184. PHP_REDIS_API int
  185. redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  186. REDIS_SCAN_TYPE type, long *iter)
  187. {
  188. REDIS_REPLY_TYPE reply_type;
  189. int reply_info;
  190. char *p_iter;
  191. /* Our response should have two multibulk replies */
  192. if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
  193. || reply_type != TYPE_MULTIBULK || reply_info != 2)
  194. {
  195. return -1;
  196. }
  197. /* The BULK response iterator */
  198. if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
  199. || reply_type != TYPE_BULK)
  200. {
  201. return -1;
  202. }
  203. /* Attempt to read the iterator */
  204. if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info TSRMLS_CC))) {
  205. return -1;
  206. }
  207. /* Push the iterator out to the caller */
  208. *iter = atol(p_iter);
  209. efree(p_iter);
  210. /* Read our actual keys/members/etc differently depending on what kind of
  211. scan command this is. They all come back in slightly different ways */
  212. switch(type) {
  213. case TYPE_SCAN:
  214. return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU,
  215. redis_sock, NULL, NULL);
  216. case TYPE_SSCAN:
  217. return redis_sock_read_multibulk_reply(
  218. INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
  219. case TYPE_ZSCAN:
  220. return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU,
  221. redis_sock, NULL, NULL);
  222. case TYPE_HSCAN:
  223. return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU,
  224. redis_sock, NULL, NULL);
  225. default:
  226. return -1;
  227. }
  228. }
  229. PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
  230. RedisSock *redis_sock, zval *z_tab,
  231. void *ctx)
  232. {
  233. subscribeContext *sctx = (subscribeContext*)ctx;
  234. zval **z_tmp, *z_ret, **z_args[4];
  235. // Consume response(s) from subscribe, which will vary on argc
  236. while(sctx->argc--) {
  237. z_tab = redis_sock_read_multibulk_reply_zval(
  238. INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
  239. if(!z_tab) {
  240. efree(sctx);
  241. return -1;
  242. }
  243. // We'll need to find the command response
  244. if(zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_tmp)
  245. ==FAILURE)
  246. {
  247. zval_dtor(z_tab);
  248. efree(z_tab);
  249. efree(sctx);
  250. return -1;
  251. }
  252. // Make sure the command response matches the command we called
  253. if(strcasecmp(Z_STRVAL_PP(z_tmp), sctx->kw) !=0) {
  254. zval_dtor(z_tab);
  255. efree(z_tab);
  256. efree(sctx);
  257. return -1;
  258. }
  259. zval_dtor(z_tab);
  260. efree(z_tab);
  261. }
  262. sctx->cb.retval_ptr_ptr = &z_ret;
  263. sctx->cb.params = z_args;
  264. sctx->cb.no_separation = 0;
  265. /* Multibulk response, {[pattern], type, channel, payload } */
  266. while(1) {
  267. zval **z_type, **z_chan, **z_pat, **z_data;
  268. HashTable *ht_tab;
  269. int tab_idx=1, is_pmsg;
  270. z_tab = redis_sock_read_multibulk_reply_zval(
  271. INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
  272. if(z_tab == NULL) break;
  273. ht_tab = Z_ARRVAL_P(z_tab);
  274. if(zend_hash_index_find(ht_tab, 0, (void**)&z_type)==FAILURE ||
  275. Z_TYPE_PP(z_type) != IS_STRING)
  276. {
  277. break;
  278. }
  279. // Check for message or pmessage
  280. if(!strncmp(Z_STRVAL_PP(z_type), "message", 7) ||
  281. !strncmp(Z_STRVAL_PP(z_type), "pmessage", 8))
  282. {
  283. is_pmsg = *Z_STRVAL_PP(z_type)=='p';
  284. } else {
  285. break;
  286. }
  287. // Extract pattern if it's a pmessage
  288. if(is_pmsg) {
  289. if(zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_pat)
  290. ==FAILURE)
  291. {
  292. break;
  293. }
  294. }
  295. // Extract channel and data
  296. if(zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_chan)==FAILURE ||
  297. zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_data)==FAILURE)
  298. {
  299. break;
  300. }
  301. // Different args for SUBSCRIBE and PSUBSCRIBE
  302. z_args[0] = &getThis();
  303. if(is_pmsg) {
  304. z_args[1] = z_pat;
  305. z_args[2] = z_chan;
  306. z_args[3] = z_data;
  307. } else {
  308. z_args[1] = z_chan;
  309. z_args[2] = z_data;
  310. }
  311. // Set arg count
  312. sctx->cb.param_count = tab_idx;
  313. // Execute callback
  314. if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC)
  315. ==FAILURE)
  316. {
  317. break;
  318. }
  319. // If we have a return value free it
  320. if(z_ret) zval_ptr_dtor(&z_ret);
  321. zval_dtor(z_tab);
  322. efree(z_tab);
  323. }
  324. // This is an error state, clean up
  325. if(z_tab) {
  326. zval_dtor(z_tab);
  327. efree(z_tab);
  328. }
  329. efree(sctx);
  330. return -1;
  331. }
  332. PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
  333. RedisSock *redis_sock, zval *z_tab,
  334. void *ctx)
  335. {
  336. subscribeContext *sctx = (subscribeContext*)ctx;
  337. zval **z_chan, *z_ret;
  338. int i=0;
  339. MAKE_STD_ZVAL(z_ret);
  340. array_init(z_ret);
  341. while(i<sctx->argc) {
  342. z_tab = redis_sock_read_multibulk_reply_zval(
  343. INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
  344. if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1,
  345. (void**)&z_chan)==FAILURE)
  346. {
  347. zval_dtor(z_ret);
  348. efree(z_ret);
  349. return -1;
  350. }
  351. add_assoc_bool(z_ret, Z_STRVAL_PP(z_chan), 1);
  352. zval_dtor(z_tab);
  353. efree(z_tab);
  354. i++;
  355. }
  356. efree(sctx);
  357. RETVAL_ZVAL(z_ret, 0, 1);
  358. // Success
  359. return 0;
  360. }
  361. PHP_REDIS_API zval *
  362. redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS,
  363. RedisSock *redis_sock) {
  364. char inbuf[1024];
  365. int numElems;
  366. zval *z_tab;
  367. if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) {
  368. return NULL;
  369. }
  370. if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) {
  371. redis_stream_close(redis_sock TSRMLS_CC);
  372. redis_sock->stream = NULL;
  373. redis_sock->status = REDIS_SOCK_STATUS_FAILED;
  374. redis_sock->mode = ATOMIC;
  375. redis_sock->watching = 0;
  376. zend_throw_exception(redis_exception_ce,
  377. "read error on connection", 0 TSRMLS_CC);
  378. return NULL;
  379. }
  380. if(inbuf[0] != '*') {
  381. return NULL;
  382. }
  383. numElems = atoi(inbuf+1);
  384. MAKE_STD_ZVAL(z_tab);
  385. array_init(z_tab);
  386. redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab,
  387. numElems, UNSERIALIZE_ALL);
  388. return z_tab;
  389. }
  390. /**
  391. * redis_sock_read_bulk_reply
  392. */
  393. PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC)
  394. {
  395. int offset = 0;
  396. size_t got;
  397. char * reply;
  398. if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) {
  399. return NULL;
  400. }
  401. if (bytes == -1) {
  402. return NULL;
  403. } else {
  404. char c;
  405. int i;
  406. reply = emalloc(bytes+1);
  407. while(offset < bytes) {
  408. got = php_stream_read(redis_sock->stream, reply + offset,
  409. bytes-offset);
  410. if (got <= 0) {
  411. /* Error or EOF */
  412. zend_throw_exception(redis_exception_ce,
  413. "socket error on read socket", 0 TSRMLS_CC);
  414. break;
  415. }
  416. offset += got;
  417. }
  418. for(i = 0; i < 2; i++) {
  419. php_stream_read(redis_sock->stream, &c, 1);
  420. }
  421. }
  422. reply[bytes] = 0;
  423. return reply;
  424. }
  425. /**
  426. * redis_sock_read
  427. */
  428. PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC)
  429. {
  430. char inbuf[1024];
  431. char *resp = NULL;
  432. size_t err_len;
  433. if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) {
  434. return NULL;
  435. }
  436. if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) {
  437. redis_stream_close(redis_sock TSRMLS_CC);
  438. redis_sock->stream = NULL;
  439. redis_sock->status = REDIS_SOCK_STATUS_FAILED;
  440. redis_sock->mode = ATOMIC;
  441. redis_sock->watching = 0;
  442. zend_throw_exception(redis_exception_ce, "read error on connection",
  443. 0 TSRMLS_CC);
  444. return NULL;
  445. }
  446. switch(inbuf[0]) {
  447. case '-':
  448. err_len = strlen(inbuf+1) - 2;
  449. redis_sock_set_err(redis_sock, inbuf+1, err_len);
  450. /* Filter our ERROR through the few that should actually throw */
  451. redis_error_throw(inbuf + 1, err_len TSRMLS_CC);
  452. /* Handle stale data error */
  453. if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) {
  454. zend_throw_exception(redis_exception_ce,
  455. "SYNC with master in progress", 0 TSRMLS_CC);
  456. }
  457. return NULL;
  458. case '$':
  459. *buf_len = atoi(inbuf + 1);
  460. resp = redis_sock_read_bulk_reply(redis_sock, *buf_len TSRMLS_CC);
  461. return resp;
  462. case '*':
  463. /* For null multi-bulk replies (like timeouts from brpoplpush): */
  464. if(memcmp(inbuf + 1, "-1", 2) == 0) {
  465. return NULL;
  466. }
  467. /* fall through */
  468. case '+':
  469. case ':':
  470. /* Single Line Reply */
  471. /* :123\r\n */
  472. *buf_len = strlen(inbuf) - 2;
  473. if(*buf_len >= 2) {
  474. resp = emalloc(1+*buf_len);
  475. memcpy(resp, inbuf, *buf_len);
  476. resp[*buf_len] = 0;
  477. return resp;
  478. }
  479. default:
  480. zend_throw_exception_ex(
  481. redis_exception_ce,
  482. 0 TSRMLS_CC,
  483. "protocol error, got '%c' as reply type byte\n",
  484. inbuf[0]
  485. );
  486. }
  487. return NULL;
  488. }
  489. void add_constant_long(zend_class_entry *ce, char *name, int value) {
  490. zval *constval;
  491. constval = pemalloc(sizeof(zval), 1);
  492. INIT_PZVAL(constval);
  493. ZVAL_LONG(constval, value);
  494. zend_hash_add(&ce->constants_table, name, 1 + strlen(name),
  495. (void*)&constval, sizeof(zval*), NULL);
  496. }
  497. int
  498. integer_length(int i) {
  499. int sz = 0;
  500. int ci = abs(i);
  501. while (ci > 0) {
  502. ci /= 10;
  503. sz++;
  504. }
  505. if (i == 0) { /* log 0 doesn't make sense. */
  506. sz = 1;
  507. } else if (i < 0) { /* allow for neg sign as well. */
  508. sz++;
  509. }
  510. return sz;
  511. }
  512. int
  513. redis_cmd_format_header(char **ret, char *keyword, int arg_count) {
  514. /* Our return buffer */
  515. smart_str buf = {0};
  516. /* Keyword length */
  517. int l = strlen(keyword);
  518. smart_str_appendc(&buf, '*');
  519. smart_str_append_long(&buf, arg_count + 1);
  520. smart_str_appendl(&buf, _NL, sizeof(_NL) -1);
  521. smart_str_appendc(&buf, '$');
  522. smart_str_append_long(&buf, l);
  523. smart_str_appendl(&buf, _NL, sizeof(_NL) -1);
  524. smart_str_appendl(&buf, keyword, l);
  525. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  526. /* Set our return pointer */
  527. *ret = buf.c;
  528. /* Return the length */
  529. return buf.len;
  530. }
  531. int
  532. redis_cmd_format_static(char **ret, char *keyword, char *format, ...)
  533. {
  534. char *p = format;
  535. va_list ap;
  536. smart_str buf = {0};
  537. int l = strlen(keyword);
  538. char *dbl_str;
  539. int dbl_len;
  540. va_start(ap, format);
  541. /* add header */
  542. smart_str_appendc(&buf, '*');
  543. smart_str_append_long(&buf, strlen(format) + 1);
  544. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  545. smart_str_appendc(&buf, '$');
  546. smart_str_append_long(&buf, l);
  547. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  548. smart_str_appendl(&buf, keyword, l);
  549. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  550. while (*p) {
  551. smart_str_appendc(&buf, '$');
  552. switch(*p) {
  553. case 's': {
  554. char *val = va_arg(ap, char*);
  555. int val_len = va_arg(ap, int);
  556. smart_str_append_long(&buf, val_len);
  557. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  558. smart_str_appendl(&buf, val, val_len);
  559. }
  560. break;
  561. case 'f':
  562. case 'F': {
  563. double d = va_arg(ap, double);
  564. REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d)
  565. smart_str_append_long(&buf, dbl_len);
  566. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  567. smart_str_appendl(&buf, dbl_str, dbl_len);
  568. efree(dbl_str);
  569. }
  570. break;
  571. case 'i':
  572. case 'd': {
  573. int i = va_arg(ap, int);
  574. char tmp[32];
  575. int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i);
  576. smart_str_append_long(&buf, tmp_len);
  577. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  578. smart_str_appendl(&buf, tmp, tmp_len);
  579. }
  580. break;
  581. case 'l':
  582. case 'L': {
  583. long l = va_arg(ap, long);
  584. char tmp[32];
  585. int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l);
  586. smart_str_append_long(&buf, tmp_len);
  587. smart_str_appendl(&buf, _NL, sizeof(_NL) -1);
  588. smart_str_appendl(&buf, tmp, tmp_len);
  589. }
  590. break;
  591. }
  592. p++;
  593. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  594. }
  595. smart_str_0(&buf);
  596. *ret = buf.c;
  597. return buf.len;
  598. }
  599. /**
  600. * This command behave somehow like printf, except that strings need 2
  601. * arguments:
  602. * Their data and their size (strlen).
  603. * Supported formats are: %d, %i, %s, %l
  604. */
  605. int
  606. redis_cmd_format(char **ret, char *format, ...) {
  607. smart_str buf = {0};
  608. va_list ap;
  609. char *p = format;
  610. char *dbl_str;
  611. int dbl_len;
  612. va_start(ap, format);
  613. while (*p) {
  614. if (*p == '%') {
  615. switch (*(++p)) {
  616. case 's': {
  617. char *tmp = va_arg(ap, char*);
  618. int tmp_len = va_arg(ap, int);
  619. smart_str_appendl(&buf, tmp, tmp_len);
  620. }
  621. break;
  622. case 'F':
  623. case 'f': {
  624. double d = va_arg(ap, double);
  625. REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d)
  626. smart_str_append_long(&buf, dbl_len);
  627. smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
  628. smart_str_appendl(&buf, dbl_str, dbl_len);
  629. efree(dbl_str);
  630. }
  631. break;
  632. case 'd':
  633. case 'i': {
  634. int i = va_arg(ap, int);
  635. char tmp[32];
  636. int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i);
  637. smart_str_appendl(&buf, tmp, tmp_len);
  638. }
  639. break;
  640. }
  641. } else {
  642. smart_str_appendc(&buf, *p);
  643. }
  644. p++;
  645. }
  646. smart_str_0(&buf);
  647. *ret = buf.c;
  648. return buf.len;
  649. }
  650. /*
  651. * Append a command sequence to a Redis command
  652. */
  653. int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) {
  654. /* Smart string buffer */
  655. smart_str buf = {0};
  656. /* Append the current command to our smart_str */
  657. smart_str_appendl(&buf, *cmd, cmd_len);
  658. /* Append our new command sequence */
  659. smart_str_appendc(&buf, '$');
  660. smart_str_append_long(&buf, append_len);
  661. smart_str_appendl(&buf, _NL, sizeof(_NL) -1);
  662. smart_str_appendl(&buf, append, append_len);
  663. smart_str_appendl(&buf, _NL, sizeof(_NL) -1);
  664. /* Free our old command */
  665. efree(*cmd);
  666. /* Set our return pointer */
  667. *cmd = buf.c;
  668. /* Return new command length */
  669. return buf.len;
  670. }
  671. /*
  672. * Given a smart string, number of arguments, a keyword, and the length of the keyword
  673. * initialize our smart string with the proper Redis header for the command to follow
  674. */
  675. int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len) {
  676. smart_str_appendc(str, '*');
  677. smart_str_append_long(str, num_args + 1);
  678. smart_str_appendl(str, _NL, sizeof(_NL) -1);
  679. smart_str_appendc(str, '$');
  680. smart_str_append_long(str, keyword_len);
  681. smart_str_appendl(str, _NL, sizeof(_NL) - 1);
  682. smart_str_appendl(str, keyword, keyword_len);
  683. smart_str_appendl(str, _NL, sizeof(_NL) - 1);
  684. return str->len;
  685. }
  686. /*
  687. * Append a command sequence to a smart_str
  688. */
  689. int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) {
  690. smart_str_appendc(str, '$');
  691. smart_str_append_long(str, append_len);
  692. smart_str_appendl(str, _NL, sizeof(_NL) - 1);
  693. smart_str_appendl(str, append, append_len);
  694. smart_str_appendl(str, _NL, sizeof(_NL) - 1);
  695. /* Return our new length */
  696. return str->len;
  697. }
  698. /*
  699. * Append an integer to a smart string command
  700. */
  701. int redis_cmd_append_sstr_int(smart_str *str, int append) {
  702. char int_buf[32];
  703. int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
  704. return redis_cmd_append_sstr(str, int_buf, int_len);
  705. }
  706. /*
  707. * Append a long to a smart string command
  708. */
  709. int redis_cmd_append_sstr_long(smart_str *str, long append) {
  710. char long_buf[32];
  711. int long_len = snprintf(long_buf, sizeof(long_buf), "%ld", append);
  712. return redis_cmd_append_sstr(str, long_buf, long_len);
  713. }
  714. /*
  715. * Append a double to a smart string command
  716. */
  717. int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
  718. char *dbl_str;
  719. int dbl_len, retval;
  720. /* Convert to double */
  721. REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value);
  722. // Append the string
  723. retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
  724. /* Free our double string */
  725. efree(dbl_str);
  726. /* Return new length */
  727. return retval;
  728. }
  729. /*
  730. * Append an integer command to a Redis command
  731. */
  732. int redis_cmd_append_int(char **cmd, int cmd_len, int append) {
  733. char int_buf[32];
  734. int int_len;
  735. // Conver to an int, capture length
  736. int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
  737. /* Return the new length */
  738. return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len);
  739. }
  740. PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
  741. char *response;
  742. int response_len;
  743. double ret;
  744. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
  745. IF_MULTI_OR_PIPELINE() {
  746. add_next_index_bool(z_tab, 0);
  747. } else {
  748. RETURN_FALSE;
  749. }
  750. return;
  751. }
  752. ret = atof(response);
  753. efree(response);
  754. IF_MULTI_OR_PIPELINE() {
  755. add_next_index_double(z_tab, ret);
  756. } else {
  757. RETURN_DOUBLE(ret);
  758. }
  759. }
  760. PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
  761. char *response;
  762. int response_len;
  763. long l;
  764. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
  765. IF_MULTI_OR_PIPELINE() {
  766. add_next_index_bool(z_tab, 0);
  767. } else {
  768. RETURN_FALSE;
  769. }
  770. }
  771. if (strncmp(response, "+string", 7) == 0) {
  772. l = REDIS_STRING;
  773. } else if (strncmp(response, "+set", 4) == 0){
  774. l = REDIS_SET;
  775. } else if (strncmp(response, "+list", 5) == 0){
  776. l = REDIS_LIST;
  777. } else if (strncmp(response, "+zset", 5) == 0){
  778. l = REDIS_ZSET;
  779. } else if (strncmp(response, "+hash", 5) == 0){
  780. l = REDIS_HASH;
  781. } else {
  782. l = REDIS_NOT_FOUND;
  783. }
  784. efree(response);
  785. IF_MULTI_OR_PIPELINE() {
  786. add_next_index_long(z_tab, l);
  787. } else {
  788. RETURN_LONG(l);
  789. }
  790. }
  791. PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
  792. char *response;
  793. int response_len;
  794. zval *z_ret;
  795. /* Read bulk response */
  796. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
  797. RETURN_FALSE;
  798. }
  799. /* Parse it into a zval array */
  800. z_ret = redis_parse_info_response(response);
  801. /* Free source response */
  802. efree(response);
  803. IF_MULTI_OR_PIPELINE() {
  804. add_next_index_zval(z_tab, z_ret);
  805. } else {
  806. RETVAL_ZVAL(z_ret, 0, 1);
  807. }
  808. }
  809. PHP_REDIS_API zval *redis_parse_info_response(char *response) {
  810. zval *z_ret;
  811. char *key, *value, *p, *cur, *pos;
  812. int is_numeric;
  813. MAKE_STD_ZVAL(z_ret);
  814. array_init(z_ret);
  815. cur = response;
  816. while(1) {
  817. /* skip comments and empty lines */
  818. if(*cur == '#' || *cur == '\r') {
  819. if(!(cur = strchr(cur, '\n')))
  820. break;
  821. cur++;
  822. continue;
  823. }
  824. /* key */
  825. pos = strchr(cur, ':');
  826. if(pos == NULL) {
  827. break;
  828. }
  829. key = emalloc(pos - cur + 1);
  830. memcpy(key, cur, pos-cur);
  831. key[pos-cur] = 0;
  832. /* value */
  833. cur = pos + 1;
  834. pos = strchr(cur, '\r');
  835. if(pos == NULL) {
  836. break;
  837. }
  838. value = emalloc(pos - cur + 1);
  839. memcpy(value, cur, pos-cur);
  840. value[pos-cur] = 0;
  841. pos += 2; /* \r, \n */
  842. cur = pos;
  843. is_numeric = 1;
  844. for(p = value; *p; ++p) {
  845. if(*p < '0' || *p > '9') {
  846. is_numeric = 0;
  847. break;
  848. }
  849. }
  850. if(is_numeric == 1) {
  851. add_assoc_long(z_ret, key, atol(value));
  852. efree(value);
  853. } else {
  854. add_assoc_string(z_ret, key, value, 0);
  855. }
  856. efree(key);
  857. }
  858. return z_ret;
  859. }
  860. /*
  861. * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code
  862. * to handle.
  863. */
  864. PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
  865. char *resp;
  866. int resp_len;
  867. zval *z_ret;
  868. /* Make sure we can read the bulk response from Redis */
  869. if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
  870. RETURN_FALSE;
  871. }
  872. /* Parse it out */
  873. z_ret = redis_parse_client_list_response(resp);
  874. /* Free our response */
  875. efree(resp);
  876. /* Return or append depending if we're atomic */
  877. IF_MULTI_OR_PIPELINE() {
  878. add_next_index_zval(z_tab, z_ret);
  879. } else {
  880. RETVAL_ZVAL(z_ret, 0, 1);
  881. }
  882. }
  883. PHP_REDIS_API zval* redis_parse_client_list_response(char *response) {
  884. zval *z_result, *z_sub_result;
  885. char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
  886. int klen = 0, done = 0, is_numeric;
  887. // Allocate memory for our response
  888. MAKE_STD_ZVAL(z_result);
  889. array_init(z_result);
  890. /* Allocate memory for one user (there should be at least one, namely us!) */
  891. ALLOC_INIT_ZVAL(z_sub_result);
  892. array_init(z_sub_result);
  893. // Pointers for parsing
  894. p = response;
  895. lpos = response;
  896. /* While we've got more to parse */
  897. while(!done) {
  898. /* What character are we on */
  899. switch(*p) {
  900. /* We're done */
  901. case '\0':
  902. done = 1;
  903. break;
  904. /* \n, ' ' mean we can pull a k/v pair */
  905. case '\n':
  906. case ' ':
  907. /* Grab our value */
  908. vpos = lpos;
  909. /* There is some communication error or Redis bug if we don't
  910. have a key and value, but check anyway. */
  911. if(kpos && vpos) {
  912. /* Allocate, copy in our key */
  913. key = emalloc(klen + 1);
  914. strncpy(key, kpos, klen);
  915. key[klen] = 0;
  916. /* Allocate, copy in our value */
  917. value = emalloc(p-lpos+1);
  918. strncpy(value,lpos,p-lpos+1);
  919. value[p-lpos]=0;
  920. /* Treat numbers as numbers, strings as strings */
  921. is_numeric = 1;
  922. for(p2 = value; *p2; ++p2) {
  923. if(*p2 < '0' || *p2 > '9') {
  924. is_numeric = 0;
  925. break;
  926. }
  927. }
  928. /* Add as a long or string, depending */
  929. if(is_numeric == 1) {
  930. add_assoc_long(z_sub_result, key, atol(value));
  931. efree(value);
  932. } else {
  933. add_assoc_string(z_sub_result, key, value, 0);
  934. }
  935. // If we hit a '\n', then we can add this user to our list
  936. if(*p == '\n') {
  937. /* Add our user */
  938. add_next_index_zval(z_result, z_sub_result);
  939. /* If we have another user, make another one */
  940. if(*(p+1) != '\0') {
  941. ALLOC_INIT_ZVAL(z_sub_result);
  942. array_init(z_sub_result);
  943. }
  944. }
  945. // Free our key
  946. efree(key);
  947. } else {
  948. // Something is wrong
  949. zval_dtor(z_result);
  950. MAKE_STD_ZVAL(z_result);
  951. ZVAL_BOOL(z_result, 0);
  952. return z_result;
  953. }
  954. /* Move forward */
  955. lpos = p + 1;
  956. break;
  957. /* We can pull the key and null terminate at our sep */
  958. case '=':
  959. /* Key, key length */
  960. kpos = lpos;
  961. klen = p - lpos;
  962. /* Move forward */
  963. lpos = p + 1;
  964. break;
  965. }
  966. /* Increment */
  967. p++;
  968. }
  969. /* Return our parsed response */
  970. return z_result;
  971. }
  972. PHP_REDIS_API void
  973. redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  974. zval *z_tab, void *ctx,
  975. SuccessCallback success_callback)
  976. {
  977. char *response;
  978. int response_len;
  979. char ret;
  980. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
  981. IF_MULTI_OR_PIPELINE() {
  982. add_next_index_bool(z_tab, 0);
  983. return;
  984. }
  985. RETURN_FALSE;
  986. }
  987. ret = response[0];
  988. efree(response);
  989. IF_MULTI_OR_PIPELINE() {
  990. if (ret == '+') {
  991. if (success_callback != NULL) {
  992. success_callback(redis_sock);
  993. }
  994. add_next_index_bool(z_tab, 1);
  995. } else {
  996. add_next_index_bool(z_tab, 0);
  997. }
  998. } else {
  999. if (ret == '+') {
  1000. if (success_callback != NULL) {
  1001. success_callback(redis_sock);
  1002. }
  1003. RETURN_TRUE;
  1004. } else {
  1005. RETURN_FALSE;
  1006. }
  1007. }
  1008. }
  1009. PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS,
  1010. RedisSock *redis_sock, zval *z_tab,
  1011. void *ctx)
  1012. {
  1013. redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
  1014. z_tab, ctx, NULL);
  1015. }
  1016. PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS,
  1017. RedisSock *redis_sock, zval * z_tab,
  1018. void *ctx)
  1019. {
  1020. char *response;
  1021. int response_len;
  1022. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))
  1023. == NULL)
  1024. {
  1025. IF_MULTI_OR_PIPELINE() {
  1026. add_next_index_bool(z_tab, 0);
  1027. return;
  1028. } else {
  1029. RETURN_FALSE;
  1030. }
  1031. }
  1032. if(response[0] == ':') {
  1033. #ifdef PHP_WIN32
  1034. __int64 ret = _atoi64(response + 1);
  1035. #else
  1036. long long ret = atoll(response + 1);
  1037. #endif
  1038. IF_MULTI_OR_PIPELINE() {
  1039. if(ret > LONG_MAX) { /* overflow */
  1040. add_next_index_stringl(z_tab, response+1, response_len-1, 1);
  1041. } else {
  1042. efree(response);
  1043. add_next_index_long(z_tab, (long)ret);
  1044. }
  1045. } else {
  1046. if(ret > LONG_MAX) { /* overflow */
  1047. RETURN_STRINGL(response+1, response_len-1, 1);
  1048. } else {
  1049. efree(response);
  1050. RETURN_LONG((long)ret);
  1051. }
  1052. }
  1053. } else {
  1054. efree(response);
  1055. IF_MULTI_OR_PIPELINE() {
  1056. add_next_index_null(z_tab);
  1057. } else {
  1058. RETURN_FALSE;
  1059. }
  1060. }
  1061. }
  1062. /* Helper method to convert [key, value, key, value] into [key => value,
  1063. * key => value] when returning data to the caller. Depending on our decode
  1064. * flag we'll convert the value data types */
  1065. static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
  1066. int decode TSRMLS_DC)
  1067. {
  1068. zval *z_ret;
  1069. HashTable *keytable;
  1070. MAKE_STD_ZVAL(z_ret);
  1071. array_init(z_ret);
  1072. keytable = Z_ARRVAL_P(z_tab);
  1073. for(zend_hash_internal_pointer_reset(keytable);
  1074. zend_hash_has_more_elements(keytable) == SUCCESS;
  1075. zend_hash_move_forward(keytable)) {
  1076. char *tablekey, *hkey, *hval;
  1077. unsigned int tablekey_len;
  1078. int hkey_len;
  1079. unsigned long idx;
  1080. zval **z_key_pp, **z_value_pp;
  1081. zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
  1082. if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) {
  1083. continue; /* this should never happen, according to the PHP people. */
  1084. }
  1085. /* get current value, a key */
  1086. convert_to_string(*z_key_pp);
  1087. hkey = Z_STRVAL_PP(z_key_pp);
  1088. hkey_len = Z_STRLEN_PP(z_key_pp);
  1089. /* move forward */
  1090. zend_hash_move_forward(keytable);
  1091. /* fetch again */
  1092. zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
  1093. if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) {
  1094. continue; /* this should never happen, according to the PHP people. */
  1095. }
  1096. /* get current value, a hash value now. */
  1097. hval = Z_STRVAL_PP(z_value_pp);
  1098. /* Decode the score depending on flag */
  1099. if (decode == SCORE_DECODE_INT && Z_STRLEN_PP(z_value_pp) > 0) {
  1100. add_assoc_long_ex(z_ret, hkey, 1+hkey_len, atoi(hval+1));
  1101. } else if (decode == SCORE_DECODE_DOUBLE) {
  1102. add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval));
  1103. } else {
  1104. zval *z = NULL;
  1105. MAKE_STD_ZVAL(z);
  1106. *z = **z_value_pp;
  1107. zval_copy_ctor(z);
  1108. add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z);
  1109. }
  1110. }
  1111. /* replace */
  1112. zval_dtor(z_tab);
  1113. *z_tab = *z_ret;
  1114. zval_copy_ctor(z_tab);
  1115. zval_dtor(z_ret);
  1116. efree(z_ret);
  1117. }
  1118. static int
  1119. redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  1120. zval *z_tab, int unserialize, int decode)
  1121. {
  1122. char inbuf[1024];
  1123. int numElems;
  1124. zval *z_multi_result;
  1125. if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) {
  1126. return -1;
  1127. }
  1128. if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) {
  1129. redis_stream_close(redis_sock TSRMLS_CC);
  1130. redis_sock->stream = NULL;
  1131. redis_sock->status = REDIS_SOCK_STATUS_FAILED;
  1132. redis_sock->mode = ATOMIC;
  1133. redis_sock->watching = 0;
  1134. zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC);
  1135. return -1;
  1136. }
  1137. if(inbuf[0] != '*') {
  1138. IF_MULTI_OR_PIPELINE() {
  1139. add_next_index_bool(z_tab, 0);
  1140. } else {
  1141. RETVAL_FALSE;
  1142. }
  1143. return -1;
  1144. }
  1145. numElems = atoi(inbuf+1);
  1146. MAKE_STD_ZVAL(z_multi_result);
  1147. array_init(z_multi_result); /* pre-allocate array for multi's results. */
  1148. /* Grab our key, value, key, value array */
  1149. redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
  1150. z_multi_result, numElems, unserialize);
  1151. /* Zip keys and values */
  1152. array_zip_values_and_scores(redis_sock, z_multi_result, decode TSRMLS_CC);
  1153. IF_MULTI_OR_PIPELINE() {
  1154. add_next_index_zval(z_tab, z_multi_result);
  1155. } else {
  1156. *return_value = *z_multi_result;
  1157. zval_copy_ctor(return_value);
  1158. zval_dtor(z_multi_result);
  1159. efree(z_multi_result);
  1160. }
  1161. return 0;
  1162. }
  1163. /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */
  1164. PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
  1165. {
  1166. return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
  1167. z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE);
  1168. }
  1169. /* Zipped key => value reply unserializing keys and decoding the score as an integer (PUBSUB) */
  1170. PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  1171. zval *z_tab, void *ctx)
  1172. {
  1173. return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
  1174. z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_INT);
  1175. }
  1176. /* Zipped key => value reply unserializing keys and decoding the score as a double (ZSET commands) */
  1177. PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  1178. zval *z_tab, void *ctx)
  1179. {
  1180. return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
  1181. z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_DOUBLE);
  1182. }
  1183. /* Zipped key => value reply where only the values are unserialized (e.g. HMGET) */
  1184. PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  1185. zval *z_tab, void *ctx)
  1186. {
  1187. return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
  1188. z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE);
  1189. }
  1190. PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS,
  1191. RedisSock *redis_sock, zval *z_tab, void *ctx)
  1192. {
  1193. char *response;
  1194. int response_len;
  1195. char ret;
  1196. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))
  1197. == NULL)
  1198. {
  1199. IF_MULTI_OR_PIPELINE() {
  1200. add_next_index_bool(z_tab, 0);
  1201. return;
  1202. } else {
  1203. RETURN_FALSE;
  1204. }
  1205. }
  1206. ret = response[1];
  1207. efree(response);
  1208. IF_MULTI_OR_PIPELINE() {
  1209. if(ret == '1') {
  1210. add_next_index_bool(z_tab, 1);
  1211. } else {
  1212. add_next_index_bool(z_tab, 0);
  1213. }
  1214. } else {
  1215. if (ret == '1') {
  1216. RETURN_TRUE;
  1217. } else {
  1218. RETURN_FALSE;
  1219. }
  1220. }
  1221. }
  1222. PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
  1223. char *response;
  1224. int response_len;
  1225. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))
  1226. == NULL)
  1227. {
  1228. IF_MULTI_OR_PIPELINE() {
  1229. add_next_index_bool(z_tab, 0);
  1230. return;
  1231. }
  1232. RETURN_FALSE;
  1233. }
  1234. IF_MULTI_OR_PIPELINE() {
  1235. zval *z = NULL;
  1236. if(redis_unserialize(redis_sock, response, response_len,
  1237. &z TSRMLS_CC) == 1)
  1238. {
  1239. efree(response);
  1240. add_next_index_zval(z_tab, z);
  1241. } else {
  1242. add_next_index_stringl(z_tab, response, response_len, 0);
  1243. }
  1244. } else {
  1245. if(redis_unserialize(redis_sock, response, response_len,
  1246. &return_value TSRMLS_CC) == 0)
  1247. {
  1248. RETURN_STRINGL(response, response_len, 0);
  1249. } else {
  1250. efree(response);
  1251. }
  1252. }
  1253. }
  1254. /* like string response, but never unserialized. */
  1255. PHP_REDIS_API void
  1256. redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  1257. zval *z_tab, void *ctx)
  1258. {
  1259. char *response;
  1260. int response_len;
  1261. if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))
  1262. == NULL)
  1263. {
  1264. IF_MULTI_OR_PIPELINE() {
  1265. add_next_index_bool(z_tab, 0);
  1266. return;
  1267. }
  1268. RETURN_FALSE;
  1269. }
  1270. IF_MULTI_OR_PIPELINE() {
  1271. add_next_index_stringl(z_tab, response, response_len, 0);
  1272. } else {
  1273. RETURN_STRINGL(response, response_len, 0);
  1274. }
  1275. }
  1276. /* Response for DEBUG object which is a formatted single line reply */
  1277. PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  1278. zval *z_tab, void *ctx)
  1279. {
  1280. char *resp, *p, *p2, *p3, *p4;
  1281. int is_numeric, resp_len;
  1282. zval *z_result;
  1283. /* Add or return false if we can't read from the socket */
  1284. if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC))==NULL) {
  1285. IF_MULTI_OR_PIPELINE() {
  1286. add_next_index_bool(z_tab, 0);
  1287. return;
  1288. }
  1289. RETURN_FALSE;
  1290. }
  1291. MAKE_STD_ZVAL(z_result);
  1292. array_init(z_result);
  1293. /* Skip the '+' */
  1294. p = resp + 1;
  1295. /* <info>:<value> <info2:value2> ... */
  1296. while((p2 = strchr(p, ':'))!=NULL) {
  1297. /* Null terminate at the ':' */
  1298. *p2++ = '\0';
  1299. /* Null terminate at the space if we have one */
  1300. if((p3 = strchr(p2, ' '))!=NULL) {
  1301. *p3++ = '\0';
  1302. } else {
  1303. p3 = resp + resp_len;
  1304. }
  1305. is_numeric = 1;
  1306. for(p4=p2; *p4; ++p4) {
  1307. if(*p4 < '0' || *p4 > '9') {
  1308. is_numeric = 0;
  1309. break;
  1310. }
  1311. }
  1312. /* Add our value */
  1313. if(is_numeric) {
  1314. add_assoc_long(z_result, p, atol(p2));
  1315. } else {
  1316. add_assoc_string(z_result, p, p2, 1);
  1317. }
  1318. p = p3;
  1319. }
  1320. efree(resp);
  1321. IF_MULTI_OR_PIPELINE() {
  1322. add_next_index_zval(z_tab, z_result);
  1323. } else {
  1324. RETVAL_ZVAL(z_result, 0, 1);
  1325. }
  1326. }
  1327. /**
  1328. * redis_sock_create
  1329. */
  1330. PHP_REDIS_API RedisSock*
  1331. redis_sock_create(char *host, int host_len, unsigned short port, double timeout,
  1332. int persistent, char *persistent_id, long retry_interval,
  1333. zend_bool lazy_connect)
  1334. {
  1335. RedisSock *redis_sock;
  1336. redis_sock = ecalloc(1, sizeof(RedisSock));
  1337. redis_sock->host = estrndup(host, host_len);
  1338. redis_sock->stream = NULL;
  1339. redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;
  1340. redis_sock->watching = 0;
  1341. redis_sock->dbNumber = 0;
  1342. redis_sock->retry_interval = retry_interval * 1000;
  1343. redis_sock->persistent = persistent;
  1344. redis_sock->lazy_connect = lazy_connect;
  1345. if(persistent_id) {
  1346. size_t persistent_id_len = strlen(persistent_id);
  1347. redis_sock->persistent_id = ecalloc(persistent_id_len + 1, 1);
  1348. memcpy(redis_sock->persistent_id, persistent_id, persistent_id_len);
  1349. } else {
  1350. redis_sock->persistent_id = NULL;
  1351. }
  1352. memcpy(redis_sock->host, host, host_len);
  1353. redis_sock->host[host_len] = '\0';
  1354. redis_sock->port = port;
  1355. redis_sock->timeout = timeout;
  1356. redis_sock->read_timeout = timeout;
  1357. redis_sock->serializer = REDIS_SERIALIZER_NONE;
  1358. redis_sock->mode = ATOMIC;
  1359. redis_sock->head = NULL;
  1360. redis_sock->current = NULL;
  1361. redis_sock->pipeline_head = NULL;
  1362. redis_sock->pipeline_current = NULL;
  1363. redis_sock->err = NULL;
  1364. redis_sock->err_len = 0;
  1365. redis_sock->scan = REDIS_SCAN_NORETRY;
  1366. redis_sock->readonly = 0;
  1367. return redis_sock;
  1368. }
  1369. /**
  1370. * redis_sock_connect
  1371. */
  1372. PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC)
  1373. {
  1374. struct timeval tv, read_tv, *tv_ptr = NULL;
  1375. char *host = NULL, *persistent_id = NULL, *errstr = NULL;
  1376. int host_len, err = 0;
  1377. php_netstream_data_t *sock;
  1378. int tcp_flag = 1;
  1379. if (redis_sock->stream != NULL) {
  1380. redis_sock_disconnect(redis_sock TSRMLS_CC);
  1381. }
  1382. tv.tv_sec = (time_t)redis_sock->timeout;
  1383. tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000);
  1384. if(tv.tv_sec != 0 || tv.tv_usec != 0) {
  1385. tv_ptr = &tv;
  1386. }
  1387. read_tv.tv_sec = (time_t)redis_sock->read_timeout;
  1388. read_tv.tv_usec = (int)((redis_sock->read_timeout-read_tv.tv_sec)*1000000);
  1389. if(redis_sock->host[0] == '/' && redis_sock->port < 1) {
  1390. host_len = spprintf(&host, 0, "unix://%s", redis_sock->host);
  1391. } else {
  1392. if(redis_sock->port == 0)
  1393. redis_sock->port = 6379;
  1394. host_len = spprintf(&host, 0, "%s:%d", redis_sock->host,
  1395. redis_sock->port);
  1396. }
  1397. if (redis_sock->persistent) {
  1398. if (redis_sock->persistent_id) {
  1399. spprintf(&persistent_id, 0, "phpredis:%s:%s", host,
  1400. redis_sock->persistent_id);
  1401. } else {
  1402. spprintf(&persistent_id, 0, "phpredis:%s:%f", host,
  1403. redis_sock->timeout);
  1404. }
  1405. }
  1406. redis_sock->stream = php_stream_xport_create(host, host_len,
  1407. ENFORCE_SAFE_MODE, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
  1408. persistent_id, tv_ptr, NULL, &errstr, &err);
  1409. if (persistent_id) {
  1410. efree(persistent_id);
  1411. }
  1412. efree(host);
  1413. if (!redis_sock->stream) {
  1414. if (errstr) efree(errstr);
  1415. return -1;
  1416. }
  1417. /* set TCP_NODELAY */
  1418. sock = (php_netstream_data_t*)redis_sock->stream->abstract;
  1419. setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag,
  1420. sizeof(int));
  1421. php_stream_auto_cleanup(redis_sock->stream);
  1422. if(tv.tv_sec != 0 || tv.tv_usec != 0) {
  1423. php_stream_set_option(redis_sock->stream,PHP_STREAM_OPTION_READ_TIMEOUT,
  1424. 0, &read_tv);
  1425. }
  1426. php_stream_set_option(redis_sock->stream,
  1427. PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
  1428. redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
  1429. return 0;
  1430. }
  1431. /**
  1432. * redis_sock_server_open
  1433. */
  1434. PHP_REDIS_API int
  1435. redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC)
  1436. {
  1437. int res = -1;
  1438. switch (redis_sock->status) {
  1439. case REDIS_SOCK_STATUS_DISCONNECTED:
  1440. return redis_sock_connect(redis_sock TSRMLS_CC);
  1441. case REDIS_SOCK_STATUS_CONNECTED:
  1442. res = 0;
  1443. break;
  1444. case REDIS_SOCK_STATUS_UNKNOWN:
  1445. if (force_connect > 0 && redis_sock_connect(redis_sock TSRMLS_CC) < 0) {
  1446. res = -1;
  1447. } else {
  1448. res = 0;
  1449. redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
  1450. }
  1451. break;
  1452. }
  1453. return res;
  1454. }
  1455. /**
  1456. * redis_sock_disconnect
  1457. */
  1458. PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC)
  1459. {
  1460. if (redis_sock == NULL) {
  1461. return 1;
  1462. }
  1463. redis_sock->dbNumber = 0;
  1464. if (redis_sock->stream != NULL) {
  1465. redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;
  1466. redis_sock->watching = 0;
  1467. /* Stil valid? */
  1468. if(redis_sock->stream && !redis_sock->persistent) {
  1469. php_stream_close(redis_sock->stream);
  1470. }
  1471. redis_sock->stream = NULL;
  1472. return 1;