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>2013-09-02 05:39:08 +0400
committermichael-grunder <michael.grunder@gmail.com>2013-09-02 05:39:08 +0400
commit19e6f315f24735a86a1deff67e99003d66df65e1 (patch)
treee17ffc84b45b82da9a4df343b915e4483abf1297
parent4c24dfa5c5320e1bedbb3fe72286ede4e2f7117e (diff)
parent8ed1a29ad3bcbfbfbd9b7e06b4e78f9010751c92 (diff)
Merge branch 'release/2.2.4'2.2.4
-rw-r--r--README.markdown50
-rw-r--r--arrays.markdown25
-rw-r--r--common.h15
-rw-r--r--config.h69
-rw-r--r--debian.control2
-rw-r--r--library.c58
-rw-r--r--library.h7
-rw-r--r--package.xml14
-rw-r--r--php_redis.h3
-rw-r--r--redis.c560
-rw-r--r--redis_array.c105
-rw-r--r--redis_array.h1
-rw-r--r--redis_array_impl.c107
-rw-r--r--redis_array_impl.h4
-rw-r--r--redis_session.c4
-rw-r--r--rpm/php-redis.spec2
-rw-r--r--tests/TestRedis.php200
17 files changed, 849 insertions, 377 deletions
diff --git a/README.markdown b/README.markdown
index ce978c05..7d5d2dc8 100644
--- a/README.markdown
+++ b/README.markdown
@@ -165,6 +165,8 @@ _**Description**_: Connects to a Redis instance.
*host*: string. can be a host, or the path to a unix domain socket
*port*: int, optional
*timeout*: float, value in seconds (optional, default is 0 meaning unlimited)
+*reserved*: should be NULL if retry_interval is specified
+*retry_interval*: int, value in milliseconds (optional)
##### *Return value*
@@ -177,6 +179,7 @@ $redis->connect('127.0.0.1', 6379);
$redis->connect('127.0.0.1'); // port 6379 by default
$redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
$redis->connect('/tmp/redis.sock'); // unix domain socket.
+$redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
~~~
### pconnect, popen
@@ -199,6 +202,7 @@ persistent equivalents.
*port*: int, optional
*timeout*: float, value in seconds (optional, default is 0 meaning unlimited)
*persistent_id*: string. identity for the requested persistent connection
+*retry_interval*: int, value in milliseconds (optional)
##### *Return value*
@@ -322,6 +326,7 @@ _**Description**_: Sends a string to Redis, which replies with the same string
1. [save](#save) - Synchronously save the dataset to disk (wait to complete)
1. [slaveof](#slaveof) - Make the server a slave of another instance, or promote it to master
1. [time](#time) - Return the current server time
+1. [slowlog](#slowlog) - Access the Redis slowlog entries
### bgrewriteaof
-----
@@ -539,6 +544,36 @@ the unix timestamp, and element one being microseconds.
$redis->time();
~~~
+### slowlog
+-----
+_**Description**_: Access the Redis slowlog
+
+##### *Parameters*
+*Operation* (string): This can be either `GET`, `LEN`, or `RESET`
+*Length* (integer), optional: If executing a `SLOWLOG GET` command, you can pass an optional length.
+#####
+
+##### *Return value*
+The return value of SLOWLOG will depend on which operation was performed.
+SLOWLOG GET: Array of slowlog entries, as provided by Redis
+SLOGLOG LEN: Integer, the length of the slowlog
+SLOWLOG RESET: Boolean, depending on success
+#####
+
+##### *Examples*
+~~~
+// Get ten slowlog entries
+$redis->slowlog('get', 10);
+// Get the default number of slowlog entries
+
+$redis->slowlog('get');
+// Reset our slowlog
+$redis->slowlog('reset');
+
+// Retrieve slowlog length
+$redis->slowlog('len');
+~~~
+
## Keys and Strings
### Strings
@@ -604,19 +639,30 @@ $redis->get('key');
### set
-----
-_**Description**_: Set the string value in argument as value of the key.
+_**Description**_: Set the string value in argument as value of the key. If you're using Redis >= 2.6.12, you can pass extended options as explained below
##### *Parameters*
*Key*
*Value*
-*Timeout* (optional). Calling `SETEX` is preferred if you want a timeout.
+*Timeout or Options Array* (optional). If you pass an integer, phpredis will redirect to SETEX, and will try to use Redis >= 2.6.12 extended options if you pass an array with valid values
##### *Return value*
*Bool* `TRUE` if the command is successful.
##### *Examples*
~~~
+// Simple key -> value set
$redis->set('key', 'value');
+
+// Will redirect, and actually make an SETEX call
+$redis->set('key','value', 10);
+
+// Will set the key, if it doesn't exist, with a ttl of 10 seconds
+$redis->set('key', 'value', Array('nx', 'ex'=>10);
+
+// Will set a key, if it does exist, with a ttl of 1000 miliseconds
+$redis->set('key', 'value', Array('xx', 'px'=>1000);
+
~~~
### setex, psetex
diff --git a/arrays.markdown b/arrays.markdown
index 16c9601c..b1220891 100644
--- a/arrays.markdown
+++ b/arrays.markdown
@@ -17,7 +17,7 @@ There are several ways of creating Redis arrays; they can be pre-defined in red
#### Declaring a new array with a list of nodes
<pre>
-$ra = new RedisArray(array("host1", "host2:63792, "host2:6380"));
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"));
</pre>
@@ -26,7 +26,7 @@ $ra = new RedisArray(array("host1", "host2:63792, "host2:6380"));
function extract_key_part($k) {
return substr($k, 0, 3); // hash only on first 3 characters.
}
-$ra = new RedisArray(array("host1", "host2:63792, "host2:6380"), array("function" => "extract_key_part"));
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("function" => "extract_key_part"));
</pre>
#### Defining a "previous" array when nodes are added or removed.
@@ -34,7 +34,19 @@ When a new node is added to an array, phpredis needs to know about it. The old l
<pre>
// adding host3 to a ring containing host1 and host2. Read commands will look in the previous ring if the data is not found in the main ring.
-$ra = new RedisArray(array('host1', 'host2', 'host3'), array('previous' => array('host1', 'host2')));
+$ra = new RedisArray(array("host1", "host2", "host3"), array("previous" => array("host1", "host2")));
+</pre>
+
+#### Specifying the "retry_interval" parameter
+The retry_interval is used to specify a delay in milliseconds between reconnection attempts in case the client loses connection with a server
+<pre>
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_timeout" => 100)));
+</pre>
+
+#### Specifying the "lazy_connect" parameter
+This option is useful when a cluster has many shards but not of them are necessarily used at one time.
+<pre>
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("lazy_connect" => true)));
</pre>
#### Defining arrays in Redis.ini
@@ -76,6 +88,13 @@ In order to control the distribution of keys by hand, you can provide a custom f
For instance, instanciate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server.
+### Example
+<pre>
+$ra = new RedisArray(array("host1", "host2", "host3", "host4", "host5", "host6", "host7", "host8"), array("distributor" => array(2, 2)));
+</pre>
+
+This declares that we started with 2 shards and moved to 4 then 8 shards. The number of initial shards is 2 and the resharding level (or number of iterations) is 2.
+
## Migrating keys
When a node is added or removed from a ring, RedisArray instances must be instanciated with a “previous” list of nodes. A single call to `$ra->_rehash()` causes all the keys to be redistributed according to the new list of nodes. Passing a callback function to `_rehash()` makes it possible to track the progress of that operation: the function is called with a node name and a number of keys that will be examined, e.g. `_rehash(function ($host, $count){ ... });`.
diff --git a/common.h b/common.h
index 2abb4209..63b1d9ee 100644
--- a/common.h
+++ b/common.h
@@ -5,6 +5,11 @@
#ifndef REDIS_COMMON_H
#define REDIS_COMMON_H
+/* NULL check so Eclipse doesn't go crazy */
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
#define redis_sock_name "Redis Socket Buffer"
#define REDIS_SOCK_STATUS_FAILED 0
#define REDIS_SOCK_STATUS_DISCONNECTED 1
@@ -138,6 +143,15 @@ else if(redis_sock->mode == MULTI) { \
#define REDIS_PROCESS_RESPONSE(function) REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL)
+/* Extended SET argument detection */
+#define IS_EX_ARG(a) ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+#define IS_PX_ARG(a) ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+#define IS_NX_ARG(a) ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+#define IS_XX_ARG(a) ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+
+#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))
+
typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode;
typedef struct fold_item {
@@ -182,6 +196,7 @@ typedef struct {
char *err;
int err_len;
+ zend_bool lazy_connect;
} RedisSock;
/* }}} */
diff --git a/config.h b/config.h
new file mode 100644
index 00000000..00c355bd
--- /dev/null
+++ b/config.h
@@ -0,0 +1,69 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.in by autoheader. */
+
+/* Whether to build redis as dynamic module */
+#define COMPILE_DL_REDIS 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Whether redis igbinary serializer is enabled */
+/* #undef HAVE_REDIS_IGBINARY */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* redis sessions */
+#define PHP_SESSION 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
diff --git a/debian.control b/debian.control
index 5bd408b9..0a5fe738 100644
--- a/debian.control
+++ b/debian.control
@@ -1,5 +1,5 @@
Package: phpredis
-Version: 2.2.2
+Version: 2.2.4
Section: web
Priority: optional
Architecture: all
diff --git a/library.c b/library.c
index 8a35ffda..19a6f43b 100644
--- a/library.c
+++ b/library.c
@@ -455,6 +455,22 @@ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_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_str *str, int num_args, char *keyword, int keyword_len) {
+ smart_str_appendc(str, '*');
+ smart_str_append_long(str, num_args + 1);
+ smart_str_appendl(str, _NL, sizeof(_NL) -1);
+ smart_str_appendc(str, '$');
+ smart_str_append_long(str, keyword_len);
+ smart_str_appendl(str, _NL, sizeof(_NL) - 1);
+ smart_str_appendl(str, keyword, keyword_len);
+ smart_str_appendl(str, _NL, sizeof(_NL) - 1);
+ return str->len;
+}
+
+/*
* Append a command sequence to a smart_str
*/
int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) {
@@ -469,6 +485,44 @@ int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) {
}
/*
+ * Append an integer to a smart string command
+ */
+int redis_cmd_append_sstr_int(smart_str *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_str *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 double to a smart string command
+ */
+int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
+ char *dbl_str;
+ int dbl_len;
+
+ /// Convert to double
+ REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value);
+
+ // Append the string
+ int retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
+
+ // Free our double string
+ efree(dbl_str);
+
+ // Return new length
+ return retval;
+}
+
+/*
* Append an integer command to a Redis command
*/
int redis_cmd_append_int(char **cmd, int cmd_len, int append) {
@@ -970,7 +1024,8 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
*/
PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port,
double timeout, int persistent, char *persistent_id,
- long retry_interval)
+ long retry_interval,
+ zend_bool lazy_connect)
{
RedisSock *redis_sock;
@@ -982,6 +1037,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por
redis_sock->dbNumber = 0;
redis_sock->retry_interval = retry_interval * 1000;
redis_sock->persistent = persistent;
+ redis_sock->lazy_connect = lazy_connect;
if(persistent_id) {
size_t persistent_id_len = strlen(persistent_id);
diff --git a/library.h b/library.h
index 2f83b3ea..7b1d0383 100644
--- a/library.h
+++ b/library.h
@@ -4,9 +4,12 @@ int redis_cmd_format(char **ret, char *format, ...);
int redis_cmd_format_static(char **ret, char *keyword, char *format, ...);
int redis_cmd_format_header(char **ret, char *keyword, int arg_count);
int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len);
+int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len);
int redis_cmd_append_sstr(smart_str *str, char *append, int append_len);
+int redis_cmd_append_sstr_int(smart_str *str, int append);
+int redis_cmd_append_sstr_long(smart_str *str, long append);
int redis_cmd_append_int(char **cmd, int cmd_len, int append);
-
+int redis_cmd_append_sstr_dbl(smart_str *str, double value);
PHPAPI char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC);
@@ -20,7 +23,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis
PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval);
+PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect);
PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC);
PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC);
PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC);
diff --git a/package.xml b/package.xml
index 1fd2ab3b..dd97ad77 100644
--- a/package.xml
+++ b/package.xml
@@ -21,10 +21,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
<email>michael.grunder@gmail.com</email>
<active>yes</active>
</lead>
- <date>2013-04-29</date>
+ <date>2013-09-01</date>
<version>
- <release>2.2.3</release>
- <api>2.2.3</api>
+ <release>2.2.4</release>
+ <api>2.2.4</api>
</version>
<stability>
<release>stable</release>
@@ -72,6 +72,14 @@ http://pear.php.net/dtd/package-2.0.xsd">
<changelog>
<release>
<stability><release>stable</release><api>stable</api></stability>
+ <version><release>2.2.4</release><api>2.2.4</api></version>
+ <date>2013-09-01</date>
+ <notes>
+ See GitHub for release notes
+ </notes>
+ </release>
+ <release>
+ <stability><release>stable</release><api>stable</api></stability>
<version><release>2.2.3</release><api>2.2.3</api></version>
<date>2013-04-29</date>
<notes>
diff --git a/php_redis.h b/php_redis.h
index a8f99be0..879503aa 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -180,6 +180,7 @@ PHP_METHOD(Redis, getOption);
PHP_METHOD(Redis, setOption);
PHP_METHOD(Redis, config);
+PHP_METHOD(Redis, slowlog);
PHP_METHOD(Redis, client);
@@ -256,7 +257,7 @@ extern zend_module_entry redis_module_entry;
#define phpext_redis_ptr redis_module_ptr
-#define PHP_REDIS_VERSION "2.2.3"
+#define PHP_REDIS_VERSION "2.2.4"
#endif
diff --git a/redis.c b/redis.c
index 6b283170..38a01142 100644
--- a/redis.c
+++ b/redis.c
@@ -72,8 +72,8 @@ PHP_INI_END()
ZEND_DECLARE_MODULE_GLOBALS(redis)
static zend_function_entry redis_functions[] = {
- PHP_ME(Redis, __construct, NULL, ZEND_ACC_PUBLIC)
- PHP_ME(Redis, __destruct, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Redis, __construct, NULL, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC)
+ PHP_ME(Redis, __destruct, NULL, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC)
PHP_ME(Redis, connect, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pconnect, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, close, NULL, ZEND_ACC_PUBLIC)
@@ -237,6 +237,9 @@ static zend_function_entry redis_functions[] = {
/* config */
PHP_ME(Redis, config, NULL, ZEND_ACC_PUBLIC)
+ /* slowlog */
+ PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC)
+
/* introspection */
PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC)
@@ -394,6 +397,13 @@ PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_thr
}
return -1;
}
+ if ((*redis_sock)->lazy_connect)
+ {
+ (*redis_sock)->lazy_connect = 0;
+ if (redis_sock_server_open(*redis_sock, 1 TSRMLS_CC) < 0) {
+ return -1;
+ }
+ }
return Z_LVAL_PP(socket);
}
@@ -629,18 +639,17 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
}
/* if there is a redis sock already we have to remove it from the list */
- if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) > 0) {
+ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) {
if (zend_hash_find(Z_OBJPROP_P(object), "socket",
- sizeof("socket"), (void **) &socket) == FAILURE) {
+ sizeof("socket"), (void **) &socket) == FAILURE)
+ {
/* maybe there is a socket but the id isn't known.. what to do? */
} else {
zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the detructor called */
}
- } else {
- zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */
}
- redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval);
+ redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0);
if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) {
redis_free_socket(redis_sock);
@@ -802,43 +811,103 @@ PHP_METHOD(Redis, close)
}
/* }}} */
-/* {{{ proto boolean Redis::set(string key, mixed value)
- */
-PHP_METHOD(Redis, set)
-{
+/* {{{ proto boolean Redis::set(string key, mixed value, long timeout | array options) */
+PHP_METHOD(Redis, set) {
zval *object;
RedisSock *redis_sock;
- char *key = NULL, *val = NULL, *cmd;
+ char *key = NULL, *val = NULL, *cmd, *exp_type = NULL, *set_type = NULL;
int key_len, val_len, cmd_len;
long expire = -1;
int val_free = 0, key_free = 0;
- zval *z_value;
+ zval *z_value, *z_opts = NULL;
- if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|l",
- &object, redis_ce, &key, &key_len,
- &z_value, &expire) == FAILURE) {
+ // Make sure the arguments are correct
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|z",
+ &object, redis_ce, &key, &key_len, &z_value,
+ &z_opts) == FAILURE)
+ {
RETURN_FALSE;
}
- if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ // Ensure we can grab our redis socket
+ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ RETURN_FALSE;
+ }
+
+ /* 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) {
RETURN_FALSE;
}
+ /* Serialization, key prefixing */
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
- key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
- if(expire > 0) {
- cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", key, key_len, expire, val, val_len);
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+ if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
+ HashTable *kt = Z_ARRVAL_P(z_opts);
+ int type;
+ unsigned int ht_key_len;
+ unsigned long idx;
+ char *k;
+ zval **v;
+
+ /* Iterate our option array */
+ for(zend_hash_internal_pointer_reset(kt);
+ zend_hash_has_more_elements(kt) == SUCCESS;
+ zend_hash_move_forward(kt))
+ {
+ // Grab key and value
+ type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL);
+ zend_hash_get_current_data(kt, (void**)&v);
+
+ if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) &&
+ (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k))
+ {
+ exp_type = k;
+ expire = Z_LVAL_PP(v);
+ } else if(Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) {
+ set_type = Z_STRVAL_PP(v);
+ }
+ }
+ } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) {
+ expire = Z_LVAL_P(z_opts);
+ }
+
+ /* Now let's construct the command we want */
+ if(exp_type && set_type) {
+ /* SET <key> <value> NX|XX PX|EX <timeout> */
+ cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl", key, key_len,
+ val, val_len, set_type, 2, exp_type,
+ 2, expire);
+ } else if(exp_type) {
+ /* SET <key> <value> PX|EX <timeout> */
+ cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl", key, key_len,
+ val, val_len, exp_type, 2, expire);
+ } else if(set_type) {
+ /* SET <key> <value> NX|XX */
+ cmd_len = redis_cmd_format_static(&cmd, "SET", "sss", key, key_len,
+ val, val_len, set_type, 2);
+ } else if(expire > 0) {
+ /* Backward compatible SETEX redirection */
+ cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", key, key_len,
+ expire, val, val_len);
} else {
- cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, key_len, val, val_len);
+ /* SET <key> <value> */
+ cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, key_len,
+ val, val_len);
}
- if(val_free) efree(val);
+
+ /* Free our key or value if we prefixed/serialized */
if(key_free) efree(key);
+ if(val_free) efree(val);
- REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
- IF_ATOMIC() {
- redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
- }
- REDIS_PROCESS_RESPONSE(redis_boolean_response);
+ /* Kick off the command */
+ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+ IF_ATOMIC() {
+ redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+ }
+ REDIS_PROCESS_RESPONSE(redis_boolean_response);
}
PHPAPI void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
@@ -1323,88 +1392,85 @@ PHP_METHOD(Redis, decrBy){
*/
PHP_METHOD(Redis, getMultiple)
{
- zval *object, *array, **data;
- HashTable *arr_hash;
- HashPosition pointer;
+ zval *object, *z_args, **z_ele;
+ HashTable *hash;
+ HashPosition ptr;
RedisSock *redis_sock;
- char *cmd = "", *old_cmd = NULL;
- int cmd_len = 0, array_count, elements = 1;
+ smart_str cmd = {0};
+ int arg_count;
- if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
- &object, redis_ce, &array) == FAILURE) {
+ // Make sure we have proper arguments
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
+ &object, redis_ce, &z_args) == FAILURE) {
RETURN_FALSE;
}
- if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ // We'll need the socket
+ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
- arr_hash = Z_ARRVAL_P(array);
- array_count = zend_hash_num_elements(arr_hash);
+ // Grab our array
+ hash = Z_ARRVAL_P(z_args);
- if (array_count == 0) {
+ // We don't need to do anything if there aren't any keys
+ if((arg_count = zend_hash_num_elements(hash)) == 0) {
RETURN_FALSE;
}
- for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer);
- zend_hash_get_current_data_ex(arr_hash, (void**) &data,
- &pointer) == SUCCESS;
- zend_hash_move_forward_ex(arr_hash, &pointer)) {
+ // Build our command header
+ redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4);
+ // Iterate through and grab our keys
+ for(zend_hash_internal_pointer_reset_ex(hash, &ptr);
+ zend_hash_get_current_data_ex(hash, (void**)&z_ele, &ptr) == SUCCESS;
+ zend_hash_move_forward_ex(hash, &ptr))
+ {
char *key;
- int key_len;
+ int key_len, key_free;
zval *z_tmp = NULL;
- char *old_cmd;
- int key_free;
- if (Z_TYPE_PP(data) == IS_STRING) {
- key = Z_STRVAL_PP(data);
- key_len = Z_STRLEN_PP(data);
- } else { /* not a string, copy and convert. */
+ // If the key isn't a string, turn it into one
+ if(Z_TYPE_PP(z_ele) == IS_STRING) {
+ key = Z_STRVAL_PP(z_ele);
+ key_len = Z_STRLEN_PP(z_ele);
+ } else {
MAKE_STD_ZVAL(z_tmp);
- *z_tmp = **data;
+ *z_tmp = **z_ele;
zval_copy_ctor(z_tmp);
convert_to_string(z_tmp);
key = Z_STRVAL_P(z_tmp);
key_len = Z_STRLEN_P(z_tmp);
}
- old_cmd = NULL;
- if(*cmd) {
- old_cmd = cmd;
- }
- key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
- cmd_len = redis_cmd_format(&cmd, "%s$%d" _NL "%s" _NL
- , cmd, cmd_len
- , key_len, key, key_len);
- if(key_free) efree(key);
+ // Apply key prefix if necissary
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
- if(old_cmd) {
- efree(old_cmd);
- }
- elements++;
+ // Append this key to our command
+ redis_cmd_append_sstr(&cmd, key, key_len);
+
+ // Free our key if it was prefixed
+ if(key_free) efree(key);
+
+ // Free oour temporary ZVAL if we converted from a non-string
if(z_tmp) {
zval_dtor(z_tmp);
efree(z_tmp);
+ z_tmp = NULL;
}
}
- old_cmd = cmd;
- cmd_len = redis_cmd_format(&cmd, "*%d" _NL "$4" _NL "MGET" _NL "%s", elements, cmd, cmd_len);
- efree(old_cmd);
-
- REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+ // Kick off our command
+ REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
IF_ATOMIC() {
- if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
- redis_sock, NULL, NULL) < 0) {
- RETURN_FALSE;
- }
+ 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 boolean Redis::exists(string key)
*/
@@ -2360,7 +2426,6 @@ PHP_METHOD(Redis, sMembers)
}
/* }}} */
-
PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len,
int min_argc, RedisSock **out_sock, int has_timeout, int all_keys, int can_serialize)
{
@@ -4290,187 +4355,167 @@ PHP_METHOD(Redis, zIncrBy)
generic_incrby_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZINCRBY", sizeof("ZINCRBY")-1);
}
/* }}} */
-PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) {
-
- zval *object, *keys_array, *weights_array = NULL, **data;
- HashTable *arr_weights_hash = NULL, *arr_keys_hash;
- int key_output_len, array_weights_count, array_keys_count, operation_len = 0;
- char *key_output, *operation;
- RedisSock *redis_sock;
- HashPosition pointer;
- char *cmd = "";
- char *old_cmd;
- int cmd_len, cmd_elements;
- int free_key_output;
-
- if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|as",
- &object, redis_ce,
- &key_output, &key_output_len, &keys_array, &weights_array, &operation, &operation_len) == FAILURE) {
- RETURN_FALSE;
- }
+PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) {
+ zval *object, *z_keys, *z_weights = NULL, **z_data;
+ HashTable *ht_keys, *ht_weights = NULL;
+ RedisSock *redis_sock;
+ smart_str cmd = {0};
+ HashPosition ptr;
+ char *store_key, *agg_op = NULL;
+ int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count;
+
+ // Grab our parameters
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
+ &object, redis_ce, &store_key, &store_key_len,
+ &z_keys, &z_weights, &agg_op, &agg_op_len) == FAILURE)
+ {
+ RETURN_FALSE;
+ }
+ // We'll need our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
- RETURN_FALSE;
+ RETURN_FALSE;
}
- arr_keys_hash = Z_ARRVAL_P(keys_array);
- array_keys_count = zend_hash_num_elements(arr_keys_hash);
+ // Grab our keys argument as an array
+ ht_keys = Z_ARRVAL_P(z_keys);
- if (array_keys_count == 0) {
+ // Nothing to do if there aren't any keys
+ if((keys_count = zend_hash_num_elements(ht_keys)) == 0) {
RETURN_FALSE;
+ } else {
+ // Increment our overall argument count
+ cmd_arg_count += keys_count;
}
- if(weights_array != NULL) {
- arr_weights_hash = Z_ARRVAL_P(weights_array);
- array_weights_count = zend_hash_num_elements(arr_weights_hash);
- if (array_weights_count == 0) {
- RETURN_FALSE;
- }
- if((array_weights_count != 0) && (array_weights_count != array_keys_count)) {
- RETURN_FALSE;
- }
+ // Grab and validate our weights array
+ if(z_weights != NULL) {
+ ht_weights = Z_ARRVAL_P(z_weights);
- }
+ // This command is invalid if the weights array isn't the same size
+ // as our keys array.
+ if(zend_hash_num_elements(ht_weights) != keys_count) {
+ RETURN_FALSE;
+ }
- free_key_output = redis_key_prefix(redis_sock, &key_output, &key_output_len TSRMLS_CC);
- cmd_elements = 3;
- cmd_len = redis_cmd_format(&cmd,
- "$%d" _NL /* command_len */
- "%s" _NL /* command */
+ // Increment our overall argument count by the number of keys
+ // plus one, for the "WEIGHTS" argument itself
+ cmd_arg_count += keys_count + 1;
+ }
- "$%d" _NL /* key_output_len */
- "%s" _NL /* key_output */
+ // AGGREGATE option
+ if(agg_op_len != 0) {
+ // Verify our aggregation option
+ if(strncasecmp(agg_op, "SUM", sizeof("SUM")) &&
+ strncasecmp(agg_op, "MIN", sizeof("MIN")) &&
+ strncasecmp(agg_op, "MAX", sizeof("MAX")))
+ {
+ RETURN_FALSE;
+ }
- "$%d" _NL
- "%d" _NL /* array_keys_count */
+ // Two more arguments: "AGGREGATE" and agg_op
+ cmd_arg_count += 2;
+ }
- , command_len, command, command_len
- , key_output_len, key_output, key_output_len
- , integer_length(array_keys_count), array_keys_count);
- if(free_key_output) efree(key_output);
+ // Command header
+ redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len);
- /* keys */
- for (zend_hash_internal_pointer_reset_ex(arr_keys_hash, &pointer);
- zend_hash_get_current_data_ex(arr_keys_hash, (void**) &data,
- &pointer) == SUCCESS;
- zend_hash_move_forward_ex(arr_keys_hash, &pointer)) {
+ // Prefix our key if necessary and add the output key
+ int key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
+ redis_cmd_append_sstr(&cmd, store_key, store_key_len);
+ if(key_free) efree(store_key);
- if (Z_TYPE_PP(data) == IS_STRING) {
- char *old_cmd = NULL;
- char *data_str;
- int data_len;
- int free_data;
-
- if(*cmd) {
- old_cmd = cmd;
- }
- data_str = Z_STRVAL_PP(data);
- data_len = Z_STRLEN_PP(data);
-
- free_data = redis_key_prefix(redis_sock, &data_str, &data_len TSRMLS_CC);
- cmd_len = redis_cmd_format(&cmd,
- "%s" /* cmd */
- "$%d" _NL
- "%s" _NL
- , cmd, cmd_len
- , data_len, data_str, data_len);
- cmd_elements++;
- if(free_data) efree(data_str);
- if(old_cmd) {
- efree(old_cmd);
- }
+ // Number of input keys argument
+ redis_cmd_append_sstr_int(&cmd, keys_count);
+
+ // Process input keys
+ for(zend_hash_internal_pointer_reset_ex(ht_keys, &ptr);
+ zend_hash_get_current_data_ex(ht_keys, (void**)&z_data, &ptr)==SUCCESS;
+ zend_hash_move_forward_ex(ht_keys, &ptr))
+ {
+ char *key;
+ int key_free, key_len;
+ zval *z_tmp = NULL;
+
+ if(Z_TYPE_PP(z_data) == IS_STRING) {
+ key = Z_STRVAL_PP(z_data);
+ key_len = Z_STRLEN_PP(z_data);
+ } else {
+ MAKE_STD_ZVAL(z_tmp);
+ *z_tmp = **z_data;
+ convert_to_string(z_tmp);
+
+ key = Z_STRVAL_P(z_tmp);
+ key_len = Z_STRLEN_P(z_tmp);
}
- }
- /* weight */
- if(weights_array != NULL) {
- cmd_len = redis_cmd_format(&cmd,
- "%s" /* cmd */
- "$7" _NL
- "WEIGHTS" _NL
- , cmd, cmd_len);
- cmd_elements++;
-
- for (zend_hash_internal_pointer_reset_ex(arr_weights_hash, &pointer);
- zend_hash_get_current_data_ex(arr_weights_hash, (void**) &data, &pointer) == SUCCESS;
- zend_hash_move_forward_ex(arr_weights_hash, &pointer)) {
-
- // Ignore non numeric arguments, unless they're the special Redis numbers
- // "inf" ,"-inf", and "+inf" which can be passed as weights
- if (Z_TYPE_PP(data) != IS_LONG && Z_TYPE_PP(data) != IS_DOUBLE &&
- strncasecmp(Z_STRVAL_PP(data), "inf", sizeof("inf")) != 0 &&
- strncasecmp(Z_STRVAL_PP(data), "-inf", sizeof("-inf")) != 0 &&
- strncasecmp(Z_STRVAL_PP(data), "+inf", sizeof("+inf")) != 0)
- {
- continue;
- }
+ // Apply key prefix if necessary
+ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
- old_cmd = NULL;
- if(*cmd) {
- old_cmd = cmd;
- }
+ // Append this input set
+ redis_cmd_append_sstr(&cmd, key, key_len);
- if(Z_TYPE_PP(data) == IS_LONG) {
- cmd_len = redis_cmd_format(&cmd,
- "%s" /* cmd */
- "$%d" _NL /* data_len */
- "%d" _NL /* data */
- , cmd, cmd_len
- , integer_length(Z_LVAL_PP(data)), Z_LVAL_PP(data));
-
- } else if(Z_TYPE_PP(data) == IS_DOUBLE) {
- cmd_len = redis_cmd_format(&cmd,
- "%s" /* cmd */
- "$%f" _NL /* data, including size */
- , cmd, cmd_len
- , Z_DVAL_PP(data));
- } else if(Z_TYPE_PP(data) == IS_STRING) {
- cmd_len = redis_cmd_format(&cmd,
- "%s" /* cmd */
- "$%d" _NL /* data len */
- "%s" _NL /* data */
- , cmd, cmd_len, Z_STRLEN_PP(data),
- Z_STRVAL_PP(data), Z_STRLEN_PP(data));
- }
+ // Free our key if it was prefixed
+ if(key_free) efree(key);
- // keep track of elements added
- cmd_elements++;
- if(old_cmd) {
- efree(old_cmd);
- }
- }
- }
+ // Free our temporary z_val if it was converted
+ if(z_tmp) {
+ zval_dtor(z_tmp);
+ efree(z_tmp);
+ z_tmp = NULL;
+ }
+ }
- if(operation_len != 0) {
- char *old_cmd = NULL;
- old_cmd = cmd;
- cmd_len = redis_cmd_format(&cmd,
- "%s" /* cmd */
- "$9" _NL
- "AGGREGATE" _NL
- "$%d" _NL
- "%s" _NL
- , cmd, cmd_len
- , operation_len, operation, operation_len);
- cmd_elements += 2;
- efree(old_cmd);
- }
+ // Weights
+ if(ht_weights != NULL) {
+ // Append "WEIGHTS" argument
+ redis_cmd_append_sstr(&cmd, "WEIGHTS", sizeof("WEIGHTS") - 1);
+
+ // Process weights
+ for(zend_hash_internal_pointer_reset_ex(ht_weights, &ptr);
+ zend_hash_get_current_data_ex(ht_weights, (void**)&z_data, &ptr)==SUCCESS;
+ zend_hash_move_forward_ex(ht_weights, &ptr))
+ {
+ // Ignore non numeric arguments, unless they're special Redis numbers
+ if (Z_TYPE_PP(z_data) != IS_LONG && Z_TYPE_PP(z_data) != IS_DOUBLE &&
+ strncasecmp(Z_STRVAL_PP(z_data), "inf", sizeof("inf")) != 0 &&
+ strncasecmp(Z_STRVAL_PP(z_data), "-inf", sizeof("-inf")) != 0 &&
+ strncasecmp(Z_STRVAL_PP(z_data), "+inf", sizeof("+inf")) != 0)
+ {
+ // We should abort if we have an invalid weight, rather than pass
+ // a different number of weights than the user is expecting
+ efree(cmd.c);
+ RETURN_FALSE;
+ }
- old_cmd = cmd;
- cmd_len = redis_cmd_format(&cmd,
- "*%d" _NL
- "%s"
- , cmd_elements
- , cmd, cmd_len);
- efree(old_cmd);
+ // Append the weight based on the input type
+ switch(Z_TYPE_PP(z_data)) {
+ case IS_LONG:
+ redis_cmd_append_sstr_long(&cmd, Z_LVAL_PP(z_data));
+ break;
+ case IS_DOUBLE:
+ redis_cmd_append_sstr_dbl(&cmd, Z_DVAL_PP(z_data));
+ break;
+ case IS_STRING:
+ redis_cmd_append_sstr(&cmd, Z_STRVAL_PP(z_data), Z_STRLEN_PP(z_data));
+ break;
+ }
+ }
+ }
- 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);
+ // Aggregation options, if we have them
+ if(agg_op_len != 0) {
+ redis_cmd_append_sstr(&cmd, "AGGREGATE", sizeof("AGGREGATE") - 1);
+ redis_cmd_append_sstr(&cmd, agg_op, agg_op_len);
+ }
+ // Kick off our request
+ 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);
}
/* zInter */
@@ -5885,6 +5930,56 @@ PHP_METHOD(Redis, config)
/* }}} */
+/* {{{ proto boolean Redis::slowlog(string arg, [int option])
+ */
+PHP_METHOD(Redis, slowlog) {
+ zval *object;
+ RedisSock *redis_sock;
+ char *arg, *cmd;
+ int arg_len, cmd_len;
+ long option;
+ enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode;
+
+ // Make sure we can get parameters
+ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
+ &object, redis_ce, &arg, &arg_len, &option) == FAILURE)
+ {
+ RETURN_FALSE;
+ }
+
+ // Figure out what kind of slowlog command we're executing
+ if(!strncasecmp(arg, "GET", 3)) {
+ mode = SLOWLOG_GET;
+ } else if(!strncasecmp(arg, "LEN", 3)) {
+ mode = SLOWLOG_LEN;
+ } else if(!strncasecmp(arg, "RESET", 5)) {
+ mode = SLOWLOG_RESET;
+ } else {
+ // This command is not valid
+ RETURN_FALSE;
+ }
+
+ // Make sure we can grab our redis socket
+ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+ RETURN_FALSE;
+ }
+
+ // Create our command. For everything except SLOWLOG GET (with an arg) it's just two parts
+ if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) {
+ cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, arg_len, option);
+ } else {
+ cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s", arg, arg_len);
+ }
+
+ // Kick off our command
+ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+ IF_ATOMIC() {
+ if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
+ RETURN_FALSE;
+ }
+ }
+ REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+}
// Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
PHPAPI int
@@ -6511,9 +6606,6 @@ PHP_METHOD(Redis, client) {
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
}
- // Handle CLIENT LIST specifically
- int is_list = !strncasecmp(opt, "list", 4);
-
// Execute our queue command
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
diff --git a/redis_array.c b/redis_array.c
index 31f77278..ecc158ea 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -194,9 +194,10 @@ PHP_METHOD(RedisArray, __construct)
zval *z0, *z_fun = NULL, *z_dist = NULL, **zpData, *z_opts = NULL;
int id;
RedisArray *ra = NULL;
- zend_bool b_index = 0, b_autorehash = 0;
+ zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
HashTable *hPrev = NULL, *hOpts = NULL;
long l_retry_interval = 0;
+ zend_bool b_lazy_connect = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) {
RETURN_FALSE;
@@ -238,9 +239,14 @@ PHP_METHOD(RedisArray, __construct)
b_autorehash = Z_BVAL_PP(zpData);
}
+ /* pconnect */
+ if(FAILURE != zend_hash_find(hOpts, "pconnect", sizeof("pconnect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) {
+ b_pconnect = Z_BVAL_PP(zpData);
+ }
+
/* extract retry_interval option. */
- zval **z_retry_interval_pp;
- if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) {
+ zval **z_retry_interval_pp;
+ if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) {
if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) {
if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) {
l_retry_interval = Z_LVAL_PP(z_retry_interval_pp);
@@ -250,6 +256,11 @@ PHP_METHOD(RedisArray, __construct)
}
}
}
+
+ /* extract lazy connect option. */
+ if(FAILURE != zend_hash_find(hOpts, "lazy_connect", sizeof("lazy_connect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) {
+ b_lazy_connect = Z_BVAL_PP(zpData);
+ }
}
/* extract either name of list of hosts from z0 */
@@ -259,7 +270,7 @@ PHP_METHOD(RedisArray, __construct)
break;
case IS_ARRAY:
- ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC);
+ ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
break;
default:
@@ -826,18 +837,34 @@ PHP_METHOD(RedisArray, mget)
for (i = 0, zend_hash_internal_pointer_reset_ex(h_keys, &pointer);
zend_hash_get_current_data_ex(h_keys, (void**) &data,
&pointer) == SUCCESS;
- zend_hash_move_forward_ex(h_keys, &pointer), ++i) {
-
- if (Z_TYPE_PP(data) != IS_STRING) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be string.");
- efree(argv);
- efree(pos);
- efree(redis_instances);
- efree(argc_each);
- RETURN_FALSE;
- }
+ zend_hash_move_forward_ex(h_keys, &pointer), ++i)
+ {
+ /* If we need to represent a long key as a string */
+ unsigned int key_len;
+ char kbuf[40], *key_lookup;
+
+ /* phpredis proper can only use string or long keys, so restrict to that here */
+ if(Z_TYPE_PP(data) != IS_STRING && Z_TYPE_PP(data) != IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs");
+ efree(argv);
+ efree(pos);
+ efree(redis_instances);
+ efree(argc_each);
+ RETURN_FALSE;
+ }
+
+ /* Convert to a string for hash lookup if it isn't one */
+ if(Z_TYPE_PP(data) == IS_STRING) {
+ key_len = Z_STRLEN_PP(data);
+ key_lookup = Z_STRVAL_PP(data);
+ } else {
+ key_len = snprintf(kbuf, sizeof(kbuf), "%ld", Z_LVAL_PP(data));
+ key_lookup = (char*)kbuf;
+ }
+
+ /* Find our node */
+ redis_instances[i] = ra_find_node(ra, key_lookup, key_len, &pos[i] TSRMLS_CC);
- redis_instances[i] = ra_find_node(ra, Z_STRVAL_PP(data), Z_STRLEN_PP(data), &pos[i] TSRMLS_CC);
argc_each[pos[i]]++; /* count number of keys per node */
argv[i] = *data;
}
@@ -930,8 +957,8 @@ PHP_METHOD(RedisArray, mset)
int *pos, argc, *argc_each;
HashTable *h_keys;
zval **redis_instances, *redis_inst, **argv;
- char *key, **keys;
- unsigned int key_len;
+ char *key, **keys, **key_free, kbuf[40];
+ unsigned int key_len, free_idx = 0;
int type, *key_lens;
unsigned long idx;
@@ -953,31 +980,43 @@ PHP_METHOD(RedisArray, mset)
argv = emalloc(argc * sizeof(zval*));
pos = emalloc(argc * sizeof(int));
keys = emalloc(argc * sizeof(char*));
- key_lens = emalloc(argc * sizeof(int));
+ key_lens = emalloc(argc * sizeof(int));
redis_instances = emalloc(argc * sizeof(zval*));
memset(redis_instances, 0, argc * sizeof(zval*));
+ /* Allocate an array holding the indexes of any keys that need freeing */
+ key_free = emalloc(argc * sizeof(char*));
+
argc_each = emalloc(ra->count * sizeof(int));
memset(argc_each, 0, ra->count * sizeof(int));
/* associate each key to a redis node */
for(i = 0, zend_hash_internal_pointer_reset(h_keys);
zend_hash_has_more_elements(h_keys) == SUCCESS;
- zend_hash_move_forward(h_keys), i++) {
-
- type = zend_hash_get_current_key_ex(h_keys, &key, &key_len, &idx, 0, NULL);
- if(type != HASH_KEY_IS_STRING) { /* ignore non-string keys */
- continue;
- }
- if(zend_hash_get_current_data(h_keys, (void**)&data) == FAILURE) {
- continue;
- }
-
- redis_instances[i] = ra_find_node(ra, key, (int)key_len - 1, &pos[i] TSRMLS_CC); /* -1 because of PHP assoc keys which count \0... */
+ zend_hash_move_forward(h_keys), i++)
+ {
+ /* We have to skip the element if we can't get the array value */
+ if(zend_hash_get_current_data(h_keys, (void**)&data) == FAILURE) {
+ continue;
+ }
+
+ /* Grab our key */
+ type = zend_hash_get_current_key_ex(h_keys, &key, &key_len, &idx, 0, NULL);
+
+ /* If the key isn't a string, make a string representation of it */
+ if(type != HASH_KEY_IS_STRING) {
+ key_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx);
+ key = estrndup(kbuf, key_len);
+ key_free[free_idx++]=key;
+ } else {
+ key_len--; /* We don't want the null terminator */
+ }
+
+ redis_instances[i] = ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC);
argc_each[pos[i]]++; /* count number of keys per node */
argv[i] = *data;
keys[i] = key;
- key_lens[i] = (int)key_len - 1;
+ key_lens[i] = (int)key_len;
}
@@ -1030,8 +1069,14 @@ PHP_METHOD(RedisArray, mset)
zval_ptr_dtor(&z_argarray);
}
+ /* Free any keys that we needed to allocate memory for, because they weren't strings */
+ for(i=0; i<free_idx; i++) {
+ efree(key_free[i]);
+ }
+
/* cleanup */
efree(keys);
+ efree(key_free);
efree(key_lens);
efree(argv);
efree(pos);
diff --git a/redis_array.h b/redis_array.h
index e61b5977..652b5aef 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -41,6 +41,7 @@ typedef struct RedisArray_ {
zval *z_multi_exec; /* Redis instance to be used in multi-exec */
zend_bool index; /* use per-node index */
zend_bool auto_rehash; /* migrate keys on read operations */
+ zend_bool pconnect; /* should we use pconnect */
zval *z_fun; /* key extractor, callable */
zval *z_dist; /* key distributor, callable */
zval *z_pure_cmds; /* hash table */
diff --git a/redis_array_impl.c b/redis_array_impl.c
index bafcbd49..c3a1f365 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -30,7 +30,7 @@ extern int le_redis_sock;
extern zend_class_entry *redis_ce;
RedisArray*
-ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC)
+ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC)
{
int i, host_len, id;
int count = zend_hash_num_elements(hosts);
@@ -59,7 +59,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC)
if((p = strchr(host, ':'))) { /* found port */
host_len = p - host;
port = (short)atoi(p+1);
- }
+ } else if(strchr(host,'/') != NULL) { /* unix socket */
+ port = -1;
+ }
/* create Redis object */
MAKE_STD_ZVAL(ra->redis[i]);
@@ -68,10 +70,13 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC)
call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC);
/* create socket */
- redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval); /* TODO: persistence? */
+ redis_sock = redis_sock_create(host, host_len, port, 0, ra->pconnect, NULL, retry_interval, b_lazy_connect);
- /* connect */
- redis_sock_server_open(redis_sock, 1 TSRMLS_CC);
+ if (!b_lazy_connect)
+ {
+ /* connect */
+ redis_sock_server_open(redis_sock, 1 TSRMLS_CC);
+ }
/* attach */
#if PHP_VERSION_ID >= 50400
@@ -160,10 +165,13 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
zval *z_params_index;
zval *z_params_autorehash;
zval *z_params_retry_interval;
+ zval *z_params_pconnect;
+ zval *z_params_lazy_connect;
RedisArray *ra = NULL;
- zend_bool b_index = 0, b_autorehash = 0;
+ zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
long l_retry_interval = 0;
+ zend_bool b_lazy_connect = 0;
HashTable *hHosts = NULL, *hPrev = NULL;
/* find entry */
@@ -241,8 +249,27 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
}
}
+ /* find pconnect option */
+ MAKE_STD_ZVAL(z_params_pconnect);
+ array_init(z_params_pconnect);
+ sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.pconnect")), z_params_pconnect TSRMLS_CC);
+ if (zend_hash_find(Z_ARRVAL_P(z_params_pconnect), name, strlen(name) + 1, (void**) &z_data_pp) != FAILURE) {
+ if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) {
+ b_pconnect = 1;
+ }
+ }
+ /* find retry interval option */
+ MAKE_STD_ZVAL(z_params_lazy_connect);
+ array_init(z_params_lazy_connect);
+ sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.lazyconnect")), z_params_lazy_connect TSRMLS_CC);
+ if (zend_hash_find(Z_ARRVAL_P(z_params_lazy_connect), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) {
+ if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) {
+ b_lazy_connect = 1;
+ }
+ }
+
/* create RedisArray object */
- ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC);
+ ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
ra->auto_rehash = b_autorehash;
/* cleanup */
@@ -258,12 +285,16 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
efree(z_params_autorehash);
zval_dtor(z_params_retry_interval);
efree(z_params_retry_interval);
+ zval_dtor(z_params_pconnect);
+ efree(z_params_pconnect);
+ zval_dtor(z_params_lazy_connect);
+ efree(z_params_lazy_connect);
return ra;
}
RedisArray *
-ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC) {
+ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) {
int count = zend_hash_num_elements(hosts);
@@ -281,10 +312,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
/* init array data structures */
ra_init_function_table(ra);
- if(NULL == ra_load_hosts(ra, hosts, retry_interval TSRMLS_CC)) {
+ if(NULL == ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC)) {
return NULL;
}
- ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval TSRMLS_CC) : NULL;
+ ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect TSRMLS_CC) : NULL;
/* copy function if provided */
if(z_fun) {
@@ -398,34 +429,34 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML
zval *
ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) {
- uint32_t hash;
- char *out;
- int pos, out_len;
-
- /* extract relevant part of the key */
- out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC);
- if(!out)
- return NULL;
-
- if(ra->z_dist) {
- if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) {
- return NULL;
- }
- }
- else {
- /* hash */
- hash = rcrc32(out, out_len);
- efree(out);
-
- /* get position on ring */
- uint64_t h64 = hash;
- h64 *= ra->count;
- h64 /= 0xffffffff;
- pos = (int)h64;
- }
- if(out_pos) *out_pos = pos;
-
- return ra->redis[pos];
+ uint32_t hash;
+ char *out;
+ int pos, out_len;
+
+ /* extract relevant part of the key */
+ out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC);
+ if(!out)
+ return NULL;
+
+ if(ra->z_dist) {
+ if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) {
+ return NULL;
+ }
+ }
+ else {
+ /* hash */
+ hash = rcrc32(out, out_len);
+ efree(out);
+
+ /* get position on ring */
+ uint64_t h64 = hash;
+ h64 *= ra->count;
+ h64 /= 0xffffffff;
+ pos = (int)h64;
+ }
+ if(out_pos) *out_pos = pos;
+
+ return ra->redis[pos];
}
zval *
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 8dd5201b..10f8512a 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -5,9 +5,9 @@
#include "common.h"
#include "redis_array.h"
-RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC);
+RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
RedisArray *ra_load_array(const char *name TSRMLS_DC);
-RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC);
+RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC);
zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC);
void ra_init_function_table(RedisArray *ra);
diff --git a/redis_session.c b/redis_session.c
index 4e633fc9..591f07b8 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -278,9 +278,9 @@ PS_OPEN_FUNC(redis)
RedisSock *redis_sock;
if(url->host) {
- redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval);
+ redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0);
} else { /* unix */
- redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval);
+ redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval, 0);
}
redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC);
diff --git a/rpm/php-redis.spec b/rpm/php-redis.spec
index 714854bc..4f04fb18 100644
--- a/rpm/php-redis.spec
+++ b/rpm/php-redis.spec
@@ -3,7 +3,7 @@
%global php_version %(php-config --version 2>/dev/null || echo 0)
Name: php-redis
-Version: 2.2.3
+Version: 2.2.4
Release: 1%{?dist}
Summary: The phpredis extension provides an API for communicating with the Redis key-value store.
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 6091a94d..d19f9e0b 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -138,84 +138,140 @@ class Redis_Test extends TestSuite
public function testSet()
{
- $this->assertEquals(TRUE, $this->redis->set('key', 'nil'));
- $this->assertEquals('nil', $this->redis->get('key'));
+ $this->assertEquals(TRUE, $this->redis->set('key', 'nil'));
+ $this->assertEquals('nil', $this->redis->get('key'));
- $this->assertEquals(TRUE, $this->redis->set('key', 'val'));
+ $this->assertEquals(TRUE, $this->redis->set('key', 'val'));
- $this->assertEquals('val', $this->redis->get('key'));
- $this->assertEquals('val', $this->redis->get('key'));
- $this->redis->delete('keyNotExist');
- $this->assertEquals(FALSE, $this->redis->get('keyNotExist'));
+ $this->assertEquals('val', $this->redis->get('key'));
+ $this->assertEquals('val', $this->redis->get('key'));
+ $this->redis->delete('keyNotExist');
+ $this->assertEquals(FALSE, $this->redis->get('keyNotExist'));
- $this->redis->set('key2', 'val');
- $this->assertEquals('val', $this->redis->get('key2'));
+ $this->redis->set('key2', 'val');
+ $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'));
+ $value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
+
+ $this->redis->set('key2', $value);
+ $this->assertEquals($value, $this->redis->get('key2'));
+ $this->assertEquals($value, $this->redis->get('key2'));
- $this->redis->delete('key');
- $this->redis->delete('key2');
+ $this->redis->delete('key');
+ $this->redis->delete('key2');
- $i = 66000;
- $value2 = 'X';
- while($i--) {
- $value2 .= 'A';
- }
- $value2 .= 'X';
+ $i = 66000;
+ $value2 = 'X';
+ while($i--) {
+ $value2 .= 'A';
+ }
+ $value2 .= 'X';
- $this->redis->set('key', $value2);
+ $this->redis->set('key', $value2);
$this->assertEquals($value2, $this->redis->get('key'));
- $this->redis->delete('key');
- $this->assertEquals(False, $this->redis->get('key'));
+ $this->redis->delete('key');
+ $this->assertEquals(False, $this->redis->get('key'));
- $data = gzcompress('42');
+ $data = gzcompress('42');
$this->assertEquals(True, $this->redis->set('key', $data));
- $this->assertEquals('42', gzuncompress($this->redis->get('key')));
+ $this->assertEquals('42', gzuncompress($this->redis->get('key')));
- $this->redis->delete('key');
- $data = gzcompress('value1');
+ $this->redis->delete('key');
+ $data = gzcompress('value1');
$this->assertEquals(True, $this->redis->set('key', $data));
- $this->assertEquals('value1', gzuncompress($this->redis->get('key')));
-
- $this->redis->delete('key');
- $this->assertEquals(TRUE, $this->redis->set('key', 0));
- $this->assertEquals('0', $this->redis->get('key'));
- $this->assertEquals(TRUE, $this->redis->set('key', 1));
- $this->assertEquals('1', $this->redis->get('key'));
- $this->assertEquals(TRUE, $this->redis->set('key', 0.1));
- $this->assertEquals('0.1', $this->redis->get('key'));
- $this->assertEquals(TRUE, $this->redis->set('key', '0.1'));
- $this->assertEquals('0.1', $this->redis->get('key'));
- $this->assertEquals(TRUE, $this->redis->set('key', TRUE));
- $this->assertEquals('1', $this->redis->get('key'));
-
- $this->assertEquals(True, $this->redis->set('key', ''));
- $this->assertEquals('', $this->redis->get('key'));
- $this->assertEquals(True, $this->redis->set('key', NULL));
- $this->assertEquals('', $this->redis->get('key'));
+ $this->assertEquals('value1', gzuncompress($this->redis->get('key')));
+
+ $this->redis->delete('key');
+ $this->assertEquals(TRUE, $this->redis->set('key', 0));
+ $this->assertEquals('0', $this->redis->get('key'));
+ $this->assertEquals(TRUE, $this->redis->set('key', 1));
+ $this->assertEquals('1', $this->redis->get('key'));
+ $this->assertEquals(TRUE, $this->redis->set('key', 0.1));
+ $this->assertEquals('0.1', $this->redis->get('key'));
+ $this->assertEquals(TRUE, $this->redis->set('key', '0.1'));
+ $this->assertEquals('0.1', $this->redis->get('key'));
+ $this->assertEquals(TRUE, $this->redis->set('key', TRUE));
+ $this->assertEquals('1', $this->redis->get('key'));
+
+ $this->assertEquals(True, $this->redis->set('key', ''));
+ $this->assertEquals('', $this->redis->get('key'));
+ $this->assertEquals(True, $this->redis->set('key', NULL));
+ $this->assertEquals('', $this->redis->get('key'));
$this->assertEquals(True, $this->redis->set('key', gzcompress('42')));
$this->assertEquals('42', gzuncompress($this->redis->get('key')));
+ }
+ /* Extended SET options for Redis >= 2.6.12 */
+ public function testExtendedSet() {
+ // Skip the test if we don't have a new enough version of Redis
+ if(version_compare($this->version, '2.6.12', 'lt')) {
+ $this->markTestSkipped();
+ return;
+ }
+
+ /* Legacy SETEX redirection */
+ $this->redis->del('foo');
+ $this->assertTrue($this->redis->set('foo','bar', 20));
+ $this->assertEquals($this->redis->get('foo'), 'bar');
+ $this->assertEquals($this->redis->ttl('foo'), 20);
+
+ /* Invalid third arguments */
+ $this->assertFalse($this->redis->set('foo','bar','baz'));
+ $this->assertFalse($this->redis->set('foo','bar',new StdClass()));
+
+ /* Set if not exist */
+ $this->redis->del('foo');
+ $this->assertTrue($this->redis->set('foo','bar',Array('nx')));
+ $this->assertEquals($this->redis->get('foo'), 'bar');
+ $this->assertFalse($this->redis->set('foo','bar',Array('nx')));
+
+ /* Set if exists */
+ $this->assertTrue($this->redis->set('foo','bar',Array('xx')));
+ $this->assertEquals($this->redis->get('foo'), 'bar');
+ $this->redis->del('foo');
+ $this->assertFalse($this->redis->set('foo','bar',Array('xx')));
+
+ /* Set with a TTL */
+ $this->assertTrue($this->redis->set('foo','bar',Array('ex'=>100)));
+ $this->assertEquals($this->redis->ttl('foo'), 100);
+
+ /* Set with a PTTL */
+ $this->assertTrue($this->redis->set('foo','bar',Array('px'=>100000)));
+ $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000);
+
+ /* Set if exists, with a TTL */
+ $this->assertTrue($this->redis->set('foo','bar',Array('xx','ex'=>105)));
+ $this->assertEquals($this->redis->ttl('foo'), 105);
+ $this->assertEquals($this->redis->get('foo'), 'bar');
+
+ /* Set if not exists, with a TTL */
+ $this->redis->del('foo');
+ $this->assertTrue($this->redis->set('foo','bar', Array('nx', 'ex'=>110)));
+ $this->assertEquals($this->redis->ttl('foo'), 110);
+ $this->assertEquals($this->redis->get('foo'), 'bar');
+ $this->assertFalse($this->redis->set('foo','bar', Array('nx', 'ex'=>110)));
+
+ /* Throw some nonsense into the array, and check that the TTL came through */
+ $this->redis->del('foo');
+ $this->assertTrue($this->redis->set('foo','barbaz', Array('not-valid','nx','invalid','ex'=>200)));
+ $this->assertEquals($this->redis->ttl('foo'), 200);
+ $this->assertEquals($this->redis->get('foo'), 'barbaz');
}
- public function testGetSet() {
- $this->redis->delete('key');
- $this->assertTrue($this->redis->getSet('key', '42') === FALSE);
- $this->assertTrue($this->redis->getSet('key', '123') === '42');
- $this->assertTrue($this->redis->getSet('key', '123') === '123');
+ public function testGetSet() {
+ $this->redis->delete('key');
+ $this->assertTrue($this->redis->getSet('key', '42') === FALSE);
+ $this->assertTrue($this->redis->getSet('key', '123') === '42');
+ $this->assertTrue($this->redis->getSet('key', '123') === '123');
}
public function testRandomKey() {
-
for($i = 0; $i < 1000; $i++) {
$k = $this->redis->randomKey();
- $this->assertTrue($this->redis->exists($k));
- }
+ $this->assertTrue($this->redis->exists($k));
+ }
}
public function testRename() {
@@ -1643,9 +1699,11 @@ class Redis_Test extends TestSuite
$this->redis->del('x'); $this->redis->set('x', 'bar');
$this->assertEquals($this->redis->ttl('x'), -1);
- // A key that doesn't exist
- $this->redis->del('x');
- $this->assertEquals($this->redis->ttl('x'), -2);
+ // A key that doesn't exist (> 2.8 will return -2)
+ if(version_compare($this->version, "2.8.0", "gte")) {
+ $this->redis->del('x');
+ $this->assertEquals($this->redis->ttl('x'), -2);
+ }
}
public function testPersist() {
@@ -1684,6 +1742,16 @@ class Redis_Test extends TestSuite
$this->assertTrue($this->redis->client('kill', $str_addr));
}
+ public function testSlowlog() {
+ // We don't really know what's going to be in the slowlog, but make sure
+ // the command returns proper types when called in various ways
+ $this->assertTrue(is_array($this->redis->slowlog('get')));
+ $this->assertTrue(is_array($this->redis->slowlog('get', 10)));
+ $this->assertTrue(is_int($this->redis->slowlog('len')));
+ $this->assertTrue($this->redis->slowlog('reset'));
+ $this->assertFalse($this->redis->slowlog('notvalid'));
+ }
+
public function testinfo() {
$info = $this->redis->info();
@@ -1971,6 +2039,20 @@ class Redis_Test extends TestSuite
$this->redis->delete('key2');
$this->redis->delete('key3');
+ //test zUnion with weights and aggegration function
+ $this->redis->zadd('key1', 1, 'duplicate');
+ $this->redis->zadd('key2', 2, 'duplicate');
+ $this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN');
+ $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0);
+ $this->redis->delete('keyU');
+
+ //now test zUnion *without* weights but with aggregrate function
+ $this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN');
+ $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0);
+ $this->redis->delete('keyU', 'key1', 'key2');
+
+
+
// test integer and float weights (GitHub issue #109).
$this->redis->del('key1', 'key2', 'key3');
@@ -2087,6 +2169,10 @@ class Redis_Test extends TestSuite
$this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max'));
$this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1));
+ $this->redis->delete('keyI');
+ $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max'));
+ $this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7));
+
// zrank, zrevrank
$this->redis->delete('z');
$this->redis->zadd('z', 1, 'one');