Skip to content

Commit 3645807

Browse files
Relax requirements on set's expire argument (#1830)
Relax requirements on set's expire argument See: #1783
1 parent 2351d12 commit 3645807

File tree

2 files changed

+44
-14
lines changed

2 files changed

+44
-14
lines changed

redis_commands.c

+37-12
Original file line numberDiff line numberDiff line change
@@ -1299,14 +1299,43 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
12991299
* have specific processing (argument validation, etc) that make them unique
13001300
*/
13011301

1302+
/* Attempt to pull a long expiry from a zval. We're more restrictave than zval_get_long
1303+
* because that function will return integers from things like open file descriptors
1304+
* which should simply fail as a TTL */
1305+
static int redis_try_get_expiry(zval *zv, zend_long *lval) {
1306+
double dval;
1307+
1308+
/* Success on an actual long or double */
1309+
if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
1310+
*lval = zval_get_long(zv);
1311+
return SUCCESS;
1312+
}
1313+
1314+
/* Automatically fail if we're not a string */
1315+
if (Z_TYPE_P(zv) != IS_STRING)
1316+
return FAILURE;
1317+
1318+
/* Attempt to get a long from the string */
1319+
switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), lval, &dval, 0)) {
1320+
case IS_DOUBLE:
1321+
*lval = dval;
1322+
/* fallthrough */
1323+
case IS_LONG:
1324+
return SUCCESS;
1325+
default:
1326+
return FAILURE;
1327+
}
1328+
}
1329+
13021330
/* SET */
13031331
int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
13041332
char **cmd, int *cmd_len, short *slot, void **ctx)
13051333
{
13061334
smart_string cmdstr = {0};
13071335
zval *z_value, *z_opts=NULL;
13081336
char *key = NULL, *exp_type = NULL, *set_type = NULL;
1309-
long expire = -1, exp_set = 0, keep_ttl = 0;
1337+
long exp_set = 0, keep_ttl = 0;
1338+
zend_long expire = -1;
13101339
size_t key_len;
13111340

13121341
// Make sure the function is being called correctly
@@ -1316,14 +1345,6 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
13161345
return FAILURE;
13171346
}
13181347

1319-
/* Our optional argument can either be a long (to support legacy SETEX */
1320-
/* redirection), or an array with Redis >= 2.6.12 set options */
1321-
if (z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY
1322-
&& Z_TYPE_P(z_opts) != IS_NULL)
1323-
{
1324-
return FAILURE;
1325-
}
1326-
13271348
// Check for an options array
13281349
if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
13291350
HashTable *kt = Z_ARRVAL_P(z_opts);
@@ -1355,8 +1376,12 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
13551376
set_type = Z_STRVAL_P(v);
13561377
}
13571378
} ZEND_HASH_FOREACH_END();
1358-
} else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) {
1359-
expire = Z_LVAL_P(z_opts);
1379+
} else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) {
1380+
if (redis_try_get_expiry(z_opts, &expire) == FAILURE) {
1381+
php_error_docref(NULL, E_WARNING, "Expire must be a long, double, or a numeric string");
1382+
return FAILURE;
1383+
}
1384+
13601385
exp_set = 1;
13611386
}
13621387

@@ -1386,7 +1411,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
13861411

13871412
if (exp_type) {
13881413
redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type));
1389-
redis_cmd_append_sstr_long(&cmdstr, expire);
1414+
redis_cmd_append_sstr_long(&cmdstr, (long)expire);
13901415
}
13911416

13921417
if (set_type)

tests/RedisTest.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,14 @@ public function testExtendedSet() {
371371
$this->assertEquals($this->redis->get('foo'), 'bar');
372372
$this->assertEquals($this->redis->ttl('foo'), 20);
373373

374+
/* Should coerce doubles into long */
375+
$this->assertTrue($this->redis->set('foo', 'bar-20.5', 20.5));
376+
$this->assertEquals($this->redis->ttl('foo'), 20);
377+
$this->assertEquals($this->redis->get('foo'), 'bar-20.5');
378+
374379
/* Invalid third arguments */
375-
$this->assertFalse($this->redis->set('foo','bar','baz'));
376-
$this->assertFalse($this->redis->set('foo','bar',new StdClass()));
380+
$this->assertFalse(@$this->redis->set('foo','bar','baz'));
381+
$this->assertFalse(@$this->redis->set('foo','bar',new StdClass()));
377382

378383
/* Set if not exist */
379384
$this->redis->del('foo');

0 commit comments

Comments
 (0)