/library.c
C | 2827 lines | 2131 code | 394 blank | 302 comment | 535 complexity | 6b7532b62dd83be256e65f998384c4d1 MD5 | raw file
Possible License(s): BSD-3-Clause, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include "common.h"
- #include "php_network.h"
- #include <sys/types.h>
- #ifdef HAVE_REDIS_IGBINARY
- #include "igbinary/igbinary.h"
- #endif
- #ifdef HAVE_REDIS_MSGPACK
- #include "msgpack/php_msgpack.h"
- #endif
- #ifdef HAVE_REDIS_LZF
- #include <lzf.h>
- #ifndef LZF_MARGIN
- #define LZF_MARGIN 128
- #endif
- #endif
- #ifdef HAVE_REDIS_ZSTD
- #include <zstd.h>
- #endif
- #include <zend_exceptions.h>
- #include "php_redis.h"
- #include "library.h"
- #include "redis_commands.h"
- #ifdef HAVE_REDIS_JSON
- #include <ext/json/php_json.h>
- #endif
- #include <ext/standard/php_rand.h>
- #define UNSERIALIZE_NONE 0
- #define UNSERIALIZE_KEYS 1
- #define UNSERIALIZE_VALS 2
- #define UNSERIALIZE_ALL 3
- #define SCORE_DECODE_NONE 0
- #define SCORE_DECODE_INT 1
- #define SCORE_DECODE_DOUBLE 2
- #ifndef PHP_WIN32
- #include <netinet/tcp.h> /* TCP_NODELAY */
- #include <sys/socket.h> /* SO_KEEPALIVE */
- #else
- #include <winsock.h>
- #endif
- extern zend_class_entry *redis_ce;
- extern zend_class_entry *redis_exception_ce;
- extern int le_redis_pconnect;
- static ConnectionPool *
- redis_sock_get_connection_pool(RedisSock *redis_sock)
- {
- ConnectionPool *p;
- zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port);
- zend_resource *le;
- if ((le = zend_hash_find_ptr(&EG(persistent_list), persistent_id)) == NULL) {
- p = pecalloc(1, sizeof(*p), 1);
- zend_llist_init(&p->list, sizeof(php_stream *), NULL, 1);
- #if (PHP_VERSION_ID < 70300)
- zend_resource res;
- res.type = le_redis_pconnect;
- res.ptr = p;
- le = &res;
- zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le));
- #else
- le = zend_register_persistent_resource(ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), p, le_redis_pconnect);
- #endif
- }
- zend_string_release(persistent_id);
- return le->ptr;
- }
- /* Helper to reselect the proper DB number when we reconnect */
- static int reselect_db(RedisSock *redis_sock) {
- char *cmd, *response;
- int cmd_len, response_len;
- cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "SELECT", "d",
- redis_sock->dbNumber);
- if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
- efree(cmd);
- return -1;
- }
- efree(cmd);
- if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
- return -1;
- }
- if (strncmp(response, "+OK", 3)) {
- efree(response);
- return -1;
- }
- efree(response);
- return 0;
- }
- /* Helper to resend AUTH <password> in the case of a reconnect */
- PHP_REDIS_API int
- redis_sock_auth(RedisSock *redis_sock)
- {
- char *cmd, *response;
- int cmd_len, response_len;
- cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->auth);
- if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
- efree(cmd);
- return -1;
- }
- efree(cmd);
- response = redis_sock_read(redis_sock, &response_len);
- if (response == NULL) {
- return -1;
- }
- if (strncmp(response, "+OK", 3)) {
- efree(response);
- return -1;
- }
- efree(response);
- return 0;
- }
- /* Helper function and macro to test a RedisSock error prefix. */
- #define REDIS_SOCK_ERRCMP_STATIC(rs, s) redis_sock_errcmp(rs, s, sizeof(s)-1)
- static int redis_sock_errcmp(RedisSock *redis_sock, const char *err, size_t errlen) {
- return ZSTR_LEN(redis_sock->err) >= errlen &&
- memcmp(ZSTR_VAL(redis_sock->err), err, errlen) == 0;
- }
- /* Helper function that will throw an exception for a small number of ERR codes
- * returned by Redis. Typically we just return FALSE to the caller in the event
- * of an ERROR reply, but for the following error types:
- * 1) MASTERDOWN
- * 2) AUTH
- * 3) LOADING
- */
- static void
- redis_error_throw(RedisSock *redis_sock)
- {
- /* Short circuit if we have no redis_sock or any error */
- if (redis_sock == NULL || redis_sock->err == NULL)
- return;
- /* We may want to flip this logic and check for MASTERDOWN, AUTH,
- * and LOADING but that may have side effects (esp for things like
- * Disque) */
- if (!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR") &&
- !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOSCRIPT") &&
- !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOQUORUM") &&
- !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGOODSLAVE") &&
- !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "WRONGTYPE") &&
- !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "BUSYGROUP") &&
- !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGROUP"))
- {
- REDIS_THROW_EXCEPTION( ZSTR_VAL(redis_sock->err), 0);
- }
- }
- PHP_REDIS_API int
- redis_check_eof(RedisSock *redis_sock, int no_throw)
- {
- int count;
- char *errmsg;
- if (!redis_sock || !redis_sock->stream || redis_sock->status == REDIS_SOCK_STATUS_FAILED) {
- if (!no_throw) {
- REDIS_THROW_EXCEPTION( "Connection closed", 0);
- }
- return -1;
- }
- /* NOITCE: set errno = 0 here
- *
- * There is a bug in php socket stream to check liveness of a connection:
- * if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) {
- * alive = 0;
- * }
- * If last errno is EWOULDBLOCK and recv returns 0 because of connection closed, alive would not be
- * set to 0. However, the connection is close indeed. The php_stream_eof is not reliable. This will
- * cause a "read error on connection" exception when use a closed persistent connection.
- *
- * We work around this by set errno = 0 first.
- *
- * Bug fix of php: https://github.com/php/php-src/pull/1456
- * */
- errno = 0;
- if (php_stream_eof(redis_sock->stream) == 0) {
- /* Success */
- return 0;
- } else if (redis_sock->mode == MULTI || redis_sock->watching) {
- errmsg = "Connection lost and socket is in MULTI/watching mode";
- } else {
- errmsg = "Connection lost";
- /* TODO: configurable max retry count */
- for (count = 0; count < 10; ++count) {
- /* close existing stream before reconnecting */
- if (redis_sock->stream) {
- redis_sock_disconnect(redis_sock, 1);
- }
- // Wait for a while before trying to reconnect
- if (redis_sock->retry_interval) {
- // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
- long retry_interval = (count ? redis_sock->retry_interval : (php_rand() % redis_sock->retry_interval));
- usleep(retry_interval);
- }
- /* reconnect */
- if (redis_sock_connect(redis_sock) == 0) {
- /* check for EOF again. */
- errno = 0;
- if (php_stream_eof(redis_sock->stream) == 0) {
- /* If we're using a password, attempt a reauthorization */
- if (redis_sock->auth && redis_sock_auth(redis_sock) != 0) {
- errmsg = "AUTH failed while reconnecting";
- break;
- }
- redis_sock->status = REDIS_SOCK_STATUS_READY;
- /* If we're using a non-zero db, reselect it */
- if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) {
- errmsg = "SELECT failed while reconnecting";
- break;
- }
- /* Success */
- return 0;
- }
- }
- }
- }
- /* close stream and mark socket as failed */
- redis_sock_disconnect(redis_sock, 1);
- redis_sock->status = REDIS_SOCK_STATUS_FAILED;
- if (!no_throw) {
- REDIS_THROW_EXCEPTION( errmsg, 0);
- }
- return -1;
- }
- PHP_REDIS_API int
- redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- REDIS_SCAN_TYPE type, zend_long *iter)
- {
- REDIS_REPLY_TYPE reply_type;
- long reply_info;
- char *p_iter;
- /* Our response should have two multibulk replies */
- if(redis_read_reply_type(redis_sock, &reply_type, &reply_info)<0
- || reply_type != TYPE_MULTIBULK || reply_info != 2)
- {
- return -1;
- }
- /* The BULK response iterator */
- if(redis_read_reply_type(redis_sock, &reply_type, &reply_info)<0
- || reply_type != TYPE_BULK)
- {
- return -1;
- }
- /* Attempt to read the iterator */
- if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info))) {
- return -1;
- }
- /* Push the iterator out to the caller */
- *iter = atol(p_iter);
- efree(p_iter);
- /* Read our actual keys/members/etc differently depending on what kind of
- scan command this is. They all come back in slightly different ways */
- switch(type) {
- case TYPE_SCAN:
- return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, NULL, NULL);
- case TYPE_SSCAN:
- return redis_sock_read_multibulk_reply(
- INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
- case TYPE_ZSCAN:
- return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, NULL, NULL);
- case TYPE_HSCAN:
- return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, NULL, NULL);
- default:
- return -1;
- }
- }
- PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
- RedisSock *redis_sock, zval *z_tab,
- void *ctx)
- {
- subscribeContext *sctx = (subscribeContext*)ctx;
- zval *z_tmp, z_resp;
- // Consume response(s) from subscribe, which will vary on argc
- while(sctx->argc--) {
- if (!redis_sock_read_multibulk_reply_zval(
- INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)
- ) {
- efree(sctx);
- return -1;
- }
- // We'll need to find the command response
- if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 0)) == NULL) {
- zval_dtor(&z_resp);
- efree(sctx);
- return -1;
- }
- // Make sure the command response matches the command we called
- if(strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) !=0) {
- zval_dtor(&z_resp);
- efree(sctx);
- return -1;
- }
- zval_dtor(&z_resp);
- }
- zval z_ret, z_args[4];
- sctx->cb.retval = &z_ret;
- sctx->cb.params = z_args;
- sctx->cb.no_separation = 0;
- /* Multibulk response, {[pattern], type, channel, payload } */
- while(1) {
- zval *z_type, *z_chan, *z_pat = NULL, *z_data;
- HashTable *ht_tab;
- int tab_idx=1, is_pmsg;
- if (!redis_sock_read_multibulk_reply_zval(
- INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break;
- ht_tab = Z_ARRVAL(z_resp);
- if ((z_type = zend_hash_index_find(ht_tab, 0)) == NULL ||
- Z_TYPE_P(z_type) != IS_STRING
- ) {
- break;
- }
- // Check for message or pmessage
- if(!strncmp(Z_STRVAL_P(z_type), "message", 7) ||
- !strncmp(Z_STRVAL_P(z_type), "pmessage", 8))
- {
- is_pmsg = *Z_STRVAL_P(z_type)=='p';
- } else {
- break;
- }
- // Extract pattern if it's a pmessage
- if(is_pmsg) {
- if ((z_pat = zend_hash_index_find(ht_tab, tab_idx++)) == NULL) {
- break;
- }
- }
- // Extract channel and data
- if ((z_chan = zend_hash_index_find(ht_tab, tab_idx++)) == NULL ||
- (z_data = zend_hash_index_find(ht_tab, tab_idx++)) == NULL
- ) {
- break;
- }
- // Different args for SUBSCRIBE and PSUBSCRIBE
- z_args[0] = *getThis();
- if(is_pmsg) {
- z_args[1] = *z_pat;
- z_args[2] = *z_chan;
- z_args[3] = *z_data;
- } else {
- z_args[1] = *z_chan;
- z_args[2] = *z_data;
- }
- // Set arg count
- sctx->cb.param_count = tab_idx;
- // Execute callback
- if(zend_call_function(&(sctx->cb), &(sctx->cb_cache))
- ==FAILURE)
- {
- break;
- }
- // If we have a return value free it
- zval_ptr_dtor(&z_ret);
- zval_dtor(&z_resp);
- }
- // This is an error state, clean up
- zval_dtor(&z_resp);
- efree(sctx);
- return -1;
- }
- PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
- RedisSock *redis_sock, zval *z_tab,
- void *ctx)
- {
- subscribeContext *sctx = (subscribeContext*)ctx;
- zval *z_chan, zv, *z_ret = &zv, z_resp;
- int i;
- array_init(z_ret);
- for (i = 0; i < sctx->argc; i++) {
- if (!redis_sock_read_multibulk_reply_zval(
- INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp) ||
- (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL
- ) {
- zval_dtor(z_ret);
- return -1;
- }
- add_assoc_bool(z_ret, Z_STRVAL_P(z_chan), 1);
- zval_dtor(&z_resp);
- }
- efree(sctx);
- RETVAL_ZVAL(z_ret, 0, 1);
- // Success
- return 0;
- }
- PHP_REDIS_API zval *
- redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS,
- RedisSock *redis_sock, zval *z_tab)
- {
- char inbuf[4096];
- int numElems;
- size_t len;
- ZVAL_NULL(z_tab);
- if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
- return NULL;
- }
- if(inbuf[0] != '*') {
- return NULL;
- }
- numElems = atoi(inbuf+1);
- array_init(z_tab);
- redis_mbulk_reply_loop(redis_sock, z_tab, numElems, UNSERIALIZE_ALL);
- return z_tab;
- }
- /**
- * redis_sock_read_bulk_reply
- */
- PHP_REDIS_API char *
- redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes)
- {
- int offset = 0, nbytes;
- char *reply;
- size_t got;
- if (-1 == bytes || -1 == redis_check_eof(redis_sock, 0)) {
- return NULL;
- }
- nbytes = bytes + 2;
- /* Allocate memory for string */
- reply = emalloc(nbytes);
- /* Consume bulk string */
- while (offset < nbytes) {
- got = php_stream_read(redis_sock->stream, reply + offset, nbytes - offset);
- if (got == 0 && php_stream_eof(redis_sock->stream)) break;
- offset += got;
- }
- /* Protect against reading too few bytes */
- if (offset < nbytes) {
- /* Error or EOF */
- REDIS_THROW_EXCEPTION("socket error on read socket", 0);
- efree(reply);
- return NULL;
- }
- /* Null terminate reply string */
- reply[bytes] = '\0';
- return reply;
- }
- /**
- * redis_sock_read
- */
- PHP_REDIS_API char *
- redis_sock_read(RedisSock *redis_sock, int *buf_len)
- {
- char inbuf[4096];
- size_t len;
- *buf_len = 0;
- if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
- return NULL;
- }
- switch(inbuf[0]) {
- case '-':
- redis_sock_set_err(redis_sock, inbuf+1, len);
- /* Filter our ERROR through the few that should actually throw */
- redis_error_throw(redis_sock);
- return NULL;
- case '$':
- *buf_len = atoi(inbuf + 1);
- return redis_sock_read_bulk_reply(redis_sock, *buf_len);
- case '*':
- /* For null multi-bulk replies (like timeouts from brpoplpush): */
- if(memcmp(inbuf + 1, "-1", 2) == 0) {
- return NULL;
- }
- /* fall through */
- case '+':
- case ':':
- /* Single Line Reply */
- /* +OK or :123 */
- if (len > 1) {
- *buf_len = len;
- return estrndup(inbuf, *buf_len);
- }
- default:
- zend_throw_exception_ex(redis_exception_ce, 0,
- "protocol error, got '%c' as reply type byte\n",
- inbuf[0]
- );
- }
- return NULL;
- }
- /* A simple union to store the various arg types we might handle in our
- * redis_spprintf command formatting function */
- union resparg {
- char *str;
- zend_string *zstr;
- zval *zv;
- int ival;
- long lval;
- double dval;
- };
- /* A printf like method to construct a Redis RESP command. It has been extended
- * to take a few different format specifiers that are convenient to phpredis.
- *
- * s - C string followed by length as a
- * S - Pointer to a zend_string
- * k - Same as 's' but the value will be prefixed if phpredis is set up do do
- * that and the working slot will be set if it has been passed.
- * v - A z_val which will be serialized if phpredis is configured to serialize.
- * f - A double value
- * F - Alias to 'f'
- * i - An integer
- * d - Alias to 'i'
- * l - A long
- * L - Alias to 'l'
- */
- PHP_REDIS_API int
- redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...) {
- smart_string cmd = {0};
- va_list ap;
- union resparg arg;
- char *dup;
- int argfree;
- size_t arglen;
- va_start(ap, fmt);
- /* Header */
- redis_cmd_init_sstr(&cmd, strlen(fmt), kw, strlen(kw));
- while (*fmt) {
- switch (*fmt) {
- case 's':
- arg.str = va_arg(ap, char*);
- arglen = va_arg(ap, size_t);
- redis_cmd_append_sstr(&cmd, arg.str, arglen);
- break;
- case 'S':
- arg.zstr = va_arg(ap, zend_string*);
- redis_cmd_append_sstr(&cmd, ZSTR_VAL(arg.zstr), ZSTR_LEN(arg.zstr));
- break;
- case 'k':
- arg.str = va_arg(ap, char*);
- arglen = va_arg(ap, size_t);
- argfree = redis_key_prefix(redis_sock, &arg.str, &arglen);
- redis_cmd_append_sstr(&cmd, arg.str, arglen);
- if (slot) *slot = cluster_hash_key(arg.str, arglen);
- if (argfree) efree(arg.str);
- break;
- case 'v':
- arg.zv = va_arg(ap, zval*);
- argfree = redis_pack(redis_sock, arg.zv, &dup, &arglen);
- redis_cmd_append_sstr(&cmd, dup, arglen);
- if (argfree) efree(dup);
- break;
- case 'f':
- case 'F':
- arg.dval = va_arg(ap, double);
- redis_cmd_append_sstr_dbl(&cmd, arg.dval);
- break;
- case 'i':
- case 'd':
- arg.ival = va_arg(ap, int);
- redis_cmd_append_sstr_int(&cmd, arg.ival);
- break;
- case 'l':
- case 'L':
- arg.lval = va_arg(ap, long);
- redis_cmd_append_sstr_long(&cmd, arg.lval);
- break;
- }
- fmt++;
- }
- /* varargs cleanup */
- va_end(ap);
- /* Null terminate */
- smart_string_0(&cmd);
- /* Push command string, return length */
- *ret = cmd.c;
- return cmd.len;
- }
- /*
- * Given a smart string, number of arguments, a keyword, and the length of the keyword
- * initialize our smart string with the proper Redis header for the command to follow
- */
- int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len) {
- smart_string_appendc(str, '*');
- smart_string_append_long(str, num_args + 1);
- smart_string_appendl(str, _NL, sizeof(_NL) -1);
- smart_string_appendc(str, '$');
- smart_string_append_long(str, keyword_len);
- smart_string_appendl(str, _NL, sizeof(_NL) - 1);
- smart_string_appendl(str, keyword, keyword_len);
- smart_string_appendl(str, _NL, sizeof(_NL) - 1);
- return str->len;
- }
- /*
- * Append a command sequence to a smart_string
- */
- int redis_cmd_append_sstr(smart_string *str, char *append, int append_len) {
- smart_string_appendc(str, '$');
- smart_string_append_long(str, append_len);
- smart_string_appendl(str, _NL, sizeof(_NL) - 1);
- smart_string_appendl(str, append, append_len);
- smart_string_appendl(str, _NL, sizeof(_NL) - 1);
- /* Return our new length */
- return str->len;
- }
- /*
- * Append an integer to a smart string command
- */
- int redis_cmd_append_sstr_int(smart_string *str, int append) {
- char int_buf[32];
- int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
- return redis_cmd_append_sstr(str, int_buf, int_len);
- }
- /*
- * Append a long to a smart string command
- */
- int redis_cmd_append_sstr_long(smart_string *str, long append) {
- char long_buf[32];
- int long_len = snprintf(long_buf, sizeof(long_buf), "%ld", append);
- return redis_cmd_append_sstr(str, long_buf, long_len);
- }
- /*
- * Append a 64-bit integer to our command
- */
- int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) {
- char nbuf[64];
- int len = snprintf(nbuf, sizeof(nbuf), "%" PRId64, append);
- return redis_cmd_append_sstr(str, nbuf, len);
- }
- /*
- * Append a double to a smart string command
- */
- int
- redis_cmd_append_sstr_dbl(smart_string *str, double value)
- {
- char tmp[64];
- int len, retval;
- /* Convert to string */
- len = snprintf(tmp, sizeof(tmp), "%.16g", value);
- // Append the string
- retval = redis_cmd_append_sstr(str, tmp, len);
- /* Return new length */
- return retval;
- }
- /* Append a zval to a redis command. The value will be serialized if we are
- * configured to do that */
- int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock) {
- char *val;
- size_t vallen;
- int valfree, retval;
- valfree = redis_pack(redis_sock, z, &val, &vallen);
- retval = redis_cmd_append_sstr(str, val, vallen);
- if (valfree) efree(val);
- return retval;
- }
- /* Append a string key to a redis command. This function takes care of prefixing the key
- * for the caller and setting the slot argument if it is passed non null */
- int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot) {
- int valfree, retval;
- valfree = redis_key_prefix(redis_sock, &key, &len);
- if (slot) *slot = cluster_hash_key(key, len);
- retval = redis_cmd_append_sstr(str, key, len);
- if (valfree) efree(key);
- return retval;
- }
- /* Append an array key to a redis smart string command. This function
- * handles the boilerplate conditionals around string or integer keys */
- int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx)
- {
- char *arg, kbuf[128];
- int len;
- if (kstr) {
- len = ZSTR_LEN(kstr);
- arg = ZSTR_VAL(kstr);
- } else {
- len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx);
- arg = (char*)kbuf;
- }
- return redis_cmd_append_sstr(cmd, arg, len);
- }
- PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
- char *response;
- int response_len;
- double ret;
- if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
- if (IS_ATOMIC(redis_sock)) {
- RETURN_FALSE;
- }
- add_next_index_bool(z_tab, 0);
- return;
- }
- ret = atof(response);
- efree(response);
- if (IS_ATOMIC(redis_sock)) {
- RETURN_DOUBLE(ret);
- } else {
- add_next_index_double(z_tab, ret);
- }
- }
- PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
- char *response;
- int response_len;
- long l;
- if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
- if (IS_ATOMIC(redis_sock)) {
- RETURN_FALSE;
- }
- add_next_index_bool(z_tab, 0);
- return;
- }
- if (strncmp(response, "+string", 7) == 0) {
- l = REDIS_STRING;
- } else if (strncmp(response, "+set", 4) == 0){
- l = REDIS_SET;
- } else if (strncmp(response, "+list", 5) == 0){
- l = REDIS_LIST;
- } else if (strncmp(response, "+zset", 5) == 0){
- l = REDIS_ZSET;
- } else if (strncmp(response, "+hash", 5) == 0){
- l = REDIS_HASH;
- } else if (strncmp(response, "+stream", 7) == 0) {
- l = REDIS_STREAM;
- } else {
- l = REDIS_NOT_FOUND;
- }
- efree(response);
- if (IS_ATOMIC(redis_sock)) {
- RETURN_LONG(l);
- } else {
- add_next_index_long(z_tab, l);
- }
- }
- PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
- char *response;
- int response_len;
- zval z_ret;
- /* Read bulk response */
- if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
- RETURN_FALSE;
- }
- /* Parse it into a zval array */
- ZVAL_UNDEF(&z_ret);
- redis_parse_info_response(response, &z_ret);
- /* Free source response */
- efree(response);
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_ret, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_ret);
- }
- }
- PHP_REDIS_API void
- redis_parse_info_response(char *response, zval *z_ret)
- {
- char *cur, *pos;
- array_init(z_ret);
- cur = response;
- while(1) {
- /* skip comments and empty lines */
- if (*cur == '#' || *cur == '\r') {
- if ((cur = strstr(cur, _NL)) == NULL) {
- break;
- }
- cur += 2;
- continue;
- }
- /* key */
- if ((pos = strchr(cur, ':')) == NULL) {
- break;
- }
- char *key = cur;
- int key_len = pos - cur;
- key[key_len] = '\0';
- /* value */
- cur = pos + 1;
- if ((pos = strstr(cur, _NL)) == NULL) {
- break;
- }
- char *value = cur;
- int value_len = pos - cur;
- value[value_len] = '\0';
- double dval;
- zend_long lval;
- zend_uchar type = is_numeric_string(value, value_len, &lval, &dval, 0);
- if (type == IS_LONG) {
- add_assoc_long_ex(z_ret, key, key_len, lval);
- } else if (type == IS_DOUBLE) {
- add_assoc_double_ex(z_ret, key, key_len, dval);
- } else {
- add_assoc_stringl_ex(z_ret, key, key_len, value, value_len);
- }
- cur = pos + 2; /* \r, \n */
- }
- }
- /*
- * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code
- * to handle.
- */
- PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
- char *resp;
- int resp_len;
- zval z_ret;
- /* Make sure we can read the bulk response from Redis */
- if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
- RETURN_FALSE;
- }
- /* Parse it out */
- redis_parse_client_list_response(resp, &z_ret);
- /* Free our response */
- efree(resp);
- /* Return or append depending if we're atomic */
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_ret, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_ret);
- }
- }
- PHP_REDIS_API void
- redis_parse_client_list_response(char *response, zval *z_ret)
- {
- char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
- int klen = 0, done = 0, is_numeric;
- zval z_sub_result;
- /* Allocate for response and our user */
- array_init(z_ret);
- array_init(&z_sub_result);
- // Pointers for parsing
- p = response;
- lpos = response;
- /* While we've got more to parse */
- while(!done) {
- /* What character are we on */
- switch(*p) {
- /* We're done */
- case '\0':
- done = 1;
- break;
- /* \n, ' ' mean we can pull a k/v pair */
- case '\n':
- case ' ':
- /* Grab our value */
- vpos = lpos;
- /* There is some communication error or Redis bug if we don't
- have a key and value, but check anyway. */
- if(kpos && vpos) {
- /* Allocate, copy in our key */
- key = estrndup(kpos, klen);
- /* Allocate, copy in our value */
- value = estrndup(lpos, p - lpos);
- /* Treat numbers as numbers, strings as strings */
- is_numeric = 1;
- for(p2 = value; *p2; ++p2) {
- if(*p2 < '0' || *p2 > '9') {
- is_numeric = 0;
- break;
- }
- }
- /* Add as a long or string, depending */
- if(is_numeric == 1) {
- add_assoc_long(&z_sub_result, key, atol(value));
- } else {
- add_assoc_string(&z_sub_result, key, value);
- }
- efree(value);
- // If we hit a '\n', then we can add this user to our list
- if(*p == '\n') {
- /* Add our user */
- add_next_index_zval(z_ret, &z_sub_result);
- /* If we have another user, make another one */
- if(*(p+1) != '\0') {
- array_init(&z_sub_result);
- }
- }
- // Free our key
- efree(key);
- } else {
- // Something is wrong
- zval_dtor(z_ret);
- ZVAL_BOOL(z_ret, 0);
- return;
- }
- /* Move forward */
- lpos = p + 1;
- break;
- /* We can pull the key and null terminate at our sep */
- case '=':
- /* Key, key length */
- kpos = lpos;
- klen = p - lpos;
- /* Move forward */
- lpos = p + 1;
- break;
- }
- /* Increment */
- p++;
- }
- }
- PHP_REDIS_API void
- redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx,
- SuccessCallback success_callback)
- {
- char *response;
- int response_len;
- zend_bool ret = 0;
- if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) {
- ret = (*response == '+');
- efree(response);
- }
- if (ret && success_callback != NULL) {
- success_callback(redis_sock);
- }
- if (IS_ATOMIC(redis_sock)) {
- RETURN_BOOL(ret);
- } else {
- add_next_index_bool(z_tab, ret);
- }
- }
- PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS,
- RedisSock *redis_sock, zval *z_tab,
- void *ctx)
- {
- redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
- z_tab, ctx, NULL);
- }
- PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS,
- RedisSock *redis_sock, zval * z_tab,
- void *ctx)
- {
- char *response;
- int response_len;
- if ((response = redis_sock_read(redis_sock, &response_len))
- == NULL)
- {
- if (IS_ATOMIC(redis_sock)) {
- RETURN_FALSE;
- }
- add_next_index_bool(z_tab, 0);
- return;
- }
- if(response[0] == ':') {
- int64_t ret = phpredis_atoi64(response + 1);
- if (IS_ATOMIC(redis_sock)) {
- if(ret > LONG_MAX) { /* overflow */
- RETVAL_STRINGL(response + 1, response_len - 1);
- } else {
- RETVAL_LONG((long)ret);
- }
- } else {
- if(ret > LONG_MAX) { /* overflow */
- add_next_index_stringl(z_tab, response + 1, response_len - 1);
- } else {
- add_next_index_long(z_tab, (long)ret);
- }
- }
- } else {
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_FALSE;
- } else {
- add_next_index_null(z_tab);
- }
- }
- efree(response);
- }
- /* Helper method to convert [key, value, key, value] into [key => value,
- * key => value] when returning data to the caller. Depending on our decode
- * flag we'll convert the value data types */
- static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
- int decode)
- {
- zval z_ret, z_sub;
- HashTable *keytable;
- array_init(&z_ret);
- keytable = Z_ARRVAL_P(z_tab);
- for(zend_hash_internal_pointer_reset(keytable);
- zend_hash_has_more_elements(keytable) == SUCCESS;
- zend_hash_move_forward(keytable)) {
- zval *z_key_p, *z_value_p;
- if ((z_key_p = zend_hash_get_current_data(keytable)) == NULL) {
- continue; /* this should never happen, according to the PHP people. */
- }
- /* get current value, a key */
- zend_string *hkey = zval_get_string(z_key_p);
- /* move forward */
- zend_hash_move_forward(keytable);
- /* fetch again */
- if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) {
- zend_string_release(hkey);
- continue; /* this should never happen, according to the PHP people. */
- }
- /* get current value, a hash value now. */
- char *hval = Z_STRVAL_P(z_value_p);
- /* Decode the score depending on flag */
- if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) {
- add_assoc_long_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atoi(hval+1));
- } else if (decode == SCORE_DECODE_DOUBLE) {
- add_assoc_double_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atof(hval));
- } else {
- ZVAL_ZVAL(&z_sub, z_value_p, 1, 0);
- add_assoc_zval_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), &z_sub);
- }
- zend_string_release(hkey);
- }
- /* replace */
- zval_dtor(z_tab);
- ZVAL_ZVAL(z_tab, &z_ret, 0, 0);
- }
- static int
- read_mbulk_header(RedisSock *redis_sock, int *nelem)
- {
- char line[4096];
- size_t len;
- /* Throws exception on failure */
- if (redis_sock_gets(redis_sock, line, sizeof(line)-1, &len) < 0)
- return -1;
- if (line[0] != '*') {
- if (IS_ATOMIC(redis_sock)) {
- if (line[0] == '-') {
- redis_sock_set_err(redis_sock, line+1, len-1);
- }
- }
- return -1;
- }
- *nelem = atoi(line+1);
- return 0;
- }
- static int
- redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, int unserialize, int decode)
- {
- char inbuf[4096];
- int numElems;
- size_t len;
- if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
- return -1;
- }
- if(inbuf[0] != '*') {
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_FALSE;
- } else {
- add_next_index_bool(z_tab, 0);
- }
- if (*inbuf == TYPE_ERR) {
- redis_sock_set_err(redis_sock, inbuf + 1, len - 1);
- }
- return -1;
- }
- numElems = atoi(inbuf+1);
- zval z_multi_result;
- array_init(&z_multi_result); /* pre-allocate array for multi's results. */
- /* Grab our key, value, key, value array */
- redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize);
- /* Zip keys and values */
- array_zip_values_and_scores(redis_sock, &z_multi_result, decode);
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_multi_result, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_multi_result);
- }
- return 0;
- }
- /* Consume message ID */
- PHP_REDIS_API int
- redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen,
- size_t *linelen, int set_err)
- {
- REDIS_REPLY_TYPE type;
- long info;
- if (redis_read_reply_type(redis_sock, &type, &info) < 0 ||
- (type != TYPE_LINE && type != TYPE_ERR))
- {
- return -1;
- }
- if (redis_sock_gets(redis_sock, buffer, buflen, linelen) < 0) {
- return -1;
- }
- if (set_err && type == TYPE_ERR) {
- if (IS_ATOMIC(redis_sock)) {
- redis_sock_set_err(redis_sock, buffer, *linelen);
- }
- }
- return type == TYPE_LINE ? 0 : -1;
- }
- /* Helper function to consume Redis stream message data. This is useful for
- * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */
- PHP_REDIS_API int
- redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret
- )
- {
- zval z_message;
- int i, mhdr, fields;
- char *id = NULL;
- int idlen;
- /* Iterate over each message */
- for (i = 0; i < count; i++) {
- /* Consume inner multi-bulk header, message ID itself and finally
- * the multi-bulk header for field and values */
- if ((read_mbulk_header(redis_sock, &mhdr) < 0 || mhdr != 2) ||
- ((id = redis_sock_read(redis_sock, &idlen)) == NULL) ||
- (read_mbulk_header(redis_sock, &fields) < 0 || fields % 2 != 0))
- {
- if (id) efree(id);
- return -1;
- }
- array_init(&z_message);
- redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS);
- array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE);
- add_assoc_zval_ex(z_ret, id, idlen, &z_message);
- efree(id);
- }
- return 0;
- }
- PHP_REDIS_API int
- redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- zval z_messages;
- int messages;
- array_init(&z_messages);
- if (read_mbulk_header(redis_sock, &messages) < 0 ||
- redis_read_stream_messages(redis_sock, messages, &z_messages) < 0)
- {
- zval_dtor(&z_messages);
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_FALSE;
- } else {
- add_next_index_bool(z_tab, 0);
- }
- return -1;
- }
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_messages, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_messages);
- }
- return 0;
- }
- PHP_REDIS_API int
- redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_streams
- )
- {
- zval z_messages;
- int i, shdr, messages;
- char *id = NULL;
- int idlen;
- for (i = 0; i < count; i++) {
- if ((read_mbulk_header(redis_sock, &shdr) < 0 || shdr != 2) ||
- (id = redis_sock_read(redis_sock, &idlen)) == NULL ||
- read_mbulk_header(redis_sock, &messages) < 0)
- {
- if (id) efree(id);
- return -1;
- }
- array_init(&z_messages);
- if (redis_read_stream_messages(redis_sock, messages, &z_messages) < 0)
- goto failure;
- add_assoc_zval_ex(z_streams, id, idlen, &z_messages);
- efree(id);
- }
- return 0;
- failure:
- efree(id);
- zval_dtor(&z_messages);
- return -1;
- }
- PHP_REDIS_API int
- redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- zval z_rv;
- int streams;
- if (read_mbulk_header(redis_sock, &streams) < 0)
- goto failure;
- array_init(&z_rv);
- if (redis_read_stream_messages_multi(redis_sock, streams, &z_rv) < 0)
- goto cleanup;
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_rv, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_rv);
- }
- return 0;
- cleanup:
- zval_dtor(&z_rv);
- failure:
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_FALSE;
- } else {
- add_next_index_bool(z_tab, 0);
- }
- return -1;
- }
- /* This helper function does that actual XCLAIM response handling, which can be used by both
- * Redis and RedisCluster. Note that XCLAIM is somewhat unique in that its reply type depends
- * on whether or not it was called with the JUSTID option */
- PHP_REDIS_API int
- redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) {
- zval z_msg;
- REDIS_REPLY_TYPE type;
- char *id = NULL;
- int i, fields, idlen;
- long li;
- for (i = 0; i < count; i++) {
- /* Consume inner reply type */
- if (redis_read_reply_type(redis_sock, &type, &li) < 0 ||
- (type != TYPE_BULK && type != TYPE_MULTIBULK) ||
- (type == TYPE_BULK && li <= 0)) return -1;
- /* TYPE_BULK is the JUSTID variant, otherwise it's standard xclaim response */
- if (type == TYPE_BULK) {
- if ((id = redis_sock_read_bulk_reply(redis_sock, (size_t)li)) == NULL)
- return -1;
- add_next_index_stringl(rv, id, li);
- efree(id);
- } else {
- if ((li != 2 || (id = redis_sock_read(redis_sock, &idlen)) == NULL) ||
- (read_mbulk_header(redis_sock, &fields) < 0 || fields % 2 != 0))
- {
- if (id) efree(id);
- return -1;
- }
- array_init(&z_msg);
- redis_mbulk_reply_loop(redis_sock, &z_msg, fields, UNSERIALIZE_VALS);
- array_zip_values_and_scores(redis_sock, &z_msg, SCORE_DECODE_NONE);
- add_assoc_zval_ex(rv, id, idlen, &z_msg);
- efree(id);
- }
- }
- return 0;
- }
- PHP_REDIS_API int
- redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- zval z_ret;
- int messages;
- /* All XCLAIM responses start multibulk */
- if (read_mbulk_header(redis_sock, &messages) < 0)
- goto failure;
- array_init(&z_ret);
- if (redis_read_xclaim_response(redis_sock, messages, &z_ret) < 0) {
- zval_dtor(&z_ret);
- goto failure;
- }
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_ret, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_ret);
- }
- return 0;
- failure:
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_FALSE;
- } else {
- add_next_index_bool(z_tab, 0);
- }
- return -1;
- }
- PHP_REDIS_API int
- redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements)
- {
- zval zv;
- int i, len = 0;
- char *key = NULL, *data;
- REDIS_REPLY_TYPE type;
- long li;
- for (i = 0; i < elements; ++i) {
- if (redis_read_reply_type(redis_sock, &type, &li) < 0) {
- goto failure;
- }
- switch (type) {
- case TYPE_BULK:
- if ((data = redis_sock_read_bulk_reply(redis_sock, li)) == NULL) {
- goto failure;
- } else if (key) {
- add_assoc_stringl_ex(z_ret, key, len, data, li);
- efree(data);
- efree(key);
- key = NULL;
- } else {
- key = data;
- len = li;
- }
- break;
- case TYPE_INT:
- if (key) {
- add_assoc_long_ex(z_ret, key, len, li);
- efree(key);
- key = NULL;
- } else {
- len = spprintf(&key, 0, "%ld", li);
- }
- break;
- case TYPE_MULTIBULK:
- array_init(&zv);
- if (redis_read_xinfo_response(redis_sock, &zv, li) != SUCCESS) {
- zval_dtor(&zv);
- goto failure;
- }
- if (key) {
- add_assoc_zval_ex(z_ret, key, len, &zv);
- efree(key);
- key = NULL;
- } else {
- add_next_index_zval(z_ret, &zv);
- }
- break;
- default:
- goto failure;
- }
- }
- return SUCCESS;
- failure:
- if (key) efree(key);
- return FAILURE;
- }
- PHP_REDIS_API int
- redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
- {
- zval z_ret;
- int elements;
- if (read_mbulk_header(redis_sock, &elements) == SUCCESS) {
- array_init(&z_ret);
- if (redis_read_xinfo_response(redis_sock, &z_ret, elements) == SUCCESS) {
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_ret, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_ret);
- }
- return SUCCESS;
- }
- zval_dtor(&z_ret);
- }
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_FALSE;
- } else {
- add_next_index_bool(z_tab, 0);
- }
- return FAILURE;
- }
- /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */
- PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
- {
- return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
- z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE);
- }
- /* Zipped key => value reply unserializing keys and decoding the score as an integer (PUBSUB) */
- PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
- z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_INT);
- }
- /* Zipped key => value reply unserializing keys and decoding the score as a double (ZSET commands) */
- PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
- z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_DOUBLE);
- }
- /* Zipped key => value reply where only the values are unserialized (e.g. HMGET) */
- PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
- z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE);
- }
- PHP_REDIS_API void
- redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
- {
- char *response;
- int response_len;
- zend_bool ret = 0;
- if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) {
- ret = (response[1] == '1');
- efree(response);
- }
- if (IS_ATOMIC(redis_sock)) {
- RETURN_BOOL(ret);
- } else {
- add_next_index_bool(z_tab, ret);
- }
- }
- PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
- char *response;
- int response_len;
- if ((response = redis_sock_read(redis_sock, &response_len))
- == NULL)
- {
- if (IS_ATOMIC(redis_sock)) {
- RETURN_FALSE;
- }
- add_next_index_bool(z_tab, 0);
- return;
- }
- if (IS_ATOMIC(redis_sock)) {
- if (!redis_unpack(redis_sock, response, response_len, return_value)) {
- RETVAL_STRINGL(response, response_len);
- }
- } else {
- zval z_unpacked;
- if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
- add_next_index_zval(z_tab, &z_unpacked);
- } else {
- add_next_index_stringl(z_tab, response, response_len);
- }
- }
- efree(response);
- }
- PHP_REDIS_API
- void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- char buffer[4096];
- size_t len;
- if (redis_sock_read_single_line(redis_sock, buffer, sizeof(buffer), &len, 1) < 0) {
- if (IS_ATOMIC(redis_sock)) {
- RETURN_FALSE;
- } else {
- add_next_index_bool(z_tab, 0);
- }
- return;
- }
- //str = estrndup(buffer, len);
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_STRINGL(buffer, len);
- } else {
- add_next_index_stringl(z_tab, buffer, len);
- }
- }
- /* like string response, but never unserialized. */
- PHP_REDIS_API void
- redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- char *response;
- int response_len;
- if ((response = redis_sock_read(redis_sock, &response_len))
- == NULL)
- {
- if (IS_ATOMIC(redis_sock)) {
- RETURN_FALSE;
- }
- add_next_index_bool(z_tab, 0);
- return;
- }
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_STRINGL(response, response_len);
- } else {
- add_next_index_stringl(z_tab, response, response_len);
- }
- efree(response);
- }
- /* Response for DEBUG object which is a formatted single line reply */
- PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
- zval *z_tab, void *ctx)
- {
- char *resp, *p, *p2, *p3, *p4;
- int is_numeric, resp_len;
- /* Add or return false if we can't read from the socket */
- if((resp = redis_sock_read(redis_sock, &resp_len))==NULL) {
- if (IS_ATOMIC(redis_sock)) {
- RETURN_FALSE;
- }
- add_next_index_bool(z_tab, 0);
- return;
- }
- zval z_result;
- array_init(&z_result);
- /* Skip the '+' */
- p = resp + 1;
- /* <info>:<value> <info2:value2> ... */
- while((p2 = strchr(p, ':'))!=NULL) {
- /* Null terminate at the ':' */
- *p2++ = '\0';
- /* Null terminate at the space if we have one */
- if((p3 = strchr(p2, ' '))!=NULL) {
- *p3++ = '\0';
- } else {
- p3 = resp + resp_len;
- }
- is_numeric = 1;
- for(p4=p2; *p4; ++p4) {
- if(*p4 < '0' || *p4 > '9') {
- is_numeric = 0;
- break;
- }
- }
- /* Add our value */
- if(is_numeric) {
- add_assoc_long(&z_result, p, atol(p2));
- } else {
- add_assoc_string(&z_result, p, p2);
- }
- p = p3;
- }
- efree(resp);
- if (IS_ATOMIC(redis_sock)) {
- RETVAL_ZVAL(&z_result, 0, 1);
- } else {
- add_next_index_zval(z_tab, &z_result);
- …
Large files files are truncated, but you can click here to view the full file