@@ -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 */
13031331int 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 )
0 commit comments