diff --git a/cluster_library.c b/cluster_library.c index 98ba9c2..3720f72 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -634,8 +634,12 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, zend_llist_init(&node->slots, sizeof(redisSlotRange), NULL, 0); // Attach socket - node->sock = redis_sock_create(host, host_len, port, c->timeout, - c->read_timeout, c->persistent, NULL, 0); + node->sock = redis_sock_create(host, host_len, port, + c->flags->timeout, c->flags->read_timeout, + c->flags->persistent, NULL, 0); + + /* Stream context */ + node->sock->stream_ctx = c->flags->stream_ctx; redis_sock_set_auth(node->sock, c->flags->user, c->flags->pass); @@ -818,12 +822,12 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, /* Initialize flags and settings */ c->flags = ecalloc(1, sizeof(RedisSock)); + c->flags->timeout = timeout; + c->flags->read_timeout = read_timeout; + c->flags->persistent = persistent; c->subscribed_slot = -1; c->clusterdown = 0; - c->timeout = timeout; - c->read_timeout = read_timeout; c->failover = failover; - c->persistent = persistent; c->err = NULL; /* Set up our waitms based on timeout */ @@ -993,9 +997,12 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) { /* Create socket */ sock = redis_sock_create(ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), cm->host.port, - c->timeout, c->read_timeout, c->persistent, + c->flags->timeout, c->flags->read_timeout, c->flags->persistent, NULL, 0); + /* Stream context */ + sock->stream_ctx = c->flags->stream_ctx; + /* Add to seed nodes */ zend_hash_str_update_ptr(c->seeds, key, keylen, sock); @@ -1027,7 +1034,8 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) { * seeds array and know we have a non-empty array of strings all in * host:port format. */ PHP_REDIS_API void -cluster_init_seeds(redisCluster *cluster, zend_string **seeds, uint32_t nseeds) { +cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds) +{ RedisSock *sock; char *seed, *sep, key[1024]; int key_len, i, *map; @@ -1044,19 +1052,22 @@ cluster_init_seeds(redisCluster *cluster, zend_string **seeds, uint32_t nseeds) ZEND_ASSERT(sep != NULL); // Allocate a structure for this seed - sock = redis_sock_create(seed, sep - seed, - (unsigned short)atoi(sep+1), cluster->timeout, - cluster->read_timeout, cluster->persistent, NULL, 0); + sock = redis_sock_create(seed, sep - seed, atoi(sep + 1), + c->flags->timeout, c->flags->read_timeout, + c->flags->persistent, NULL, 0); + + /* Stream context */ + sock->stream_ctx = c->flags->stream_ctx; /* Credentials */ - redis_sock_set_auth(sock, cluster->flags->user, cluster->flags->pass); + redis_sock_set_auth(sock, c->flags->user, c->flags->pass); // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(sock->host), sock->port); // Add to our seed HashTable - zend_hash_str_update_ptr(cluster->seeds, key, key_len, sock); + zend_hash_str_update_ptr(c->seeds, key, key_len, sock); } efree(map); @@ -1807,7 +1818,6 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * zval z_ret, z_args[4]; sctx->cb.retval = &z_ret; sctx->cb.params = z_args; - sctx->cb.no_separation = 0; /* We're in a subscribe loop */ c->subscribed_slot = c->cmd_slot; diff --git a/cluster_library.h b/cluster_library.h index de9d171..98e9b0e 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -186,12 +186,8 @@ typedef struct clusterFoldItem clusterFoldItem; /* RedisCluster implementation structure */ typedef struct redisCluster { - /* Timeout and read timeout (for normal operations) */ - double timeout; - double read_timeout; - - /* Are we using persistent connections */ - int persistent; + /* One RedisSock struct for serialization and prefix information */ + RedisSock *flags; /* How long in milliseconds should we wait when being bounced around */ long waitms; @@ -241,9 +237,6 @@ typedef struct redisCluster { /* The slot where we're subscribed */ short subscribed_slot; - /* One RedisSock struct for serialization and prefix information */ - RedisSock *flags; - /* The first line of our last reply, not including our reply type byte * or the trailing \r\n */ char line_reply[1024]; diff --git a/library.c b/library.c index 2f95aea..6aef627 100644 --- a/library.c +++ b/library.c @@ -464,7 +464,6 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, zval z_ret, z_args[4]; sctx->cb.retval = &z_ret; sctx->cb.params = z_args; - sctx->cb.no_separation = 0; /* Multibulk response, {[pattern], type, channel, payload } */ while(1) { @@ -686,19 +685,6 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len) return NULL; } -static int redis_sock_read_cmp(RedisSock *redis_sock, const char *cmp, int cmplen) { - char *resp; - int len, rv = FAILURE; - - if ((resp = redis_sock_read(redis_sock, &len)) == NULL) - return FAILURE; - - rv = len == cmplen && !memcmp(resp, cmp, cmplen) ? SUCCESS : FAILURE; - - efree(resp); - return rv; -} - /* A simple union to store the various arg types we might handle in our * redis_spprintf command formatting function */ union resparg { @@ -1519,17 +1505,21 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret * the multi-bulk header for field and values */ if ((read_mbulk_header(redis_sock, &mhdr) < 0 || mhdr != 2) || ((id = redis_sock_read(redis_sock, &idlen)) == NULL) || - (read_mbulk_header(redis_sock, &fields) < 0 || fields % 2 != 0)) + (read_mbulk_header(redis_sock, &fields) < 0 || + (fields > 0 && fields % 2 != 0))) { if (id) efree(id); return -1; } - array_init(&z_message); - - redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS); - array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE); - add_assoc_zval_ex(z_ret, id, idlen, &z_message); + if (fields < 0) { + add_assoc_null_ex(z_ret, id, idlen); + } else { + array_init(&z_message); + redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS); + array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE); + add_assoc_zval_ex(z_ret, id, idlen, &z_message); + } efree(id); } @@ -1727,7 +1717,10 @@ redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements) switch (type) { case TYPE_BULK: if ((data = redis_sock_read_bulk_reply(redis_sock, li)) == NULL) { - goto failure; + if (!key) goto failure; + add_assoc_null_ex(z_ret, key, len); + efree(key); + key = NULL; } else if (key) { add_assoc_stringl_ex(z_ret, key, len, data, li); efree(data); @@ -2130,16 +2123,21 @@ static int redis_stream_liveness_check(php_stream *stream) { static int redis_sock_check_liveness(RedisSock *redis_sock) { - char id[64], ok; + char id[64], inbuf[4096]; int idlen, auth; smart_string cmd = {0}; + size_t len; /* Short circuit if we detect the stream has gone bad or if the user has * configured persistent connection "YOLO mode". */ - if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS) - return FAILURE; - else if (!INI_INT("redis.pconnect.echo_check_liveness")) + if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS) { + goto failure; + } + + redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; + if (!INI_INT("redis.pconnect.echo_check_liveness")) { return SUCCESS; + } /* AUTH (if we need it) */ auth = redis_sock_append_auth(redis_sock, &cmd); @@ -2150,12 +2148,55 @@ redis_sock_check_liveness(RedisSock *redis_sock) redis_cmd_append_sstr(&cmd, id, idlen); /* Send command(s) and make sure we can consume reply(ies) */ - ok = (redis_sock_write(redis_sock, cmd.c, cmd.len) >= 0) && - (!auth || redis_sock_read_ok(redis_sock) == SUCCESS) && - (redis_sock_read_cmp(redis_sock, id, idlen) == SUCCESS); - + if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) { + smart_string_free(&cmd); + goto failure; + } smart_string_free(&cmd); - return ok ? SUCCESS : FAILURE; + + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + goto failure; + } + + if (auth) { + if (strncmp(inbuf, "+OK", 3) == 0 || strncmp(inbuf, "-ERR Client sent AUTH", 21) == 0) { + /* successfully authenticated or authentication isn't required */ + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + goto failure; + } + } else if (strncmp(inbuf, "-NOAUTH", 7) == 0) { + /* connection is fine but authentication failed, next command must fails too */ + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "-NOAUTH", 7) != 0) { + goto failure; + } + return SUCCESS; + } else { + goto failure; + } + redis_sock->status = REDIS_SOCK_STATUS_READY; + } else { + if (strncmp(inbuf, "-NOAUTH", 7) == 0) { + /* connection is fine but authentication required */ + return SUCCESS; + } + } + + /* check echo response */ + if (*inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen || + redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || + strncmp(inbuf, id, idlen) != 0 + ) { + goto failure; + } + + return SUCCESS; +failure: + redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; + if (redis_sock->stream) { + php_stream_pclose(redis_sock->stream); + redis_sock->stream = NULL; + } + return FAILURE; } /** @@ -2165,9 +2206,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) { struct timeval tv, read_tv, *tv_ptr = NULL; zend_string *persistent_id = NULL, *estr = NULL; - char host[1024], *pos, *address, *schema = NULL; + char host[1024], *pos, *address, *scheme = NULL; const char *fmtstr = "%s://%s:%d"; - int host_len, usocket = 0, err = 0, tcp_flag = 1; + int host_len, usocket = 0, err = 0, tcp_flag = 1, scheme_free = 0; ConnectionPool *p = NULL; if (redis_sock->stream != NULL) { @@ -2175,9 +2216,12 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) } address = ZSTR_VAL(redis_sock->host); - if ((pos = strstr(address, "://")) != NULL) { - schema = estrndup(address, pos - address); + if ((pos = strstr(address, "://")) == NULL) { + scheme = redis_sock->stream_ctx ? "ssl" : "tcp"; + } else { + scheme = estrndup(address, pos - address); address = pos + sizeof("://") - 1; + scheme_free = 1; } if (address[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", address); @@ -2193,9 +2237,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) fmtstr = "%s://[%s]:%d"; } #endif - host_len = snprintf(host, sizeof(host), fmtstr, schema ? schema : "tcp", address, redis_sock->port); - if (schema) efree(schema); + host_len = snprintf(host, sizeof(host), fmtstr, scheme, address, redis_sock->port); } + if (scheme_free) efree(scheme); if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { @@ -2205,11 +2249,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) zend_llist_remove_tail(&p->list); if (redis_sock_check_liveness(redis_sock) == SUCCESS) { - redis_sock->status = REDIS_SOCK_STATUS_READY; return SUCCESS; - } else if (redis_sock->stream) { - php_stream_pclose(redis_sock->stream); - redis_sock->stream = NULL; } p->nb_active--; } diff --git a/redis_array_impl.c b/redis_array_impl.c index a427f67..8d8cece 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1134,7 +1134,6 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, z_cb->params = z_args; z_cb->retval = z_ret; - z_cb->no_separation = 0; z_cb->param_count = 2; /* run cb(hostname, count) */ diff --git a/redis_cluster.c b/redis_cluster.c index 3233b65..5cb453b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -351,7 +351,7 @@ void free_cluster_context(zend_object *object) { /* Attempt to connect to a Redis cluster provided seeds and timeout options */ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, double read_timeout, int persistent, zend_string *user, - zend_string *pass) + zend_string *pass, zval *context) { zend_string *hash = NULL, **seeds; redisCachedCluster *cc; @@ -369,10 +369,13 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time c->flags->user = zend_string_copy(user); if (pass && ZSTR_LEN(pass)) c->flags->pass = zend_string_copy(pass); + if (context) { + redis_sock_set_stream_context(c->flags, context); + } - c->timeout = timeout; - c->read_timeout = read_timeout; - c->persistent = persistent; + c->flags->timeout = timeout; + c->flags->read_timeout = read_timeout; + c->flags->persistent = persistent; c->waitms = timeout * 1000L; /* Attempt to load slots from cache if caching is enabled */ @@ -450,7 +453,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) { } /* Attempt to create/connect to the cluster */ - redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass); + redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass, NULL); /* Clean up */ zval_dtor(&z_seeds); @@ -464,38 +467,36 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) { /* Create a RedisCluster Object */ PHP_METHOD(RedisCluster, __construct) { - zval *object, *z_seeds = NULL, *z_auth = NULL; + zval *object, *z_seeds = NULL, *z_auth = NULL, *context = NULL; zend_string *user = NULL, *pass = NULL; double timeout = 0.0, read_timeout = 0.0; size_t name_len; zend_bool persistent = 0; - redisCluster *context = GET_CONTEXT(); + redisCluster *c = GET_CONTEXT(); char *name; // Parse arguments if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Os!|addbz", &object, redis_cluster_ce, &name, + "Os!|addbza", &object, redis_cluster_ce, &name, &name_len, &z_seeds, &timeout, &read_timeout, - &persistent, &z_auth) == FAILURE) + &persistent, &z_auth, &context) == FAILURE) { RETURN_FALSE; } - // Require a name - if (name_len == 0 && ZEND_NUM_ARGS() < 2) { - CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0); - } - /* If we've got a string try to load from INI */ if (ZEND_NUM_ARGS() < 2) { - redis_cluster_load(context, name, name_len); + if (name_len == 0) { // Require a name + CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0); + } + redis_cluster_load(c, name, name_len); return; } /* The normal case, loading from arguments */ redis_extract_auth_info(z_auth, &user, &pass); - redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout, - persistent, user, pass); + redis_cluster_init(c, Z_ARRVAL_P(z_seeds), timeout, read_timeout, + persistent, user, pass, context); if (user) zend_string_release(user); if (pass) zend_string_release(pass); diff --git a/redis_commands.c b/redis_commands.c index 3b19194..d6bb2bc 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -793,7 +793,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_arr, *z_chan; HashTable *ht_chan; smart_string cmdstr = {0}; - subscribeContext *sctx = emalloc(sizeof(subscribeContext)); + subscribeContext *sctx = ecalloc(1, sizeof(*sctx)); size_t key_len; int key_free; char *key; @@ -854,7 +854,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_arr, *z_chan; HashTable *ht_arr; smart_string cmdstr = {0}; - subscribeContext *sctx = emalloc(sizeof(subscribeContext)); + subscribeContext *sctx = ecalloc(1, sizeof(*sctx)); if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { efree(sctx); @@ -1299,6 +1299,34 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * have specific processing (argument validation, etc) that make them unique */ +/* Attempt to pull a long expiry from a zval. We're more restrictave than zval_get_long + * because that function will return integers from things like open file descriptors + * which should simply fail as a TTL */ +static int redis_try_get_expiry(zval *zv, zend_long *lval) { + double dval; + + /* Success on an actual long or double */ + if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) { + *lval = zval_get_long(zv); + return SUCCESS; + } + + /* Automatically fail if we're not a string */ + if (Z_TYPE_P(zv) != IS_STRING) + return FAILURE; + + /* Attempt to get a long from the string */ + switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), lval, &dval, 0)) { + case IS_DOUBLE: + *lval = dval; + /* fallthrough */ + case IS_LONG: + return SUCCESS; + default: + return FAILURE; + } +} + /* SET */ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -1306,7 +1334,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; zval *z_value, *z_opts=NULL; char *key = NULL, *exp_type = NULL, *set_type = NULL; - long expire = -1, exp_set = 0, keep_ttl = 0; + long exp_set = 0, keep_ttl = 0; + zend_long expire = -1; size_t key_len; // Make sure the function is being called correctly @@ -1316,14 +1345,6 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - /* 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) - { - return FAILURE; - } - // Check for an options array if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { HashTable *kt = Z_ARRVAL_P(z_opts); @@ -1355,8 +1376,12 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, set_type = Z_STRVAL_P(v); } } ZEND_HASH_FOREACH_END(); - } else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) { - expire = Z_LVAL_P(z_opts); + } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) { + if (redis_try_get_expiry(z_opts, &expire) == FAILURE) { + php_error_docref(NULL, E_WARNING, "Expire must be a long, double, or a numeric string"); + return FAILURE; + } + exp_set = 1; } @@ -1386,7 +1411,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (exp_type) { redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type)); - redis_cmd_append_sstr_long(&cmdstr, expire); + redis_cmd_append_sstr_long(&cmdstr, (long)expire); } if (set_type) diff --git a/redis_session.c b/redis_session.c index 6f2c5c5..4165fff 100644 --- a/redis_session.c +++ b/redis_session.c @@ -854,7 +854,7 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, PS_OPEN_FUNC(rediscluster) { redisCluster *c; - zval z_conf, *zv; + zval z_conf, *zv, *context; HashTable *ht_conf, *ht_seeds; double timeout = 0, read_timeout = 0; int persistent = 0, failover = REDIS_FAILOVER_NONE; @@ -907,6 +907,7 @@ PS_OPEN_FUNC(rediscluster) { #define CLUSTER_SESSION_CLEANUP() \ if (hash) zend_string_release(hash); \ + if (failstr) zend_string_release(failstr); \ if (prefix) zend_string_release(prefix); \ if (user) zend_string_release(user); \ if (pass) zend_string_release(pass); \ @@ -918,7 +919,6 @@ PS_OPEN_FUNC(rediscluster) { if (seeds == NULL) { php_error_docref(NULL, E_WARNING, "No valid seeds detected"); CLUSTER_SESSION_CLEANUP(); - zval_dtor(&z_conf); return FAILURE; } @@ -932,6 +932,10 @@ PS_OPEN_FUNC(rediscluster) { redis_sock_set_auth(c->flags, user, pass); + if ((context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht_conf, "stream", IS_ARRAY)) != NULL) { + redis_sock_set_stream_context(c->flags, context); + } + /* First attempt to load from cache */ if (CLUSTER_CACHING_ENABLED()) { hash = cluster_hash_seeds(seeds, nseeds); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index c7a403f..3c84885 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -371,9 +371,14 @@ class Redis_Test extends TestSuite $this->assertEquals($this->redis->get('foo'), 'bar'); $this->assertEquals($this->redis->ttl('foo'), 20); + /* Should coerce doubles into long */ + $this->assertTrue($this->redis->set('foo', 'bar-20.5', 20.5)); + $this->assertEquals($this->redis->ttl('foo'), 20); + $this->assertEquals($this->redis->get('foo'), 'bar-20.5'); + /* Invalid third arguments */ - $this->assertFalse($this->redis->set('foo','bar','baz')); - $this->assertFalse($this->redis->set('foo','bar',new StdClass())); + $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'); @@ -6064,6 +6069,21 @@ class Redis_Test extends TestSuite } } + /* Regression test for issue-1831 (XINFO STREAM on an empty stream) */ + public function testXInfoEmptyStream() { + /* Configure an empty stream */ + $this->redis->del('s'); + $this->redis->xAdd('s', '*', ['foo' => 'bar']); + $this->redis->xTrim('s', 0); + + $arr_info = $this->redis->xInfo('STREAM', 's'); + + $this->assertTrue(is_array($arr_info)); + $this->assertEquals(0, $arr_info['length']); + $this->assertEquals(NULL, $arr_info['first-entry']); + $this->assertEquals(NULL, $arr_info['last-entry']); + } + public function testInvalidAuthArgs() { $obj_new = $this->newInstance(); @@ -6082,9 +6102,15 @@ class Redis_Test extends TestSuite foreach ($arr_args as $arr_arg) { try { - @call_user_func_array([$obj_new, 'auth'], $arr_arg); + if (is_array($arr_arg)) { + @call_user_func_array([$obj_new, 'auth'], $arr_arg); + } else { + call_user_func([$obj_new, 'auth']); + } } catch (Exception $ex) { unset($ex); /* Suppress intellisense warning */ + } catch (ArgumentCountError $ex) { + unset($ex); /* Suppress intellisense warning */ } } } diff --git a/tests/startSession.php b/tests/startSession.php index c0ae188..f82c17e 100644 --- a/tests/startSession.php +++ b/tests/startSession.php @@ -22,11 +22,11 @@ ini_set('redis.session.lock_retries', $lock_retries); ini_set('redis.session.lock_expire', $lock_expire); ini_set('session.gc_maxlifetime', $sessionLifetime); -if (isset($argv[8])) { +if (isset($argv[10])) { ini_set('redis.session.locking_enabled', $argv[10]); } -if (isset($argv[9])) { +if (isset($argv[11])) { ini_set('redis.session.lock_wait_time', $argv[11]); }