Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/phpredis/phpredis.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormichael-grunder <michael.grunder@gmail.com>2015-01-18 00:26:23 +0300
committermichael-grunder <michael.grunder@gmail.com>2015-01-18 00:26:23 +0300
commit3e13a1f514dbc479b274a8084920ee86f5b90ecb (patch)
tree2da47cf913ef218440f55b4afbb299612a9f1f3f
parent95ca34b71286a4dc887bf7ec075b820eb2a3b42c (diff)
parentc702ae01dce407c3700c18f29d2f0ef83b13bec3 (diff)
Merge branch 'develop'
Conflicts: php_redis.h
-rw-r--r--README.markdown30
-rw-r--r--common.h18
-rw-r--r--library.c414
-rw-r--r--library.h15
-rw-r--r--php_redis.h10
-rw-r--r--redis.c608
-rw-r--r--redis_array.c13
-rw-r--r--redis_array.h2
-rw-r--r--tests/TestRedis.php183
-rw-r--r--tests/test.php26
10 files changed, 1026 insertions, 293 deletions
diff --git a/README.markdown b/README.markdown
index 634193ac..69783919 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2488,6 +2488,7 @@ while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) {
* [zInter](#zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key
* [zRange](#zrange) - Return a range of members in a sorted set, by index
* [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score
+* [zRangeByLex](#zrangebylex) - Return a lexigraphical range from members that share the same score
* [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set
* [zRem, zDelete](#zrem-zdelete) - Remove one or more members from a sorted set
* [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes
@@ -2671,6 +2672,30 @@ $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2
$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); /* array('val2' => 2) */
~~~
+### zRangeByLex
+-----
+_**Description**_: Returns a lexigraphical range of members in a sorted set, assuming the members have the same score. The min and max values are required to start with '(' (exclusive), '[' (inclusive), or be exactly the values '-' (negative inf) or '+' (positive inf). The command must be called with either three *or* five arguments or will return FALSE.
+
+##### *Parameters*
+*key*: The ZSET you wish to run against
+*min*: The minimum alphanumeric value you wish to get
+*max*: The maximum alphanumeric value you wish to get
+*offset*: Optional argument if you wish to start somewhere other than the first element.
+*limit*: Optional argument if you wish to limit the number of elements returned.
+
+##### *Return value*
+*Array* containing the values in the specified range.
+
+##### *Example*
+~~~
+foreach(Array('a','b','c','d','e','f','g') as $c)
+ $redis->zAdd('key',0,$c);
+
+$redis->zRangeByLex('key','-','[c') /* Array('a','b','c'); */
+$redis->zRangeByLex('key','-','(c') /* Array('a','b') */
+$redis->zRangeByLex('key','-','[c',1,2) /* Array('b','c') */
+~~~
+
### zRank, zRevRank
-----
_**Description**_: Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score.
@@ -2873,7 +2898,7 @@ _**Description**_: Subscribe to channels by pattern
##### *Parameters*
*patterns*: An array of patterns to match
*callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message)
-
+*return value*: Mixed. Any non-null return value in the callback will be returned to the caller.
##### *Example*
~~~
function psubscribe($redis, $pattern, $chan, $msg) {
@@ -2903,7 +2928,7 @@ _**Description**_: Subscribe to channels. Warning: this function will probably c
##### *Parameters*
*channels*: an array of channels to subscribe to
*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message.
-
+*return value*: Mixed. Any non-null return value in the callback will be returned to the caller.
##### *Example*
~~~
function f($redis, $chan, $msg) {
@@ -3182,6 +3207,7 @@ $redis->_serialize(new stdClass()); // Returns "Object"
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
$redis->_serialize("foo"); // Returns 's:3:"foo";'
+~~~
### _unserialize
-----
diff --git a/common.h b/common.h
index a0623fbd..8ac05d87 100644
--- a/common.h
+++ b/common.h
@@ -30,7 +30,8 @@
/* reply types */
typedef enum _REDIS_REPLY_TYPE {
- TYPE_LINE = '+',
+ TYPE_EOF = EOF,
+ TYPE_LINE = '+',
TYPE_INT = ':',
TYPE_ERR = '-',
TYPE_BULK = '$',
@@ -64,7 +65,6 @@ typedef enum _PUBSUB_TYPE {
#define REDIS_SERIALIZER_IGBINARY 2
/* SCAN options */
-
#define REDIS_SCAN_NORETRY 0
#define REDIS_SCAN_RETRY 1
@@ -72,6 +72,14 @@ typedef enum _PUBSUB_TYPE {
#define BITOP_MIN_OFFSET 0
#define BITOP_MAX_OFFSET 4294967295
+/* Specific error messages we want to throw against */
+#define REDIS_ERR_LOADING_MSG "LOADING Redis is loading the dataset in memory"
+#define REDIS_ERR_LOADING_KW "LOADING"
+#define REDIS_ERR_AUTH_MSG "NOAUTH Authentication required."
+#define REDIS_ERR_AUTH_KW "NOAUTH"
+#define REDIS_ERR_SYNC_MSG "MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'"
+#define REDIS_ERR_SYNC_KW "MASTERDOWN"
+
#define IF_MULTI() if(redis_sock->mode == MULTI)
#define IF_MULTI_OR_ATOMIC() if(redis_sock->mode == MULTI || redis_sock->mode == ATOMIC)\
@@ -178,6 +186,12 @@ else if(redis_sock->mode == MULTI) { \
#define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a))
#define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a))
+/* Given a string and length, validate a zRangeByLex argument. The semantics
+ * here are that the argument must start with '(' or '[' or be just the char
+ * '+' or '-' */
+#define IS_LEX_ARG(s,l) \
+ (l>0 && (*s=='(' || *s=='[' || (l==1 && (*s=='+' || *s=='-'))))
+
typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode;
typedef struct fold_item {
diff --git a/library.c b/library.c
index 3e91e933..a6433ffb 100644
--- a/library.c
+++ b/library.c
@@ -32,13 +32,102 @@ PHPAPI int usleep(unsigned int useconds);
#define usleep Sleep
#endif
-#define UNSERIALIZE_ONLY_VALUES 0
-#define UNSERIALIZE_ALL 1
+#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
extern zend_class_entry *redis_ce;
extern zend_class_entry *redis_exception_ce;
extern zend_class_entry *spl_ce_RuntimeException;
+/* Helper to reselect the proper DB number when we reconnect */
+static int reselect_db(RedisSock *redis_sock TSRMLS_DC) {
+ char *cmd, *response;
+ int cmd_len, response_len;
+
+ cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber);
+
+ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
+ efree(cmd);
+ return -1;
+ }
+
+ efree(cmd);
+
+ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == 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 */
+static int resend_auth(RedisSock *redis_sock TSRMLS_DC) {
+ char *cmd, *response;
+ int cmd_len, response_len;
+
+ cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", redis_sock->auth,
+ strlen(redis_sock->auth));
+
+ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
+ efree(cmd);
+ return -1;
+ }
+
+ efree(cmd);
+
+ response = redis_sock_read(redis_sock, &response_len TSRMLS_CC);
+ if (response == NULL) {
+ return -1;
+ }
+
+ if (strncmp(response, "+OK", 3)) {
+ efree(response);
+ return -1;
+ }
+
+ efree(response);
+ return 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(char *err, size_t err_len TSRMLS_DC) {
+ /* Handle stale data error (slave syncing with master) */
+ if (err_len == sizeof(REDIS_ERR_SYNC_MSG) - 1 &&
+ !memcmp(err,REDIS_ERR_SYNC_KW,sizeof(REDIS_ERR_SYNC_KW)-1))
+ {
+ zend_throw_exception(redis_exception_ce,
+ "SYNC with master in progress or master down!", 0 TSRMLS_CC);
+ } else if (err_len == sizeof(REDIS_ERR_LOADING_MSG) - 1 &&
+ !memcmp(err,REDIS_ERR_LOADING_KW,sizeof(REDIS_ERR_LOADING_KW)-1))
+ {
+ zend_throw_exception(redis_exception_ce,
+ "Redis is LOADING the dataset", 0 TSRMLS_CC);
+ } else if (err_len == sizeof(REDIS_ERR_AUTH_MSG) -1 &&
+ !memcmp(err,REDIS_ERR_AUTH_KW,sizeof(REDIS_ERR_AUTH_KW)-1))
+ {
+ zend_throw_exception(redis_exception_ce,
+ "Failed to AUTH connection", 0 TSRMLS_CC);
+ }
+}
+
PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) {
if (!redis_sock->persistent) {
php_stream_close(redis_sock->stream);
@@ -58,64 +147,60 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
eof = php_stream_eof(redis_sock->stream);
for (; eof; count++) {
- if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */
- if(redis_sock->stream) { /* close stream if still here */
+ /* Only try up to a certain point */
+ if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) {
+ if(redis_sock->stream) { /* close stream if still here */
redis_stream_close(redis_sock TSRMLS_CC);
redis_sock->stream = NULL;
- redis_sock->mode = ATOMIC;
+ redis_sock->mode = ATOMIC;
redis_sock->status = REDIS_SOCK_STATUS_FAILED;
redis_sock->watching = 0;
- }
+ }
+
zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC);
- return -1;
- }
- if(redis_sock->stream) { /* close existing stream before reconnecting */
+ return -1;
+ }
+
+ /* Close existing stream before reconnecting */
+ if(redis_sock->stream) {
redis_stream_close(redis_sock TSRMLS_CC);
redis_sock->stream = NULL;
- redis_sock->mode = ATOMIC;
+ redis_sock->mode = ATOMIC;
redis_sock->watching = 0;
- }
- /* 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(TSRMLS_C) % redis_sock->retry_interval));
- usleep(retry_interval);
- }
+ }
+
+ /* 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(TSRMLS_C) % redis_sock->retry_interval));
+ usleep(retry_interval);
+ }
+
redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */
if(redis_sock->stream) { /* check for EOF again. */
eof = php_stream_eof(redis_sock->stream);
}
}
- /* Reselect the DB. */
- if (count && redis_sock->dbNumber) {
- char *cmd, *response;
- int cmd_len, response_len;
-
- cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber);
-
- if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
- efree(cmd);
- return -1;
- }
- efree(cmd);
-
- if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
+ /* We've reconnected if we have a count */
+ if (count) {
+ /* If we're using a password, attempt a reauthorization */
+ if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) {
return -1;
}
- if (strncmp(response, "+OK", 3)) {
- efree(response);
+ /* If we're using a non zero db, reselect it */
+ if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) {
return -1;
}
- efree(response);
}
+ /* Success */
return 0;
}
-PHP_REDIS_API int
+PHP_REDIS_API int
redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
REDIS_SCAN_TYPE type, long *iter)
{
@@ -150,13 +235,13 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
scan command this is. They all come back in slightly different ways */
switch(type) {
case TYPE_SCAN:
- return redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ 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_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
case TYPE_HSCAN:
- return redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
default:
return -1;
}
@@ -189,9 +274,10 @@ PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM
MAKE_STD_ZVAL(z_tab);
array_init(z_tab);
- redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, z_tab, numElems, 1, UNSERIALIZE_ALL);
- return z_tab;
+ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab,
+ numElems, UNSERIALIZE_ALL);
+
+ return z_tab;
}
/**
@@ -259,14 +345,14 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D
switch(inbuf[0]) {
case '-':
- err_len = strlen(inbuf+1) - 2;
+ /* Set the last error */
+ err_len = strlen(inbuf+1) - 2;
redis_sock_set_err(redis_sock, inbuf+1, err_len);
- /* stale data */
- if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) {
- zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC);
- }
- return NULL;
+ /* Filter our ERROR through the few that should actually throw */
+ redis_error_throw(inbuf + 1, err_len TSRMLS_CC);
+
+ return NULL;
case '$':
*buf_len = atoi(inbuf + 1);
resp = redis_sock_read_bulk_reply(redis_sock, *buf_len TSRMLS_CC);
@@ -432,7 +518,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) {
/**
* This command behave somehow like printf, except that strings need 2 arguments:
* Their data and their size (strlen).
- * Supported formats are: %d, %i, %s, %l
+ * Supported formats are:d, %i, %s, %l
*/
int
redis_cmd_format(char **ret, char *format, ...) {
@@ -944,15 +1030,79 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
}
}
+/* 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 TSRMLS_DC)
+{
+
+ zval *z_ret;
+ HashTable *keytable;
+ MAKE_STD_ZVAL(z_ret);
+ array_init(z_ret);
+ keytable = Z_ARRVAL_P(z_tab);
-PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) {
+ for(zend_hash_internal_pointer_reset(keytable);
+ zend_hash_has_more_elements(keytable) == SUCCESS;
+ zend_hash_move_forward(keytable)) {
- /*
- int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC);
- array_zip_values_and_scores(return_value, 0);
- */
+ char *tablekey, *hkey, *hval;
+ unsigned int tablekey_len;
+ int hkey_len;
+ unsigned long idx;
+ zval **z_key_pp, **z_value_pp;
+ zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
+ if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) {
+ continue; /* this should never happen, according to the PHP people. */
+ }
+
+ /* get current value, a key */
+ convert_to_string(*z_key_pp);
+ hkey = Z_STRVAL_PP(z_key_pp);
+ hkey_len = Z_STRLEN_PP(z_key_pp);
+
+ /* move forward */
+ zend_hash_move_forward(keytable);
+
+ /* fetch again */
+ zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
+ if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) {
+ continue; /* this should never happen, according to the PHP people. */
+ }
+
+ /* get current value, a hash value now. */
+ hval = Z_STRVAL_PP(z_value_pp);
+
+ /* Decode the score depending on flag */
+ if (decode == SCORE_DECODE_INT && Z_STRLEN_PP(z_value_pp) > 0) {
+ add_assoc_long_ex(z_ret, hkey, 1+hkey_len, atoi(hval+1));
+ } else if (decode == SCORE_DECODE_DOUBLE) {
+ add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval));
+ } else {
+ zval *z = NULL;
+ MAKE_STD_ZVAL(z);
+ *z = **z_value_pp;
+ zval_copy_ctor(z);
+ add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z);
+ }
+ }
+
+ /* replace */
+ zval_dtor(z_tab);
+ *z_tab = *z_ret;
+ zval_copy_ctor(z_tab);
+ zval_dtor(z_ret);
+
+ efree(z_ret);
+}
+
+static int
+redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+ zval *z_tab, int unserialize, int decode)
+{
char inbuf[1024];
int numElems;
zval *z_multi_result;
@@ -982,10 +1132,12 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNC
MAKE_STD_ZVAL(z_multi_result);
array_init(z_multi_result); /* pre-allocate array for multi's results. */
- redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, z_multi_result, numElems, 1, flag ? UNSERIALIZE_ALL : UNSERIALIZE_ONLY_VALUES);
+ /* Grab our key, value, key, value array */
+ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+ z_multi_result, numElems, unserialize);
- array_zip_values_and_scores(redis_sock, z_multi_result, 0 TSRMLS_CC);
+ /* Zip keys and values */
+ array_zip_values_and_scores(redis_sock, z_multi_result, decode TSRMLS_CC);
IF_MULTI_OR_PIPELINE() {
add_next_index_zval(z_tab, z_multi_result);
@@ -999,13 +1151,35 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNC
return 0;
}
-PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+/* 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);
+}
- return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1);
+/* 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);
}
-PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
- return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0);
+/* 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) {
@@ -1090,7 +1264,7 @@ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
}
/* Response for DEBUG object which is a formatted single line reply */
-PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
zval *z_tab, void *ctx)
{
char *resp, *p, *p2, *p3, *p4;
@@ -1116,7 +1290,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
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';
@@ -1138,7 +1312,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
} else {
add_assoc_string(z_result, p, p2, 1);
}
-
+
p = p3;
}
@@ -1393,7 +1567,7 @@ PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int
PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
{
char inbuf[1024];
- int numElems;
+ int numElems, err_len;
zval *z_multi_result;
if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) {
@@ -1413,6 +1587,12 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
IF_MULTI_OR_PIPELINE() {
add_next_index_bool(z_tab, 0);
} else {
+ /* Capture our error if redis has given us one */
+ if (inbuf[0] == '-') {
+ err_len = strlen(inbuf+1) - 2;
+ redis_sock_set_err(redis_sock, inbuf+1, err_len);
+ }
+
RETVAL_FALSE;
}
return -1;
@@ -1421,8 +1601,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
MAKE_STD_ZVAL(z_multi_result);
array_init(z_multi_result); /* pre-allocate array for multi's results. */
- redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, z_multi_result, numElems, 1, UNSERIALIZE_ALL);
+ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+ z_multi_result, numElems, UNSERIALIZE_ALL);
IF_MULTI_OR_PIPELINE() {
add_next_index_zval(z_tab, z_multi_result);
@@ -1437,7 +1617,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
/**
* Like multibulk reply, but don't touch the values, they won't be compressed. (this is used by HKEYS).
*/
-PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
{
char inbuf[1024];
int numElems;
@@ -1468,8 +1648,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETE
MAKE_STD_ZVAL(z_multi_result);
array_init(z_multi_result); /* pre-allocate array for multi's results. */
- redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, z_multi_result, numElems, 0, UNSERIALIZE_ALL);
+ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+ z_multi_result, numElems, UNSERIALIZE_NONE);
IF_MULTI_OR_PIPELINE() {
add_next_index_zval(z_tab, z_multi_result);
@@ -1481,6 +1661,42 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETE
return 0;
}
+PHP_REDIS_API void
+redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+ zval *z_tab, int count, int unserialize)
+{
+ char *line;
+ int len;
+
+ while(count > 0) {
+ line = redis_sock_read(redis_sock, &len TSRMLS_CC);
+ if (line != NULL) {
+ zval *z = NULL;
+ int unwrap;
+
+ /* We will attempt unserialization, if we're unserializing everything,
+ * or if we're unserializing keys and we're on a key, or we're
+ * unserializing values and we're on a value! */
+ unwrap = unserialize == UNSERIALIZE_ALL ||
+ (unserialize == UNSERIALIZE_KEYS && count % 2 == 0) ||
+ (unserialize == UNSERIALIZE_VALS && count % 2 != 0);
+
+ if (unwrap && redis_unserialize(redis_sock, line, len, &z TSRMLS_CC)) {
+ efree(line);
+ add_next_index_zval(z_tab, z);
+ } else {
+ add_next_index_stringl(z_tab, line, len, 0);
+ }
+ } else {
+ add_next_index_bool(z_tab, 0);
+ }
+
+ count--;
+ }
+}
+
+
+/*
PHP_REDIS_API int
redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only)
@@ -1509,16 +1725,16 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re
}
return 0;
}
+*/
-/**
- * redis_sock_read_multibulk_reply_assoc
- */
-PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+/* Specialized multibulk processing for HMGET where we need to pair requested
+ * keys with their returned values */
+PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
{
char inbuf[1024], *response;
int response_len;
- int i, numElems;
- zval *z_multi_result;
+ int i, numElems;
+ zval *z_multi_result;
zval **z_keys = ctx;
@@ -1526,7 +1742,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAME
return -1;
}
if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) {
- redis_stream_close(redis_sock TSRMLS_CC);
+ redis_stream_close(redis_sock TSRMLS_CC);
redis_sock->stream = NULL;
redis_sock->status = REDIS_SOCK_STATUS_FAILED;
redis_sock->mode = ATOMIC;
@@ -1550,30 +1766,30 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAME
for(i = 0; i < numElems; ++i) {
response = redis_sock_read(redis_sock, &response_len TSRMLS_CC);
if(response != NULL) {
- zval *z = NULL;
- if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) {
- efree(response);
- add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z);
- } else {
- add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0);
- }
- } else {
- add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0);
- }
- zval_dtor(z_keys[i]);
- efree(z_keys[i]);
+ zval *z = NULL;
+ if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) {
+ efree(response);
+ add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z);
+ } else {
+ add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0);
+ }
+ } else {
+ add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0);
+ }
+ zval_dtor(z_keys[i]);
+ efree(z_keys[i]);
}
efree(z_keys);
IF_MULTI_OR_PIPELINE() {
add_next_index_zval(z_tab, z_multi_result);
} else {
- *return_value = *z_multi_result;
- zval_copy_ctor(return_value);
- INIT_PZVAL(return_value);
- zval_dtor(z_multi_result);
- efree(z_multi_result);
- }
+ *return_value = *z_multi_result;
+ zval_copy_ctor(return_value);
+ INIT_PZVAL(return_value);
+ zval_dtor(z_multi_result);
+ efree(z_multi_result);
+ }
return 0;
}
@@ -1789,7 +2005,6 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz
*line_size-=2;
buf[*line_size]='\0';
-
/* Success! */
return 0;
}
@@ -1839,11 +2054,11 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval
return -1;
}
- /* If this is an error response, check if it is a SYNC error, and throw in that case */
+ /* If this is an error response, filter specific errors that should throw
+ * an exception, and set our error field in our RedisSock object. */
if(reply_type == TYPE_ERR) {
- if(memcmp(inbuf, "ERR SYNC", 9) == 0) {
- zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC);
- }
+ /* Handle throwable errors */
+ redis_error_throw(inbuf, line_size TSRMLS_CC);
/* Set our last error */
redis_sock_set_err(redis_sock, inbuf, line_size);
@@ -1912,6 +2127,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret
add_next_index_zval(*z_ret, z_subelem);
redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC);
break;
+ default: break; /* We know it's not < 0 from previous check */
}
/* Decrement our element counter */
diff --git a/library.h b/library.h
index 652db6f4..2e2d770a 100644
--- a/library.h
+++ b/library.h
@@ -31,13 +31,16 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC);
PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC);
PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx);
-PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only);
-PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter);
+PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize);
+PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+
+PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter);
PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);
diff --git a/php_redis.h b/php_redis.h
index dd315a69..ba02eb00 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -111,6 +111,7 @@ PHP_METHOD(Redis, zDelete);
PHP_METHOD(Redis, zRange);
PHP_METHOD(Redis, zReverseRange);
PHP_METHOD(Redis, zRangeByScore);
+PHP_METHOD(Redis, zRangeByLex);
PHP_METHOD(Redis, zRevRangeByScore);
PHP_METHOD(Redis, zCount);
PHP_METHOD(Redis, zDeleteRangeByScore);
@@ -196,6 +197,11 @@ PHP_METHOD(Redis, hscan);
PHP_METHOD(Redis, sscan);
PHP_METHOD(Redis, zscan);
+/* HyperLogLog commands */
+PHP_METHOD(Redis, pfadd);
+PHP_METHOD(Redis, pfcount);
+PHP_METHOD(Redis, pfmerge);
+
/* Reflection */
PHP_METHOD(Redis, getHost);
PHP_METHOD(Redis, getPort);
@@ -205,6 +211,7 @@ PHP_METHOD(Redis, getReadTimeout);
PHP_METHOD(Redis, isConnected);
PHP_METHOD(Redis, getPersistentID);
PHP_METHOD(Redis, getAuth);
+PHP_METHOD(Redis, getMode);
PHP_METHOD(Redis, rawCommand);
#ifdef PHP_WIN32
@@ -236,7 +243,6 @@ PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cm
PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd);
PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd);
-PHP_REDIS_API void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC);
PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC);
PHP_REDIS_API int get_flag(zval *object TSRMLS_DC);
@@ -271,7 +277,7 @@ extern zend_module_entry redis_module_entry;
#define phpext_redis_ptr redis_module_ptr
-#define PHP_REDIS_VERSION "2.2.5"
+#define PHP_REDIS_VERSION "2.2.6"
#endif
diff --git a/redis.c b/redis.c
index 6a2803e3..799160b8 100644
--- a/redis.c
+++ b/redis.c
@@ -192,6 +192,7 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, zReverseRange, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zCount, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC)
@@ -260,6 +261,11 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+ /* HyperLogLog commands */
+ PHP_ME(Redis, pfadd, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Redis, pfcount, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Redis, pfmerge, NULL, ZEND_ACC_PUBLIC)
+
/* options */
PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setOption, NULL, ZEND_ACC_PUBLIC)
@@ -282,7 +288,7 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, getPersistentID, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC)
-
+ PHP_ME(Redis, getMode, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC)
@@ -314,8 +320,8 @@ static zend_function_entry redis_functions[] = {
PHP_MALIAS(Redis, srem, sRemove, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, zrevrange, zReverseRange, NULL, ZEND_ACC_PUBLIC)
-
- PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC)
+
+ PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC)
@@ -474,7 +480,7 @@ PHP_MINIT_FUNCTION(redis)
zend_class_entry redis_exception_class_entry;
REGISTER_INI_ENTRIES();
-
+
/* Redis class */
INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions);
redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC);
@@ -650,7 +656,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
char *persistent_id = NULL;
int persistent_id_len = -1;
-
+
double timeout = 0.0;
RedisSock *redis_sock = NULL;
@@ -830,7 +836,7 @@ PHP_METHOD(Redis, bitcount)
/* }}} */
/* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */
-PHP_METHOD(Redis, bitpos)
+PHP_METHOD(Redis, bitpos)
{
zval *object;
RedisSock *redis_sock;
@@ -932,7 +938,7 @@ PHP_METHOD(Redis, set) {
/* Our optional argument can either be a long (to support legacy SETEX */
/* redirection), or an array with Redis >= 2.6.12 set options */
if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY
- && Z_TYPE_P(z_opts) != IS_NULL)
+ && Z_TYPE_P(z_opts) != IS_NULL)
{
RETURN_FALSE;
}
@@ -1428,7 +1434,7 @@ PHP_METHOD(Redis, incrByFloat) {
/* Prefix key, format command, free old key if necissary */
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
- if(key_free) efree(key);
+ if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
@@ -1691,12 +1697,11 @@ PHP_METHOD(Redis, getKeys)
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
- if (redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, NULL, NULL) < 0) {
+ if (redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw);
}
/* }}} */
@@ -1990,11 +1995,11 @@ PHP_METHOD(Redis, lInsert)
int pivot_len, position_len, key_len, val_len, cmd_len;
int val_free, pivot_free, key_free;
zval *z_value, *z_pivot;
-
+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osszz",
&object, redis_ce,
- &key, &key_len,
+ &key, &key_len,
&position, &position_len,
&z_pivot,
&z_value) == FAILURE) {
@@ -2004,7 +2009,7 @@ PHP_METHOD(Redis, lInsert)
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
-
+
if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) {
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
@@ -2015,15 +2020,15 @@ PHP_METHOD(Redis, lInsert)
if(key_free) efree(key);
if(pivot_free) STR_FREE(pivot);
- REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
- IF_ATOMIC() {
- redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
- }
- REDIS_PROCESS_RESPONSE(redis_long_response);
+ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+ IF_ATOMIC() {
+ redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ }
+ REDIS_PROCESS_RESPONSE(redis_long_response);
} else {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error on position");
}
-
+
}
PHP_METHOD(Redis, lPushx)
@@ -2667,7 +2672,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
if(has_timeout && Z_TYPE_P(z_args[argc - 1]) != IS_LONG) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Syntax error on timeout");
}
-
+
for(i = 0, j = 0; i < argc; ++i) { /* store each key */
if(!all_keys && j != 0) { /* not just operating on keys */
@@ -2681,7 +2686,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
}
} else {
-
+
if(Z_TYPE_P(z_args[i]) != IS_STRING) {
convert_to_string(z_args[i]);
}
@@ -2705,7 +2710,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
cmd = emalloc(cmd_len+1);
sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, keyword);
-
+
pos = 1 +integer_length(real_argc + 1) + 2
+ 1 + integer_length(keyword_len) + 2
+ keyword_len + 2;
@@ -4022,9 +4027,9 @@ PHP_METHOD(Redis, zRange)
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(withscores) {
IF_ATOMIC() {
- redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl);
} else {
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
@@ -4152,9 +4157,9 @@ PHP_METHOD(Redis, zReverseRange)
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(withscores) {
IF_ATOMIC() {
- redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl);
} else {
IF_ATOMIC() {
if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
@@ -4246,11 +4251,11 @@ redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
* we want [elt0 => val0, elt1 => val1], etc.
*/
IF_ATOMIC() {
- if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
+ if(redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl);
} else {
IF_ATOMIC() {
if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
@@ -4312,6 +4317,65 @@ PHP_METHOD(Redis, zCount)
}
/* }}} */
+/* {{{ proto array Redis::zRangeByLex(string $key, string $min, string $max,
+ * [long $offset, long $count]) */
+PHP_METHOD(Redis, zRangeByLex) {
+ zval *object;
+ RedisSock *redis_sock;
+ char *cmd, *key, *min, *max;
+ long offset, count;
+ int argc, cmd_len, key_len;
+ int key_free, min_len, max_len;
+
+ /* We need either three or five arguments for this to be a valid call */
+ argc = ZEND_NUM_ARGS();
+ if (argc != 3 && argc != 5) {
+ RETURN_FALSE;
+ }
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
+ "Osss|ll", &object, redis_ce, &key, &key_len,
+ &min, &min_len, &max, &max_len, &offset,
+ &count) == FAILURE)
+ {
+ RETURN_FALSE;
+ }
+
+ /* We can do some simple validation for the user, as we know how min/max are
+ * required to start */
+ if (!IS_LEX_ARG(min,min_len) || !IS_LEX_ARG(max,max_len)) {
+ RETURN_FALSE;
+ }
+
+ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ RETURN_FALSE;
+ }
+
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+ /* Construct our command depending on argc */
+ if (argc == 3) {
+ cmd_len = redis_cmd_format_static(&cmd, "ZRANGEBYLEX", "sss", key,
+ key_len, min, min_len, max, max_len);
+ } else {
+ cmd_len = redis_cmd_format_static(&cmd, "ZRANGEBYLEX", "ssssll", key,
+ key_len, min, min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1,
+ offset, count);
+ }
+
+ if(key_free) efree(key);
+
+ /* Kick it off */
+ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+ IF_ATOMIC() {
+ if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+ redis_sock, NULL, NULL) < 0) {
+ RETURN_FALSE;
+ }
+ }
+ REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
+}
+
/* {{{ proto long Redis::zCard(string key)
*/
PHP_METHOD(Redis, zCard)
@@ -4858,12 +4922,11 @@ PHP_METHOD(Redis, hKeys)
RETURN_FALSE;
IF_ATOMIC() {
- if (redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, NULL, NULL) < 0) {
+ if (redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw);
}
@@ -4892,72 +4955,12 @@ PHP_METHOD(Redis, hGetAll) {
RETURN_FALSE;
IF_ATOMIC() {
- if (redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+ if (redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings);
-}
-
-PHP_REDIS_API void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) {
-
- zval *z_ret;
- HashTable *keytable;
-
- MAKE_STD_ZVAL(z_ret);
- 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)) {
-
- char *tablekey, *hkey, *hval;
- unsigned int tablekey_len;
- int hkey_len;
- unsigned long idx;
- zval **z_key_pp, **z_value_pp;
-
- zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
- if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) {
- continue; /* this should never happen, according to the PHP people. */
- }
-
- /* get current value, a key */
- convert_to_string(*z_key_pp);
- hkey = Z_STRVAL_PP(z_key_pp);
- hkey_len = Z_STRLEN_PP(z_key_pp);
-
- /* move forward */
- zend_hash_move_forward(keytable);
-
- /* fetch again */
- zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL);
- if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) {
- continue; /* this should never happen, according to the PHP people. */
- }
-
- /* get current value, a hash value now. */
- hval = Z_STRVAL_PP(z_value_pp);
-
- if(use_atof) { /* zipping a score */
- add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval));
- } else { /* add raw copy */
- zval *z = NULL;
- MAKE_STD_ZVAL(z);
- *z = **z_value_pp;
- zval_copy_ctor(z);
- add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z);
- }
- }
- /* replace */
- zval_dtor(z_tab);
- *z_tab = *z_ret;
- zval_copy_ctor(z_tab);
- zval_dtor(z_ret);
-
- efree(z_ret);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_vals);
}
PHP_METHOD(Redis, hIncrByFloat)
@@ -5079,7 +5082,7 @@ PHP_METHOD(Redis, hMget) {
/* Make sure the data is a long or string, and if it's a string that */
/* it isn't empty. There is no reason to send empty length members. */
if((Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data)>0) ||
- Z_TYPE_PP(data) == IS_LONG)
+ Z_TYPE_PP(data) == IS_LONG)
{
/* This is a key we can ask for, copy it and set it in our array */
MAKE_STD_ZVAL(z_keys[valid]);
@@ -5116,9 +5119,9 @@ PHP_METHOD(Redis, hMget) {
/* Kick off our request */
REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
IF_ATOMIC() {
- redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys);
+ redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys);
}
- REDIS_PROCESS_RESPONSE_CLOSURE(redis_sock_read_multibulk_reply_assoc, z_keys);
+ REDIS_PROCESS_RESPONSE_CLOSURE(redis_mbulk_reply_assoc, z_keys);
}
PHP_METHOD(Redis, hMset)
@@ -5164,7 +5167,7 @@ PHP_METHOD(Redis, hMset)
unsigned long idx;
int type;
zval **z_value_p;
-
+
char *hval;
int hval_len, hval_free;
@@ -5359,7 +5362,7 @@ free_reply_callbacks(zval *z_this, RedisSock *redis_sock) {
fold_item *fi;
fold_item *head = redis_sock->head;
request_item *ri;
-
+
for(fi = head; fi; ) {
fold_item *fi_next = fi->next;
free(fi);
@@ -5506,9 +5509,9 @@ PHP_METHOD(Redis, pipeline)
RETURN_ZVAL(getThis(), 1, 0);
}
-/*
- publish channel message
- @return the number of subscribers
+/*
+ publish channel message
+ @return the number of subscribers
*/
PHP_METHOD(Redis, publish)
{
@@ -5554,10 +5557,10 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
zend_fcall_info_cache z_callback_cache;
zval *z_ret, **z_args[4];
-
+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf",
&object, redis_ce, &array, &z_callback, &z_callback_cache) == FAILURE) {
- RETURN_FALSE;
+ RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
@@ -5609,9 +5612,9 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
RETURN_FALSE;
}
efree(cmd);
-
+
/* read the status of the execution of the command `subscribe` */
-
+
z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
if(z_tab == NULL) {
RETURN_FALSE;
@@ -5620,15 +5623,18 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&tmp) == SUCCESS) {
type_response = Z_STRVAL_PP(tmp);
if(strcmp(type_response, sub_cmd) != 0) {
- efree(tmp);
- efree(z_tab);
+ efree(tmp);
+ zval_dtor(z_tab);
+ efree(z_tab);
RETURN_FALSE;
- }
+ }
} else {
- efree(z_tab);
+ zval_dtor(z_tab);
+ efree(z_tab);
RETURN_FALSE;
}
- efree(z_tab);
+ zval_dtor(z_tab);
+ efree(z_tab);
/* Set a pointer to our return value and to our arguments. */
z_callback.retval_ptr_ptr = &z_ret;
@@ -5636,12 +5642,12 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
z_callback.no_separation = 0;
/* Multibulk Response, format : {message type, originating channel, message payload} */
- while(1) {
+ while(1) {
/* call the callback with this z_tab in argument */
int is_pmsg, tab_idx = 1;
zval **type, **channel, **pattern, **data;
z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
-
+
if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) {
/*ERROR */
break;
@@ -5696,12 +5702,19 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
break;
}
- /* If we have a return value, free it. Note, we could use the return value to break the subscribe loop */
- if(z_ret) zval_ptr_dtor(&z_ret);
+ /* Free reply from Redis */
+ zval_dtor(z_tab);
+ efree(z_tab);
- /* TODO: provide a way to break out of the loop. */
- zval_dtor(z_tab);
- efree(z_tab);
+ /* Check for a non-null return value. If we have one, return it from
+ * the subscribe function itself. Otherwise continue our loop. */
+ if (z_ret) {
+ if (Z_TYPE_P(z_ret) != IS_NULL) {
+ RETVAL_ZVAL(z_ret, 0, 1);
+ break;
+ }
+ zval_ptr_dtor(&z_ret);
+ }
}
}
@@ -5718,7 +5731,7 @@ PHP_METHOD(Redis, subscribe) {
generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe");
}
-/**
+/**
* [p]unsubscribe channel_0 channel_1 ... channel_n
* [p]unsubscribe(array(channel_0, channel_1, ..., channel_n))
* response format :
@@ -5738,13 +5751,13 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u
RedisSock *redis_sock;
char *cmd = "", *old_cmd = NULL;
int cmd_len, array_count;
-
+
int i;
zval *z_tab, **z_channel;
-
- if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
&object, redis_ce, &array) == FAILURE) {
- RETURN_FALSE;
+ RETURN_FALSE;
}
if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
@@ -5790,10 +5803,10 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u
while( i <= array_count) {
z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
- if(Z_TYPE_P(z_tab) == IS_ARRAY) {
+ if(Z_TYPE_P(z_tab) == IS_ARRAY) {
if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) == FAILURE) {
RETURN_FALSE;
- }
+ }
add_assoc_bool(return_value, Z_STRVAL_PP(z_channel), 1);
} else {
/*error */
@@ -6033,9 +6046,9 @@ PHP_METHOD(Redis, config)
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
IF_ATOMIC() {
- redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw);
} else if(mode == CFG_SET && val != NULL) {
cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len);
@@ -6156,9 +6169,9 @@ PHP_METHOD(Redis, wait) {
int cmd_len;
/* Make sure arguments are valid */
- if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll",
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll",
&object, redis_ce, &num_slaves, &timeout)
- ==FAILURE)
+ ==FAILURE)
{
RETURN_FALSE;
}
@@ -6192,7 +6205,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
zval *arg TSRMLS_DC)
{
HashTable *ht_chan;
- HashPosition ptr;
+ HashPosition ptr;
zval **z_ele;
char *key;
int cmd_len, key_len, key_free;
@@ -6222,7 +6235,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
}
} else if(type == PUBSUB_NUMSUB) {
ht_chan = Z_ARRVAL_P(arg);
-
+
/* Add PUBSUB and NUMSUB bits */
redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
@@ -6291,9 +6304,9 @@ PHP_METHOD(Redis, pubsub) {
zval *arg=NULL;
/* Parse arguments */
- if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z",
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z",
&object, redis_ce, &keyword, &kw_len, &arg)
- ==FAILURE)
+ ==FAILURE)
{
RETURN_FALSE;
}
@@ -6308,7 +6321,7 @@ PHP_METHOD(Redis, pubsub) {
} else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
/* One array argument */
if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
- zend_hash_num_elements(Z_ARRVAL_P(arg))==0)
+ zend_hash_num_elements(Z_ARRVAL_P(arg))==0)
{
RETURN_FALSE;
}
@@ -6332,11 +6345,11 @@ PHP_METHOD(Redis, pubsub) {
if(type == PUBSUB_NUMSUB) {
IF_ATOMIC() {
- if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) {
+ if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) {
RETURN_FALSE;
}
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int);
} else {
IF_ATOMIC() {
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL)<0) {
@@ -6618,7 +6631,7 @@ PHP_METHOD(Redis, debug) {
char *cmd, *key;
int cmd_len, key_len, key_free;
- if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
&object, redis_ce, &key, &key_len)==FAILURE)
{
RETURN_FALSE;
@@ -6689,7 +6702,7 @@ PHP_METHOD(Redis, migrate) {
/* Parse arguments */
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce,
- &host, &host_len, &port, &key, &key_len, &dest_db, &timeout,
+ &host, &host_len, &port, &key, &key_len, &dest_db, &timeout,
&copy, &replace) == FAILURE) {
RETURN_FALSE;
}
@@ -6704,19 +6717,19 @@ PHP_METHOD(Redis, migrate) {
/* Construct our command */
if(copy && replace) {
- cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port,
- key, key_len, dest_db, timeout, "COPY",
+ cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port,
+ key, key_len, dest_db, timeout, "COPY",
sizeof("COPY")-1, "REPLACE", sizeof("REPLACE")-1);
} else if(copy) {
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
- key, key_len, dest_db, timeout, "COPY",
+ key, key_len, dest_db, timeout, "COPY",
sizeof("COPY")-1);
} else if(replace) {
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
key, key_len, dest_db, timeout, "REPLACE",
sizeof("REPLACE")-1);
} else {
- cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port,
+ cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port,
key, key_len, dest_db, timeout);
}
@@ -6767,7 +6780,7 @@ PHP_METHOD(Redis, _serialize) {
RedisSock *redis_sock;
zval *z_val;
char *val;
- int val_len;
+ int val_len, val_free;
/* Parse arguments */
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
@@ -6782,11 +6795,11 @@ PHP_METHOD(Redis, _serialize) {
}
/* Serialize, which will return a value even if no serializer is set */
- redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
+ val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
/* Return serialized value. Tell PHP to make a copy as some can be interned. */
RETVAL_STRINGL(val, val_len, 1);
- STR_FREE(val);
+ if(val_free) STR_FREE(val);
}
/*
@@ -6872,6 +6885,25 @@ PHP_METHOD(Redis, clearLastError) {
RETURN_TRUE;
}
+/*
+ * {{{ proto long Redis::getMode()
+ */
+PHP_METHOD(Redis, getMode) {
+ zval *object;
+ RedisSock *redis_sock;
+
+ /* Grab our object */
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ /* Grab socket */
+ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ RETURN_FALSE;
+ }
+
+ RETVAL_LONG(redis_sock->mode);
+}
/*
* {{{ proto Redis::time()
@@ -6897,11 +6929,11 @@ PHP_METHOD(Redis, time) {
/* Execute or queue command */
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
- if(redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
+ if(redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
RETURN_FALSE;
}
}
- REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
+ REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw);
}
/*
@@ -7035,8 +7067,8 @@ PHP_METHOD(Redis, client) {
int cmd_len, opt_len, arg_len;
/* Parse our method parameters */
- if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s",
- &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE)
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s",
+ &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE)
{
RETURN_FALSE;
}
@@ -7049,7 +7081,7 @@ PHP_METHOD(Redis, client) {
/* Build our CLIENT command */
if(ZEND_NUM_ARGS() == 2) {
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len,
- arg, arg_len);
+ arg, arg_len);
} else {
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
}
@@ -7243,4 +7275,280 @@ PHP_METHOD(Redis, zscan) {
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
}
+/*
+ * HyperLogLog based commands
+ */
+
+/* {{{ proto Redis::pfAdd(string key, array elements) }}} */
+PHP_METHOD(Redis, pfadd) {
+ zval *object;
+ RedisSock *redis_sock;
+ char *key;
+ int key_len, key_free, argc=1;
+ zval *z_mems, **z_mem;
+ HashTable *ht_mems;
+ HashPosition pos;
+ smart_str cmd = {0};
+
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
+ &object, redis_ce, &key, &key_len, &z_mems)
+ ==FAILURE)
+ {
+ RETURN_FALSE;
+ }
+
+ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ RETURN_FALSE;
+ }
+
+ // Grab members as an array
+ ht_mems = Z_ARRVAL_P(z_mems);
+
+ // Total arguments we'll be sending
+ argc += zend_hash_num_elements(ht_mems);
+
+ // If the array was empty we can just exit
+ if(argc < 2) {
+ RETURN_FALSE;
+ }
+
+ // Start constructing our command
+ redis_cmd_init_sstr(&cmd, argc, "PFADD", sizeof("PFADD")-1);
+
+ // Prefix our key if we're prefixing
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+ redis_cmd_append_sstr(&cmd, key, key_len);
+ if(key_free) efree(key);
+
+ // Iterate over members we're adding
+ for(zend_hash_internal_pointer_reset_ex(ht_mems, &pos);
+ zend_hash_get_current_data_ex(ht_mems, (void**)&z_mem, &pos)==SUCCESS;
+ zend_hash_move_forward_ex(ht_mems, &pos))
+ {
+ char *mem;
+ int mem_len, val_free;
+ zval *z_tmp = NULL;
+
+ // Serialize if requested
+ val_free = redis_serialize(redis_sock, *z_mem, &mem, &mem_len TSRMLS_CC);
+
+ // Allow for non string members if we're not serializing
+ if(!val_free) {
+ if(Z_TYPE_PP(z_mem)==IS_STRING) {
+ mem = Z_STRVAL_PP(z_mem);
+ mem_len = Z_STRLEN_PP(z_mem);
+ } else {
+ MAKE_STD_ZVAL(z_tmp);
+ *z_tmp = **z_mem;
+ convert_to_string(z_tmp);
+
+ mem = Z_STRVAL_P(z_tmp);
+ mem_len = Z_STRLEN_P(z_tmp);
+ }
+ }
+
+ // Append this member
+ redis_cmd_append_sstr(&cmd, mem, mem_len);
+
+ // Free memory if we serialized or converted types
+ if(z_tmp) {
+ zval_dtor(z_tmp);
+ efree(z_tmp);
+ z_tmp = NULL;
+ } else if(val_free) {
+ efree(mem);
+ }
+ }
+
+ REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
+ IF_ATOMIC() {
+ redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ }
+ REDIS_PROCESS_RESPONSE(redis_1_response);
+}
+
+/* {{{ proto Redis::pfCount(string key) }}}
+ * proto Redis::pfCount(array keys) }}} */
+PHP_METHOD(Redis, pfcount) {
+ zval *object, *z_keys, **z_key, *z_tmp = NULL;
+ HashTable *ht_keys;
+ HashPosition ptr;
+ RedisSock *redis_sock;
+ smart_str cmd = {0};
+ int num_keys, key_len, key_free;
+ char *key;
+
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
+ &object, redis_ce, &z_keys)==FAILURE)
+ {
+ RETURN_FALSE;
+ }
+
+ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ RETURN_FALSE;
+ }
+
+ /* If we were passed an array of keys, iterate through them prefixing if
+ * required and capturing lengths and if we need to free them. Otherwise
+ * attempt to treat the argument as a string and just pass one */
+ if (Z_TYPE_P(z_keys) == IS_ARRAY) {
+ /* Grab key hash table and the number of keys */
+ ht_keys = Z_ARRVAL_P(z_keys);
+ num_keys = zend_hash_num_elements(ht_keys);
+
+ /* There is no reason to send zero keys */
+ if (num_keys == 0) {
+ RETURN_FALSE;
+ }
+
+ /* Initialize the command with our number of arguments */
+ redis_cmd_init_sstr(&cmd, num_keys, "PFCOUNT", sizeof("PFCOUNT")-1);
+
+ /* Append our key(s) */
+ for (zend_hash_internal_pointer_reset_ex(ht_keys, &ptr);
+ zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &ptr)==SUCCESS;
+ zend_hash_move_forward_ex(ht_keys, &ptr))
+ {
+ /* Turn our value into a string if it isn't one */
+ if (Z_TYPE_PP(z_key) != IS_STRING) {
+ MAKE_STD_ZVAL(z_tmp);
+ *z_tmp = **z_key;
+ zval_copy_ctor(z_tmp);
+ convert_to_string(z_tmp);
+
+ key = Z_STRVAL_P(z_tmp);
+ key_len = Z_STRLEN_P(z_tmp);
+ } else {
+ key = Z_STRVAL_PP(z_key);
+ key_len = Z_STRLEN_PP(z_key);
+ }
+
+ /* Append this key to our command */
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+ redis_cmd_append_sstr(&cmd, key, key_len);
+
+ /* Cleanup */
+ if (key_free) efree(key);
+ if (z_tmp) {
+ zval_dtor(z_tmp);
+ efree(z_tmp);
+ z_tmp = NULL;
+ }
+ }
+ } else {
+ /* Turn our key into a string if it's a different type */
+ if (Z_TYPE_P(z_keys) != IS_STRING) {
+ MAKE_STD_ZVAL(z_tmp);
+ *z_tmp = *z_keys;
+ zval_copy_ctor(z_tmp);
+ convert_to_string(z_tmp);
+
+ key = Z_STRVAL_P(z_tmp);
+ key_len = Z_STRLEN_P(z_tmp);
+ } else {
+ key = Z_STRVAL_P(z_keys);
+ key_len = Z_STRLEN_P(z_keys);
+ }
+
+ /* Construct our whole command */
+ redis_cmd_init_sstr(&cmd, 1, "PFCOUNT", sizeof("PFCOUNT")-1);
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+ redis_cmd_append_sstr(&cmd, key, key_len);
+
+ /* Cleanup */
+ if (key_free) efree(key);
+ if (z_tmp) {
+ zval_dtor(z_tmp);
+ efree(z_tmp);
+ }
+ }
+
+ REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
+ IF_ATOMIC() {
+ redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ }
+ REDIS_PROCESS_RESPONSE(redis_long_response);
+}
+/* }}} */
+
+/* {{{ proto Redis::pfMerge(array keys) }}}*/
+PHP_METHOD(Redis, pfmerge) {
+ zval *object;
+ RedisSock *redis_sock;
+ zval *z_keys, **z_key;
+ HashTable *ht_keys;
+ HashPosition pos;
+ smart_str cmd = {0};
+ int key_len, key_free, argc=1;
+ char *key;
+
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
+ &object, redis_ce, &key, &key_len, &z_keys)==FAILURE)
+ {
+ RETURN_FALSE;
+ }
+
+ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ RETURN_FALSE;
+ }
+
+ // Grab keys as an array
+ ht_keys = Z_ARRVAL_P(z_keys);
+
+ // Total arguments we'll be sending
+ argc += zend_hash_num_elements(ht_keys);
+
+ // If no keys were passed we can abort
+ if(argc<2) {
+ RETURN_FALSE;
+ }
+
+ // Initial construction of our command
+ redis_cmd_init_sstr(&cmd, argc, "PFMERGE", sizeof("PFMERGE")-1);
+
+ // Add our destination key (prefixed if necessary)
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+ redis_cmd_append_sstr(&cmd, key, key_len);
+ if(key_free) efree(key);
+
+ // Iterate our keys array
+ for(zend_hash_internal_pointer_reset_ex(ht_keys, &pos);
+ zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &pos)==SUCCESS;
+ zend_hash_move_forward_ex(ht_keys, &pos))
+ {
+ zval *z_tmp = NULL;
+
+ // Keys could look like a number
+ if(Z_TYPE_PP(z_key) == IS_STRING) {
+ key = Z_STRVAL_PP(z_key);
+ key_len = Z_STRLEN_PP(z_key);
+ } else {
+ MAKE_STD_ZVAL(z_tmp);
+ *z_tmp = **z_key;
+ convert_to_string(z_tmp);
+
+ key = Z_STRVAL_P(z_tmp);
+ key_len = Z_STRLEN_P(z_tmp);
+ }
+
+ // Prefix our key if necessary and append this key
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+ redis_cmd_append_sstr(&cmd, key, key_len);
+ if(key_free) efree(key);
+
+ // Free temporary zval if we converted
+ if(z_tmp) {
+ zval_dtor(z_tmp);
+ efree(z_tmp);
+ z_tmp = NULL;
+ }
+ }
+
+ REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
+ IF_ATOMIC() {
+ redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ }
+ REDIS_PROCESS_RESPONSE(redis_boolean_response);
+}
+
/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */
diff --git a/redis_array.c b/redis_array.c
index c271fc1a..a71d938c 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -69,6 +69,8 @@ zend_function_entry redis_array_functions[] = {
PHP_ME(RedisArray, getOption, NULL, ZEND_ACC_PUBLIC)
PHP_ME(RedisArray, setOption, NULL, ZEND_ACC_PUBLIC)
PHP_ME(RedisArray, keys, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(RedisArray, save, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(RedisArray, bgsave, NULL, ZEND_ACC_PUBLIC)
/* Multi/Exec */
PHP_ME(RedisArray, multi, NULL, ZEND_ACC_PUBLIC)
@@ -625,6 +627,17 @@ PHP_METHOD(RedisArray, flushall)
multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL");
}
+PHP_METHOD(RedisArray, save)
+{
+ multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE");
+}
+
+PHP_METHOD(RedisArray, bgsave)
+{
+ multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE");
+}
+
+
PHP_METHOD(RedisArray, keys)
{
zval *object, *z_args[1], *z_tmp, z_fun;
diff --git a/redis_array.h b/redis_array.h
index 19d0a3a4..2455a375 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -30,6 +30,8 @@ PHP_METHOD(RedisArray, del);
PHP_METHOD(RedisArray, keys);
PHP_METHOD(RedisArray, getOption);
PHP_METHOD(RedisArray, setOption);
+PHP_METHOD(RedisArray, save);
+PHP_METHOD(RedisArray, bgsave);
PHP_METHOD(RedisArray, multi);
PHP_METHOD(RedisArray, exec);
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 9bbef4da..88d47254 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -88,7 +88,7 @@ class Redis_Test extends TestSuite
$this->assertTrue(is_array($result));
// PUBSUB NUMSUB
-
+
$c1 = uniqid() . '-' . rand(1,100);
$c2 = uniqid() . '-' . rand(1,100);
@@ -101,7 +101,7 @@ class Redis_Test extends TestSuite
// Make sure the elements are correct, and have zero counts
foreach(Array($c1,$c2) as $channel) {
$this->assertTrue(isset($result[$channel]));
- $this->assertEquals($result[$channel], "0");
+ $this->assertEquals($result[$channel], 0);
}
// PUBSUB NUMPAT
@@ -218,7 +218,7 @@ class Redis_Test extends TestSuite
$this->assertEquals('val', $this->redis->get('key2'));
$value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
-
+
$this->redis->set('key2', $value);
$this->assertEquals($value, $this->redis->get('key2'));
$this->assertEquals($value, $this->redis->get('key2'));
@@ -528,7 +528,7 @@ class Redis_Test extends TestSuite
}
$this->redis->delete('key');
-
+
$this->redis->set('key', 0);
$this->redis->incrbyfloat('key', 1.5);
@@ -1227,9 +1227,9 @@ class Redis_Test extends TestSuite
}
}
- //
+ //
// With and without count, while serializing
- //
+ //
$this->redis->delete('set0');
$this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
@@ -1253,11 +1253,11 @@ class Redis_Test extends TestSuite
public function testSRandMemberWithCount() {
// Make sure the set is nuked
$this->redis->delete('set0');
-
+
// Run with a count (positive and negative) on an empty set
$ret_pos = $this->redis->sRandMember('set0', 10);
$ret_neg = $this->redis->sRandMember('set0', -10);
-
+
// Should both be empty arrays
$this->assertTrue(is_array($ret_pos) && empty($ret_pos));
$this->assertTrue(is_array($ret_neg) && empty($ret_neg));
@@ -1845,10 +1845,10 @@ class Redis_Test extends TestSuite
// We should have found our connection
$this->assertFalse(empty($str_addr));
-
+
/* CLIENT GETNAME */
$this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests');
-
+
/* CLIENT KILL -- phpredis will reconnect, so we can do this */
$this->assertTrue($this->redis->client('kill', $str_addr));
}
@@ -2037,14 +2037,13 @@ class Redis_Test extends TestSuite
}
public function testZAddFirstArg() {
+ $zsetName = 100; // Make sure int keys work
+ $this->redis->delete($zsetName);
- $this->redis->delete('key');
-
- $zsetName = 100; // not a string!
- $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0'));
- $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1'));
+ $this->assertEquals(1, $this->redis->zAdd($zsetName, 0, 'val0'));
+ $this->assertEquals(1, $this->redis->zAdd($zsetName, 1, 'val1'));
- $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1));
+ $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1));
}
public function testZX() {
@@ -2328,6 +2327,40 @@ class Redis_Test extends TestSuite
}
+ public function testZRangeByLex() {
+ /* Only out since 2.8.9 */
+ if (version_compare($this->version, '2.8.9', 'lt')) {
+ $this->markTestSkipped();
+ return;
+ }
+
+ $arr_vals = Array('a','b','c','d','e','f','g');
+
+ $this->redis->del('zlex');
+ foreach($arr_vals as $str_val) {
+ $this->redis->zadd('zlex', 0, $str_val);
+ }
+
+ /* These tests were taken off of redis.io out of sheer laziness :) */
+ $arr_ret = $this->redis->zRangeByLex('zlex', '-', '[c');
+ $this->assertTrue($arr_ret === Array('a','b','c'));
+
+ $arr_ret = $this->redis->zRangeByLex('zlex', '-', '(c');
+ $this->assertTrue($arr_ret === Array('a','b'));
+
+ $arr_ret = $this->redis->zRangeByLex('zlex', '[aaa', '(g');
+ $this->assertTrue($arr_ret === Array('b','c','d','e','f'));
+
+ /* Test with a limit and count */
+ $arr_ret = $this->redis->zRangeBylex('zlex', '-', '[c', 1, 2);
+ $this->assertTrue($arr_ret === Array('b','c'));
+
+ /* Test some invalid calls */
+ $this->assertFalse($this->redis->zRangeByLex('zlex','b','[s'));
+ $this->assertFalse($this->redis->zRangeByLex('zlex','(a', ''));
+ $this->assertFalse($this->redis->zRangeByLex('zlex','(a','[b',1));
+ }
+
public function testHashes() {
$this->redis->delete('h', 'key');
@@ -2466,13 +2499,21 @@ class Redis_Test extends TestSuite
}
public function testObject() {
- $this->redis->del('key');
+ /* Version 3.0.0 (represented as >= 2.9.0 in redis info) and moving
+ * forward uses "embstr" instead of "raw" for small string values */
+ if (version_compare($this->version, "2.9.0", "lt")) {
+ $str_small_encoding = "raw";
+ } else {
+ $str_small_encoding = "embstr";
+ }
+
+ $this->redis->del('key');
$this->assertTrue($this->redis->object('encoding', 'key') === FALSE);
$this->assertTrue($this->redis->object('refcount', 'key') === FALSE);
$this->assertTrue($this->redis->object('idletime', 'key') === FALSE);
$this->redis->set('key', 'value');
- $this->assertTrue($this->redis->object('encoding', 'key') === "raw");
+ $this->assertTrue($this->redis->object('encoding', 'key') === $str_small_encoding);
$this->assertTrue($this->redis->object('refcount', 'key') === 1);
$this->assertTrue($this->redis->object('idletime', 'key') === 0);
@@ -4496,7 +4537,7 @@ class Redis_Test extends TestSuite
public function testSerialize() {
$vals = Array(1, 1.5, 'one', Array('here','is','an','array'));
-
+
// Test with no serialization at all
$this->assertTrue($this->redis->_serialize('test') === 'test');
$this->assertTrue($this->redis->_serialize(1) === '1');
@@ -4509,7 +4550,7 @@ class Redis_Test extends TestSuite
}
foreach($arr_serializers as $mode) {
- $arr_enc = Array();
+ $arr_enc = Array();
$arr_dec = Array();
foreach($vals as $k => $v) {
@@ -4679,7 +4720,7 @@ class Redis_Test extends TestSuite
$this->markTestSkipped();
return;
}
-
+
// Never get empty sets
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
@@ -4688,7 +4729,7 @@ class Redis_Test extends TestSuite
for($i=0;$i<100;$i++) {
if($i>3) {
- $this->redis->hset('hash', "member:$i", "value:$i");
+ $this->redis->hset('hash', "member:$i", "value:$i");
} else {
$this->redis->hset('hash', "foomember:$i", "value:$i");
$i_foo_mems++;
@@ -4766,7 +4807,7 @@ class Redis_Test extends TestSuite
} else {
$this->redis->zadd('zset', $i, "mem:$i");
}
-
+
$i_tot_score += $i;
}
@@ -4778,8 +4819,9 @@ class Redis_Test extends TestSuite
$i--;
}
}
+
$this->assertEquals(0, $i);
- $this->assertEquals(0, $i_tot_score);
+ $this->assertEquals(0.0, $i_tot_score);
// Just scan "pmem" members
$it = NULL;
@@ -4791,7 +4833,7 @@ class Redis_Test extends TestSuite
$i_p_count -= 1;
}
}
- $this->assertEquals(0, $i_p_score);
+ $this->assertEquals(0.0, $i_p_score);
$this->assertEquals(0, $i_p_count);
// Turn off retrying and we should get some empty results
@@ -4809,11 +4851,98 @@ class Redis_Test extends TestSuite
}
// We should still get all the keys, just with several empty results
$this->assertTrue($i_skips > 0);
- $this->assertEquals(0, $i_p_score);
+ $this->assertEquals(0.0, $i_p_score);
$this->assertEquals(0, $i_p_count);
}
+
+ //
+ // HyperLogLog (PF) commands
+ //
+
+ protected function createPFKey($str_key, $i_count) {
+ $arr_mems = Array();
+ for($i=0;$i<$i_count;$i++) {
+ $arr_mems[] = uniqid() . '-' . $i;
+ }
+
+ // Estimation by Redis
+ $this->redis->pfadd($str_key, $i_count);
+ }
+
+ public function testPFCommands() {
+ // Isn't available until 2.8.9
+ if(version_compare($this->version, "2.8.9", "lt")) {
+ $this->markTestSkipped();
+ return;
+ }
+
+ $str_uniq = uniqid();
+ $arr_mems = Array();
+
+ for($i=0;$i<1000;$i++) {
+ if($i%2 == 0) {
+ $arr_mems[] = $str_uniq . '-' . $i;
+ } else {
+ $arr_mems[] = $i;
+ }
+ }
+
+ // How many keys to create
+ $i_keys = 10;
+
+ // Iterate prefixing/serialization options
+ foreach(Array(Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP) as $str_ser) {
+ foreach(Array('', 'hl-key-prefix:') as $str_prefix) {
+ $arr_keys = Array();
+
+ // Now add for each key
+ for($i=0;$i<$i_keys;$i++) {
+ $str_key = "key:$i";
+ $arr_keys[] = $str_key;
+
+ // Clean up this key
+ $this->redis->del($str_key);
+
+ // Add to our cardinality set, and confirm we got a valid response
+ $this->assertTrue($this->redis->pfadd($str_key, $arr_mems));
+
+ // Grab estimated cardinality
+ $i_card = $this->redis->pfcount($str_key);
+ $this->assertTrue(is_int($i_card));
+
+
+ // Count should be close
+ $this->assertLess(abs($i_card-count($arr_mems)), count($arr_mems) * .1);
+
+ // The PFCOUNT on this key should be the same as the above returned response
+ $this->assertEquals($this->redis->pfcount($str_key), $i_card);
+
+ }
+
+ // Make sure we can pass an array of keys into pfCount
+ $i_card = $this->redis->pfcount($arr_keys);
+ $this->assertTrue(is_int($i_card));
+
+ // Clean up merge key
+ $this->redis->del('pf-merge-key');
+
+ // Merge the counters
+ $this->assertTrue($this->redis->pfmerge('pf-merge-key', $arr_keys));
+
+ // Validate our merged count
+ $i_redis_card = $this->redis->pfcount('pf-merge-key');
+
+ // Merged cardinality should still be roughly 1000
+ $this->assertLess(abs($i_redis_card-count($arr_mems)), count($arr_mems) * .1);
+
+ // Clean up merge key
+ $this->redis->del('pf-merge-key');
+ }
+ }
+ }
}
-exit(TestSuite::run("Redis_Test"));
+$str_test = isset($argv[1]) ? $argv[1] : NULL;
+exit(TestSuite::run("Redis_Test", $str_test));
?>
diff --git a/tests/test.php b/tests/test.php
index 8ccba5cf..43d6a445 100644
--- a/tests/test.php
+++ b/tests/test.php
@@ -2,7 +2,6 @@
// phpunit is such a pain to install, we're going with pure-PHP here.
class TestSuite {
-
public static $errors = array();
public static $warnings = array();
@@ -19,6 +18,16 @@ class TestSuite {
$bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
}
+ protected function assertLess($a, $b) {
+ if($a < $b)
+ return;
+
+ $bt = debug_backtrace(false);
+ self::$errors[] = sprintf("Assertion failed (%s >= %s): %s: %d (%s\n",
+ print_r($a, true), print_r($b, true),
+ $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
+ }
+
protected function assertEquals($a, $b) {
if($a === $b)
return;
@@ -37,17 +46,24 @@ class TestSuite {
throw new Exception($msg);
}
- public static function run($className) {
-
- $rc = new ReflectionClass($className);
+ public static function run($className, $str_limit) {
+ $rc = new ReflectionClass($className);
$methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC);
- foreach($methods as $m) {
+ if ($str_limit) {
+ echo "Limiting to tests with the substring: '$str_limit'\n";
+ }
+ foreach($methods as $m) {
$name = $m->name;
if(substr($name, 0, 4) !== 'test')
continue;
+ /* If TestRedis.php was envoked with an argument, do a simple
+ * match against the routine. Useful to limit to one test */
+ if ($str_limit && strpos(strtolower($name),strtolower($str_limit))===false)
+ continue;
+
$count = count($className::$errors);
$rt = new $className;
try {