From f11352f3f4db3fd63614ae2fa4513066bfdc669d Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sat, 13 Jun 2020 18:28:21 -0400 Subject: [PATCH 01/31] refactor based on develop branch --- common.h | 2 ++ library.c | 10 ++++++++++ redis.c | 1 + redis_commands.c | 10 ++++++++++ tests/RedisTest.php | 12 ++++++++++++ 5 files changed, 35 insertions(+) diff --git a/common.h b/common.h index 14aefb53a3..619569ceba 100644 --- a/common.h +++ b/common.h @@ -82,6 +82,7 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_COMPRESSION 7 #define REDIS_OPT_REPLY_LITERAL 8 #define REDIS_OPT_COMPRESSION_LEVEL 9 +#define REDIS_OPT_IGBINARY_NO_STRINGS 10 /* cluster options */ #define REDIS_FAILOVER_NONE 0 @@ -273,6 +274,7 @@ typedef struct { zend_string *persistent_id; redis_serializer serializer; + int no_strings; int compression; int compression_level; long dbNumber; diff --git a/library.c b/library.c index d9fc53a529..0afdbd5b17 100644 --- a/library.c +++ b/library.c @@ -1797,6 +1797,7 @@ redis_sock_create(char *host, int host_len, int port, redis_sock->serializer = REDIS_SERIALIZER_NONE; redis_sock->compression = REDIS_COMPRESSION_NONE; redis_sock->compression_level = 0; /* default */ + redis_sock->no_strings = 0; /* default */ redis_sock->mode = ATOMIC; redis_sock->head = NULL; redis_sock->current = NULL; @@ -2604,6 +2605,11 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len break; case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY + if (Z_TYPE_P(z) == IS_STRING && redis_sock->no_strings) { + *val = Z_STRVAL_P(z); + *val_len = Z_STRLEN_P(z); + return 0; + } if(igbinary_serialize(&val8, (size_t *)&sz, z) == 0) { *val = (char*)val8; *val_len = sz; @@ -2675,6 +2681,10 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, || (memcmp(val, "\x00\x00\x00\x01", 4) != 0 && memcmp(val, "\x00\x00\x00\x02", 4) != 0)) { + if (*val != '\0' && redis_sock->no_strings) { + ZVAL_STRINGL(z_ret, val, val_len); + return 1; + } /* This is most definitely not an igbinary string, so do not try to unserialize this as one. */ break; diff --git a/redis.c b/redis.c index 71e301adb4..96f61d756b 100644 --- a/redis.c +++ b/redis.c @@ -701,6 +701,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) { zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP); #ifdef HAVE_REDIS_IGBINARY zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_IGBINARY_NO_STRINGS"), REDIS_OPT_IGBINARY_NO_STRINGS); #endif #ifdef HAVE_REDIS_MSGPACK zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK); diff --git a/redis_commands.c b/redis_commands.c index 97442de387..f95fbcf72d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3933,6 +3933,10 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, switch(option) { case REDIS_OPT_SERIALIZER: RETURN_LONG(redis_sock->serializer); +#ifdef HAVE_REDIS_IGBINARY + case REDIS_OPT_IGBINARY_NO_STRINGS: + RETURN_LONG(redis_sock->no_strings); +#endif case REDIS_OPT_COMPRESSION: RETURN_LONG(redis_sock->compression); case REDIS_OPT_COMPRESSION_LEVEL: @@ -3994,6 +3998,12 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, val_long = zval_get_long(val); redis_sock->reply_literal = val_long != 0; RETURN_TRUE; +#ifdef HAVE_REDIS_IGBINARY + case REDIS_OPT_IGBINARY_NO_STRINGS: + val_long = zval_get_long(val); + redis_sock->no_strings = val_long != 0; + RETURN_TRUE; +#endif case REDIS_OPT_COMPRESSION: val_long = zval_get_long(val); if (val_long == REDIS_COMPRESSION_NONE diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 3cf753d8a9..9c784e4caf 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4502,6 +4502,18 @@ private function checkSerializer($mode) { $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok } + public function testIgbinaryNoStrings() + { + if (!defined('Redis::OPT_IGBINARY_NO_STRINGS')) { + $this->markTestSkipped(); + } + $this->assertTrue($this->redis->setOption(Redis::OPT_IGBINARY_NO_STRINGS, true)); + $this->assertTrue($this->redis->getOption(Redis::OPT_IGBINARY_NO_STRINGS)); + + $this->assertTrue($this->redis->set("no_binary", "test string")); + $this->assertEquals($this->redis->get('no_binary'), "test string"); + } + public function testCompressionLZF() { if (!defined('Redis::COMPRESSION_LZF')) { From 94212a90439ca7075fff68a40b7663ce96871b33 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 17 Jun 2020 21:48:50 -0400 Subject: [PATCH 02/31] redis clone --- common.h | 1 + library.c | 2 +- redis.c | 26 ++++++++++++++++++++++++++ redis_array.c | 1 + redis_cluster.c | 1 + sentinel_library.c | 1 + tests/RedisTest.php | 16 ++++++++++++++++ 7 files changed, 47 insertions(+), 1 deletion(-) diff --git a/common.h b/common.h index 14aefb53a3..edf8b557af 100644 --- a/common.h +++ b/common.h @@ -292,6 +292,7 @@ typedef struct { int readonly; int reply_literal; int tcp_keepalive; + int clone; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index d9fc53a529..cfff02c3b4 100644 --- a/library.c +++ b/library.c @@ -2021,7 +2021,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force) { if (redis_sock == NULL) { return FAILURE; - } else if (redis_sock->stream) { + } else if (redis_sock->stream && !redis_sock->clone) { if (redis_sock->persistent) { ConnectionPool *p = NULL; if (INI_INT("redis.pconnect.pooling_enabled")) { diff --git a/redis.c b/redis.c index 71e301adb4..75c9129f5a 100644 --- a/redis.c +++ b/redis.c @@ -580,6 +580,31 @@ free_redis_object(zend_object *object) } } +static zend_object * +clone_redis_object(zval *this_ptr) +{ + zend_object *old_object = Z_OBJ_P(this_ptr); + redis_object *old_redis = PHPREDIS_GET_OBJECT(redis_object, this_ptr); + redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(old_object->ce)); + + if (old_redis->sock) { + redis->sock = ecalloc(1, sizeof(RedisSock)); + redis->sock = memcpy(redis->sock, old_redis->sock, sizeof(RedisSock)); + redis->sock->clone = 1; + } + + zend_object_std_init(&redis->std, old_object->ce); + object_properties_init(&redis->std, old_object->ce); + + memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers)); + redis_object_handlers.offset = XtOffsetOf(redis_object, std); + redis_object_handlers.free_obj = free_redis_object; + redis_object_handlers.clone_obj = clone_redis_object; + redis->std.handlers = &redis_object_handlers; + + return &redis->std; +} + zend_object * create_redis_object(zend_class_entry *ce) { @@ -593,6 +618,7 @@ create_redis_object(zend_class_entry *ce) memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers)); redis_object_handlers.offset = XtOffsetOf(redis_object, std); redis_object_handlers.free_obj = free_redis_object; + redis_object_handlers.clone_obj = clone_redis_object; redis->std.handlers = &redis_object_handlers; return &redis->std; diff --git a/redis_array.c b/redis_array.c index bdd7120efa..99023930a7 100644 --- a/redis_array.c +++ b/redis_array.c @@ -200,6 +200,7 @@ create_redis_array_object(zend_class_entry *ce) memcpy(&redis_array_object_handlers, zend_get_std_object_handlers(), sizeof(redis_array_object_handlers)); redis_array_object_handlers.offset = XtOffsetOf(redis_array_object, std); redis_array_object_handlers.free_obj = free_redis_array_object; + redis_array_object_handlers.clone_obj = NULL; obj->std.handlers = &redis_array_object_handlers; return &obj->std; diff --git a/redis_cluster.c b/redis_cluster.c index a2ced22a86..e9e7fc6fb0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -326,6 +326,7 @@ zend_object * create_cluster_context(zend_class_entry *class_type) { memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); RedisCluster_handlers.free_obj = free_cluster_context; + RedisCluster_handlers.clone_obj = NULL; cluster->std.handlers = &RedisCluster_handlers; diff --git a/sentinel_library.c b/sentinel_library.c index 481985467f..e29d166c11 100644 --- a/sentinel_library.c +++ b/sentinel_library.c @@ -25,6 +25,7 @@ create_sentinel_object(zend_class_entry *ce) memcpy(&redis_sentinel_object_handlers, zend_get_std_object_handlers(), sizeof(redis_sentinel_object_handlers)); redis_sentinel_object_handlers.offset = XtOffsetOf(redis_sentinel_object, std); redis_sentinel_object_handlers.free_obj = free_redis_sentinel_object; + redis_sentinel_object_handlers.clone_obj = NULL; obj->std.handlers = &redis_sentinel_object_handlers; return &obj->std; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 3cf753d8a9..383b699007 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -97,6 +97,22 @@ public function testMinimumVersion() $this->assertTrue(version_compare($this->version, "2.4.0") >= 0); } + public function testClone() + { + if (get_class($this->redis) !== 'Redis') { + $this->markTestSkipped(); + return; + } + + $new = clone $this->redis; + /* check that connection works */ + $this->assertTrue($new->ping()); + /* confirm socket settings are disconnected from source object */ + $this->assertTrue($new->setOption(Redis::OPT_PREFIX, 'test')); + $this->assertEquals($new->getOption(Redis::OPT_PREFIX), 'test'); + $this->assertFalse($this->redis->getOption(Redis::OPT_PREFIX)); + } + public function testPing() { /* Reply literal off */ $this->assertTrue($this->redis->ping()); From b9713e54d05a543753c64dfaaa4b0eb1eeb36163 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 19 Jun 2020 08:48:51 -0400 Subject: [PATCH 03/31] Create new socket on clone --- common.h | 1 - library.c | 2 +- redis.c | 24 ++++++++++++++++++++---- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/common.h b/common.h index edf8b557af..14aefb53a3 100644 --- a/common.h +++ b/common.h @@ -292,7 +292,6 @@ typedef struct { int readonly; int reply_literal; int tcp_keepalive; - int clone; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index cfff02c3b4..d9fc53a529 100644 --- a/library.c +++ b/library.c @@ -2021,7 +2021,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force) { if (redis_sock == NULL) { return FAILURE; - } else if (redis_sock->stream && !redis_sock->clone) { + } else if (redis_sock->stream) { if (redis_sock->persistent) { ConnectionPool *p = NULL; if (INI_INT("redis.pconnect.pooling_enabled")) { diff --git a/redis.c b/redis.c index 75c9129f5a..c41b47239a 100644 --- a/redis.c +++ b/redis.c @@ -584,13 +584,29 @@ static zend_object * clone_redis_object(zval *this_ptr) { zend_object *old_object = Z_OBJ_P(this_ptr); - redis_object *old_redis = PHPREDIS_GET_OBJECT(redis_object, this_ptr); + redis_object *old_redis = PHPREDIS_GET_OBJECT(redis_object, old_object); redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(old_object->ce)); if (old_redis->sock) { - redis->sock = ecalloc(1, sizeof(RedisSock)); - redis->sock = memcpy(redis->sock, old_redis->sock, sizeof(RedisSock)); - redis->sock->clone = 1; + redis->sock = redis_sock_create(ZSTR_VAL(old_redis->sock->host), ZSTR_LEN(old_redis->sock->host), + old_redis->sock->port, + old_redis->sock->timeout, + old_redis->sock->read_timeout, + old_redis->sock->persistent, + ZSTR_VAL(old_redis->sock->persistent_id), + old_redis->sock->retry_interval); + + if (old_redis->sock->stream_ctx) { + redis_sock_set_stream_context(redis->sock, &old_redis->sock->stream_ctx->options); + } + + if (redis_sock_server_open(redis->sock) < 0) { + if (redis->sock->err) { + REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0); + } + redis_free_socket(redis->sock); + redis->sock = NULL; + } } zend_object_std_init(&redis->std, old_object->ce); From 7c43c54bc52168c6592ea99f32388f9a0980c529 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 21 Jul 2020 17:42:20 -0400 Subject: [PATCH 04/31] Added auth support --- redis.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redis.c b/redis.c index 651d00387a..657c1f6745 100644 --- a/redis.c +++ b/redis.c @@ -618,6 +618,10 @@ clone_redis_object(zval *this_ptr) redis_sock_set_stream_context(redis->sock, &old_redis->sock->stream_ctx->options); } + if (old_redis->sock->user) { + redis_sock_set_auth(redis->sock, old_redis->sock->user, old_redis->sock->pass); + } + if (redis_sock_server_open(redis->sock) < 0) { if (redis->sock->err) { REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0); From d2c927ba85b85a279cec5810e99723d14309a828 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 22 Oct 2020 10:00:43 +0300 Subject: [PATCH 05/31] 5.3.2 --- package.xml | 138 ++++++++++++++++++++++++++++++++++++++++++++-------- php_redis.h | 2 +- 2 files changed, 118 insertions(+), 22 deletions(-) diff --git a/package.xml b/package.xml index 466be5fcba..21339b3928 100644 --- a/package.xml +++ b/package.xml @@ -27,10 +27,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2020-07-07 + 2020-10-22 - 5.3.1 - 5.3.1 + 5.3.2 + 5.3.2 stable @@ -38,14 +38,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredis 5.3.1 - - This is a small bugfix release that fixes a couple of issues in 5.3.0. - - You should upgrade if you're using persistent_id in session.save_path or - of if you're having trouble building 5.3.0 because the php_hash_bin2hex - symbol is missing. - + This release containse some bugfixes and small improvements. You can find a detailed list of changes in Changelog.md and package.xml * Sponsors @@ -53,17 +46,38 @@ http://pear.php.net/dtd/package-2.0.xsd"> ~ BlueHost - https://bluehost.com ~ Redis Cache Pro for WordPress - https://wprediscache.com ~ Avtandil Kikabidze - https://github.com/akalongman + ~ Oleg Babushkin - https://github.com/olbabushkin + + phpredis 5.3.2 + + * Use "%.17g" sprintf format for doubles as done in Redis server. [32be3006] (Pavlo Yatsukhnenko) + * Allow to pass NULL as RedisCluster stream context options. [72024afe] (Pavlo Yatsukhnenko) --- - * Properly clean up on session start failure [066cff6a] (Michael Grunder) - * Treat NULL as a failure for redis_extract_auth_info [49428a2f, 14ac969d] - (Michael Grunder) - * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2] - (Michael Grunder) - * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5, - 3c56289c, 08f202e7] (Remi Collet) - * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko) - * Remove EOL Fedora installation instructions [b4779e6a] (Remi Collet) + + phpredis 5.3.2RC2 + + --- + + * Verify SET options are strings before testing them as strings [514bc371] (Michael Grunder) + + --- + + phpredis 5.3.2RC1 + + --- + * Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster [950e8de8] (Michael Grunder, Alex Offshore) + * Fix xReadGroup() must return message id [500916a4] (Pavlo Yatsukhnenko) + * Fix memory leak in rediscluster session handler [b2cffffc] (Pavlo Yatsukhnenko) + * Fix XInfo() returns false if the stream is empty [5719c9f7, 566fdeeb] (Pavlo Yatsukhnenko, Michael Grunder) + * Relax requirements on set's expire argument [36458071] (Michael Grunder) + * Refactor redis_sock_check_liveness [c5950644] (Pavlo Yatsukhnenko) + * PHP8 compatibility [a7662da7, f4a30cb2, 17848791] (Pavlo Yatsukhnenko, Remi Collet) + * Update documentation [c9ed151d, 398c99d9] (Ali Alwash, Gregoire Pineau) + * Add Redis::OPT_NULL_MULTIBULK_AS_NULL setting to treat NULL multi bulk replies as NULL instead of []. [950e8de8] (Michael Grunder, Alex Offshore) + * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko) + * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko) + * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko) @@ -125,7 +139,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> 7.0.0 - 7.9.99 1.4.0b1 @@ -139,6 +152,87 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + stablestable + 5.3.25.3.2 + 2020-10-22 + + This release containse some bugfixes and small improvements. + You can find a detailed list of changes in Changelog.md and package.xml + + * Sponsors + ~ Audiomack - https://audiomack.com + ~ BlueHost - https://bluehost.com + ~ Redis Cache Pro for WordPress - https://wprediscache.com + ~ Avtandil Kikabidze - https://github.com/akalongman + ~ Oleg Babushkin - https://github.com/olbabushkin + + phpredis 5.3.2 + + * Use "%.17g" sprintf format for doubles as done in Redis server. [32be3006] (Pavlo Yatsukhnenko) + * Allow to pass NULL as RedisCluster stream context options. [72024afe] (Pavlo Yatsukhnenko) + + --- + + phpredis 5.3.2RC2 + + --- + + * Verify SET options are strings before testing them as strings [514bc371] (Michael Grunder) + + --- + + phpredis 5.3.2RC1 + + --- + * Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster [950e8de8] (Michael Grunder, Alex Offshore) + * Fix xReadGroup() must return message id [500916a4] (Pavlo Yatsukhnenko) + * Fix memory leak in rediscluster session handler [b2cffffc] (Pavlo Yatsukhnenko) + * Fix XInfo() returns false if the stream is empty [5719c9f7, 566fdeeb] (Pavlo Yatsukhnenko, Michael Grunder) + * Relax requirements on set's expire argument [36458071] (Michael Grunder) + * Refactor redis_sock_check_liveness [c5950644] (Pavlo Yatsukhnenko) + * PHP8 compatibility [a7662da7, f4a30cb2, 17848791] (Pavlo Yatsukhnenko, Remi Collet) + * Update documentation [c9ed151d, 398c99d9] (Ali Alwash, Gregoire Pineau) + * Add Redis::OPT_NULL_MULTIBULK_AS_NULL setting to treat NULL multi bulk replies as NULL instead of []. [950e8de8] (Michael Grunder, Alex Offshore) + * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko) + * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko) + * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko) + + + + + stablestable + 5.3.15.3.1 + 2020-07-07 + + phpredis 5.3.1 + + This is a small bugfix release that fixes a couple of issues in 5.3.0. + + You should upgrade if you're using persistent_id in session.save_path or + of if you're having trouble building 5.3.0 because the php_hash_bin2hex + symbol is missing. + + You can find a detailed list of changes in Changelog.md and package.xml + + * Sponsors + ~ Audiomack - https://audiomack.com + ~ BlueHost - https://bluehost.com + ~ Redis Cache Pro for WordPress - https://wprediscache.com + ~ Avtandil Kikabidze - https://github.com/akalongman + + --- + * Properly clean up on session start failure [066cff6a] (Michael Grunder) + * Treat NULL as a failure for redis_extract_auth_info [49428a2f, 14ac969d] + (Michael Grunder) + * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2] + (Michael Grunder) + * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5, + 3c56289c, 08f202e7] (Remi Collet) + * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko) + * Remove EOL Fedora installation instructions [b4779e6a] (Remi Collet) + + stablestable 5.3.05.3.0 @@ -223,6 +317,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> (Michael Grunder) + stablestable 5.2.25.2.2 @@ -242,6 +337,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> ~ Till Kruss - https://github.com/tillkruss + stablestable 5.2.15.2.1 diff --git a/php_redis.h b/php_redis.h index 76983fe0df..24ba89a77e 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "5.3.1" +#define PHP_REDIS_VERSION "5.3.2" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 87297cbb4000c88b07e729b9379de321ead74aa2 Mon Sep 17 00:00:00 2001 From: defender-11 Date: Sat, 23 Jan 2021 00:52:55 +0800 Subject: [PATCH 06/31] Fixed#1895 --- library.c | 4 ++++ redis_array_impl.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/library.c b/library.c index 9008ffe0b7..a36f1d4e5e 100644 --- a/library.c +++ b/library.c @@ -721,7 +721,11 @@ static zend_string *redis_hash_auth(zend_string *user, zend_string *pass) { smart_str_appendl_ex(&salted, REDIS_G(salt), sizeof(REDIS_G(salt)), 0); ctx = emalloc(ops->context_size); +#if PHP_VERSION_ID >= 80100 + ops->hash_init(ctx,NULL); +#else ops->hash_init(ctx); +#endif ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(salted.s), ZSTR_LEN(salted.s)); digest = emalloc(ops->digest_size); diff --git a/redis_array_impl.c b/redis_array_impl.c index 8d8ceceede..7305e23703 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -494,7 +494,11 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos) void *ctx = emalloc(ops->context_size); unsigned char *digest = emalloc(ops->digest_size); +#if PHP_VERSION_ID >= 80100 + ops->hash_init(ctx,NULL); +#else ops->hash_init(ctx); +#endif ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(out), ZSTR_LEN(out)); ops->hash_final(digest, ctx); From 270b4db821fcbe9fb881eef83e046f87587c4110 Mon Sep 17 00:00:00 2001 From: Jan-E Date: Tue, 22 Sep 2020 11:56:59 +0200 Subject: [PATCH 07/31] PHP 8 compatibility Windows --- redis_array.h | 2 +- redis_array_impl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_array.h b/redis_array.h index 34460b10a8..805442aac9 100644 --- a/redis_array.h +++ b/redis_array.h @@ -1,7 +1,7 @@ #ifndef REDIS_ARRAY_H #define REDIS_ARRAY_H -#ifdef PHP_WIN32 +#if (defined(_MSC_VER) && _MSC_VER <= 1920) #include "win32/php_stdint.h" #else #include diff --git a/redis_array_impl.h b/redis_array_impl.h index 0ef5a73f10..a6802dd299 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -1,7 +1,7 @@ #ifndef REDIS_ARRAY_IMPL_H #define REDIS_ARRAY_IMPL_H -#ifdef PHP_WIN32 +#if (defined(_MSC_VER) && _MSC_VER <= 1920) #include #else #include From 9d0cd31c230acab6c7a444f361ff82eaa58210f2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 31 Jan 2021 16:16:33 -0800 Subject: [PATCH 08/31] Prepare 5.3.3 release --- Changelog.md | 23 ++++++++++++++++++++++- package.xml | 48 +++++++++++++----------------------------------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/Changelog.md b/Changelog.md index 70ec00ebc3..05af5fd7a2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,28 @@ All changes to phpredis will be documented in this file. We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [5.3.3] - 2021-02-01 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.3), [PECL](https:/pecl.php.net/package/redis/5.3.3)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [BlueHost](https://bluehost.com) +- [Redis Cache Pro for WordPress](https://wprediscache.com) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Oleg Babushkin](https://github.com/olbabushkin) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) + +### Fixed + +- Fixed Windows includes for PHP 8 + [270b4db8](https://www.github.com/phpredis//phpredis/commit/270b4db821fcbe9fb881eef83e046f87587c4110) + ([Jan-E](https://github.com/Jan-E)) +- Fix hash_ops for PHP 8.0.1 + [87297cbb](https://www.github.com/phpredis/phpredis/commit/87297cbb4000c88b07e729b9379de321ead74aa2) + ([defender-11](https://github.com/defender-11)) + +## [5.3.2] - 2020-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.2), [PECL](https://pecl.php.net/package/redis/5.3.2)) ### Sponsors :sparkling_heart: diff --git a/package.xml b/package.xml index 21339b3928..4fcb53840e 100644 --- a/package.xml +++ b/package.xml @@ -27,10 +27,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2020-10-22 + 2021-02-01 - 5.3.2 - 5.3.2 + 5.3.3 + 5.3.3 stable @@ -38,46 +38,24 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - This release containse some bugfixes and small improvements. + phpredsi 5.3.3 + + This release mostly includes just small PHP 8 Windows compatibility fixes + such that pecl.php.net can automatically build Windows DLLs. + You can find a detailed list of changes in Changelog.md and package.xml + * Fix PHP8 Windows includes [270b4db8] (Jan-E) + * Fix hash ops for php 8.0.1 [87297cbb] (defender-11) + * Sponsors ~ Audiomack - https://audiomack.com ~ BlueHost - https://bluehost.com ~ Redis Cache Pro for WordPress - https://wprediscache.com ~ Avtandil Kikabidze - https://github.com/akalongman ~ Oleg Babushkin - https://github.com/olbabushkin - - phpredis 5.3.2 - - * Use "%.17g" sprintf format for doubles as done in Redis server. [32be3006] (Pavlo Yatsukhnenko) - * Allow to pass NULL as RedisCluster stream context options. [72024afe] (Pavlo Yatsukhnenko) - - --- - - phpredis 5.3.2RC2 - - --- - - * Verify SET options are strings before testing them as strings [514bc371] (Michael Grunder) - - --- - - phpredis 5.3.2RC1 - - --- - * Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster [950e8de8] (Michael Grunder, Alex Offshore) - * Fix xReadGroup() must return message id [500916a4] (Pavlo Yatsukhnenko) - * Fix memory leak in rediscluster session handler [b2cffffc] (Pavlo Yatsukhnenko) - * Fix XInfo() returns false if the stream is empty [5719c9f7, 566fdeeb] (Pavlo Yatsukhnenko, Michael Grunder) - * Relax requirements on set's expire argument [36458071] (Michael Grunder) - * Refactor redis_sock_check_liveness [c5950644] (Pavlo Yatsukhnenko) - * PHP8 compatibility [a7662da7, f4a30cb2, 17848791] (Pavlo Yatsukhnenko, Remi Collet) - * Update documentation [c9ed151d, 398c99d9] (Ali Alwash, Gregoire Pineau) - * Add Redis::OPT_NULL_MULTIBULK_AS_NULL setting to treat NULL multi bulk replies as NULL instead of []. [950e8de8] (Michael Grunder, Alex Offshore) - * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko) - * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko) - * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko) + ~ Zaher Ghaibeh - https://github.com/zaherg + ~ BatchLabs - https://batch.com From cd05a3441fa1a1fe78ae924486620047643d4bf3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 1 Feb 2021 11:21:07 -0800 Subject: [PATCH 09/31] Disable clone handlers for Redis and RedisCluster. Presently PhpRedis will segfault if users attempt to clone either the Redis or RedisCluster objects. We plan on investigating proper clone support in future releases, but for now just diable the handlers. --- redis.c | 1 + redis_cluster.c | 1 + 2 files changed, 2 insertions(+) diff --git a/redis.c b/redis.c index 821e8bcbba..82c2aa8e17 100644 --- a/redis.c +++ b/redis.c @@ -611,6 +611,7 @@ create_redis_object(zend_class_entry *ce) memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers)); redis_object_handlers.offset = XtOffsetOf(redis_object, std); redis_object_handlers.free_obj = free_redis_object; + redis_object_handlers.clone_obj = NULL; redis->std.handlers = &redis_object_handlers; return &redis->std; diff --git a/redis_cluster.c b/redis_cluster.c index ab9d55b7d5..d5efdf0cc5 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -333,6 +333,7 @@ zend_object * create_cluster_context(zend_class_entry *class_type) { memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); RedisCluster_handlers.free_obj = free_cluster_context; + RedisCluster_handlers.clone_obj = NULL; cluster->std.handlers = &RedisCluster_handlers; From b50b3615877649a48dc49056f866b29585ddb47d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 1 Feb 2021 14:41:56 -0800 Subject: [PATCH 10/31] Update changelog for 5.3.3 --- Changelog.md | 3 +++ package.xml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 05af5fd7a2..b3f7b1e1ef 100644 --- a/Changelog.md +++ b/Changelog.md @@ -25,6 +25,9 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix hash_ops for PHP 8.0.1 [87297cbb](https://www.github.com/phpredis/phpredis/commit/87297cbb4000c88b07e729b9379de321ead74aa2) ([defender-11](https://github.com/defender-11)) +- Disable clone for Redis and RedisCluster objects. Presently they segfault. + [cd05a344](https://www.github.com/phpredis/phpredis/commit/87297cbb4000c88b07e729b9379de321ead74aa2) + ([Michael Grunder](https://github.com/michael-grunder)) ## [5.3.2] - 2020-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.2), [PECL](https://pecl.php.net/package/redis/5.3.2)) diff --git a/package.xml b/package.xml index 4fcb53840e..d12c5b0734 100644 --- a/package.xml +++ b/package.xml @@ -47,6 +47,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix PHP8 Windows includes [270b4db8] (Jan-E) * Fix hash ops for php 8.0.1 [87297cbb] (defender-11) + * Disable cloning Redis and RedisCluster objects [cd05a344] + (Michael Grunder) * Sponsors ~ Audiomack - https://audiomack.com From 9dbd3c56312d8b7f9cd434ad46a45e499813a19d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 1 Feb 2021 15:06:58 -0800 Subject: [PATCH 11/31] 5.3.2 --- package.xml | 2 +- php_redis.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.xml b/package.xml index d12c5b0734..071d95be82 100644 --- a/package.xml +++ b/package.xml @@ -38,7 +38,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredsi 5.3.3 + phpredis 5.3.3 This release mostly includes just small PHP 8 Windows compatibility fixes such that pecl.php.net can automatically build Windows DLLs. diff --git a/php_redis.h b/php_redis.h index 24ba89a77e..167e68f500 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "5.3.2" +#define PHP_REDIS_VERSION "5.3.3" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From edc724e6022620414abf4f90256522d03c3160fd Mon Sep 17 00:00:00 2001 From: Adam Olley Date: Wed, 10 Mar 2021 14:11:04 +1030 Subject: [PATCH 12/31] Pass compression flag when performing HMGET (#1945) Without this, performing a HMGET call fails to decompress the data before returning it to php. --- cluster_library.c | 1 + tests/RedisTest.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/cluster_library.c b/cluster_library.c index 5bcd23385f..f61a6704a7 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2112,6 +2112,7 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, if (c->reply_len > 0) { /* Push serialization settings from the cluster into our socket */ c->cmd_sock->serializer = c->flags->serializer; + c->cmd_sock->compression = c->flags->compression; /* Call our specified callback */ if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index c9b147b87b..b4995ed5aa 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4610,6 +4610,10 @@ private function checkCompression($mode, $level) $this->assertEquals($val, $this->redis->get('key')); } } + + // Issue 1945. Ensure we decompress data with hmget. + $this->redis->hset('hkey', 'data', 'abc'); + $this->assertEquals('abc', current($this->redis->hmget('hkey', ['data']))); } public function testDumpRestore() { From c48b3a2ba3d677efceb9ac1958246631f6bd8037 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 26 Feb 2021 09:52:10 -0800 Subject: [PATCH 13/31] Fix ZSTD decompression on bad data. ZSTD uses two defined error numbers to inform the caller when the compressed data is invalid (e.g. wrong magic number) or the size is unknown. We should always know the size so abort if ZSTD returns either to us. Fixes: #1936 --- library.c | 3 ++- tests/RedisTest.php | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index a36f1d4e5e..c9a2f7abe5 100644 --- a/library.c +++ b/library.c @@ -2877,7 +2877,8 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) size_t len; len = ZSTD_getFrameContentSize(val, val_len); - if (len >= 0) { + + if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN) { data = emalloc(len); len = ZSTD_decompress(data, len, val, val_len); if (ZSTD_isError(len)) { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index b4995ed5aa..48a679789a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4573,6 +4573,14 @@ public function testCompressionZSTD() if (!defined('Redis::COMPRESSION_ZSTD')) { $this->markTestSkipped(); } + + /* Issue 1936 regression. Make sure we don't overflow on bad data */ + $this->redis->del('badzstd'); + $this->redis->set('badzstd', '123'); + $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_ZSTD); + $this->assertEquals('123', $this->redis->get('badzstd')); + $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); + $this->checkCompression(Redis::COMPRESSION_ZSTD, 0); $this->checkCompression(Redis::COMPRESSION_ZSTD, 9); } From fe9011d8767175d61b512e978fe24424dd31dae6 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 25 Feb 2021 10:03:53 -0800 Subject: [PATCH 14/31] Normalize Redis callback prototypes and stop typecasting. (#1935) --- common.h | 24 ++++++----- library.c | 104 ++++++++++++++++++++++++++++----------------- library.h | 20 ++++----- php_redis.h | 5 --- redis.c | 23 +++++----- sentinel_library.c | 13 ++++-- sentinel_library.h | 2 +- 7 files changed, 111 insertions(+), 80 deletions(-) diff --git a/common.h b/common.h index 80b34e6568..955895a58d 100644 --- a/common.h +++ b/common.h @@ -140,7 +140,7 @@ typedef enum { #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \ fold_item *fi = malloc(sizeof(fold_item)); \ - fi->fun = (void *)callback; \ + fi->fun = callback; \ fi->ctx = closure_context; \ fi->next = NULL; \ if (redis_sock->current) { \ @@ -195,7 +195,7 @@ typedef enum { REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ } -/* Process a command but with a specific command building function +/* Process a command but with a specific command building function * and keyword which is passed to us*/ #define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ @@ -256,12 +256,6 @@ typedef enum { #endif #endif -typedef struct fold_item { - zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...); - void *ctx; - struct fold_item *next; -} fold_item; - /* {{{ struct RedisSock */ typedef struct { php_stream *stream; @@ -286,8 +280,8 @@ typedef struct { zend_string *prefix; short mode; - fold_item *head; - fold_item *current; + struct fold_item *head; + struct fold_item *current; zend_string *pipeline_cmd; @@ -302,6 +296,16 @@ typedef struct { } RedisSock; /* }}} */ +/* Redis response handler function callback prototype */ +typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*, zval*, void*); + +typedef struct fold_item { + FailableResultCallback fun; + void *ctx; + struct fold_item *next; +} fold_item; + typedef struct { zend_llist list; int nb_active; diff --git a/library.c b/library.c index c9a2f7abe5..722b497209 100644 --- a/library.c +++ b/library.c @@ -1010,7 +1010,8 @@ int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulon return redis_cmd_append_sstr(cmd, arg, len); } -PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +PHP_REDIS_API int +redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; @@ -1018,32 +1019,36 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } - add_next_index_bool(z_tab, 0); - return; + return FAILURE; } ret = atof(response); efree(response); if (IS_ATOMIC(redis_sock)) { - RETURN_DOUBLE(ret); + RETVAL_DOUBLE(ret); } else { add_next_index_double(z_tab, ret); } + + return SUCCESS; } -PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; long l; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } - add_next_index_bool(z_tab, 0); - return; + return FAILURE; } if (strncmp(response, "+string", 7) == 0) { @@ -1064,20 +1069,23 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * efree(response); if (IS_ATOMIC(redis_sock)) { - RETURN_LONG(l); + RETVAL_LONG(l); } else { add_next_index_long(z_tab, l); } + + return SUCCESS; } -PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; zval z_ret; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { - RETURN_FALSE; + RETVAL_FALSE; + return FAILURE; } /* Parse it into a zval array */ @@ -1092,6 +1100,8 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } else { add_next_index_zval(z_tab, &z_ret); } + + return SUCCESS; } PHP_REDIS_API void @@ -1148,14 +1158,16 @@ redis_parse_info_response(char *response, zval *z_ret) * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code * to handle. */ -PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { +PHP_REDIS_API int +redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *resp; int resp_len; zval z_ret; /* Make sure we can read the bulk response from Redis */ if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { - RETURN_FALSE; + RETVAL_FALSE; + return FAILURE; } /* Parse it out */ @@ -1170,6 +1182,8 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo } else { add_next_index_zval(z_tab, &z_ret); } + + return SUCCESS; } PHP_REDIS_API void @@ -1267,7 +1281,7 @@ redis_parse_client_list_response(char *response, zval *z_ret) } } -PHP_REDIS_API void +PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) @@ -1286,36 +1300,38 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, success_callback(redis_sock); } if (IS_ATOMIC(redis_sock)) { - RETURN_BOOL(ret); + RETVAL_BOOL(ret); } else { add_next_index_bool(z_tab, ret); } + + return ret ? SUCCESS : FAILURE; } -PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, ctx, NULL); + return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, ctx, NULL); } -PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval * z_tab, - void *ctx) +PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval * z_tab, + void *ctx) { char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len)) - == NULL) - { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } - add_next_index_bool(z_tab, 0); - return; + + return FAILURE; } if(response[0] == ':') { @@ -1340,8 +1356,12 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, } else { add_next_index_null(z_tab); } + efree(response); + return FAILURE; } + efree(response); + return SUCCESS; } /* Helper method to convert [key, value, key, value] into [key => value, @@ -1922,7 +1942,7 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, Re z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } -PHP_REDIS_API void +PHP_REDIS_API int redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; @@ -1935,13 +1955,15 @@ redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ta } if (IS_ATOMIC(redis_sock)) { - RETURN_BOOL(ret); + RETVAL_BOOL(ret); } else { add_next_index_bool(z_tab, ret); } + + return ret ? SUCCESS : FAILURE; } -PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; @@ -1950,10 +1972,11 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock == NULL) { if (IS_ATOMIC(redis_sock)) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } - add_next_index_bool(z_tab, 0); - return; + return FAILURE; } if (IS_ATOMIC(redis_sock)) { if (!redis_unpack(redis_sock, response, response_len, return_value)) { @@ -1967,7 +1990,9 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_stringl(z_tab, response, response_len); } } + efree(response); + return SUCCESS; } PHP_REDIS_API @@ -1995,7 +2020,7 @@ void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock } /* like string response, but never unserialized. */ -PHP_REDIS_API void +PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -2007,17 +2032,20 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, == NULL) { if (IS_ATOMIC(redis_sock)) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } - add_next_index_bool(z_tab, 0); - return; + return FAILURE; } if (IS_ATOMIC(redis_sock)) { RETVAL_STRINGL(response, response_len); } else { add_next_index_stringl(z_tab, response, response_len); } + efree(response); + return SUCCESS; } /* Response for DEBUG object which is a formatted single line reply */ diff --git a/library.h b/library.h index db47545dc9..53da60e08d 100644 --- a/library.h +++ b/library.h @@ -49,20 +49,20 @@ PHP_REDIS_API zend_string *redis_pool_spprintf(RedisSock *redis_sock, char *fmt, PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len); PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len); -PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); +PHP_REDIS_API int redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); typedef void (*SuccessCallback)(RedisSock *redis_sock); -PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); -PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); +PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); -PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock); @@ -148,7 +148,7 @@ PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, long lon PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); +PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); /* Helper methods to get configuration values from a HashTable. */ diff --git a/php_redis.h b/php_redis.h index 167e68f500..0bf06dbb0f 100644 --- a/php_redis.h +++ b/php_redis.h @@ -273,11 +273,6 @@ PHP_MINIT_FUNCTION(redis); PHP_MSHUTDOWN_FUNCTION(redis); PHP_MINFO_FUNCTION(redis); -/* Redis response handler function callback prototype */ -typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); - -typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*, zval*, void*); - PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock); diff --git a/redis.c b/redis.c index 82c2aa8e17..bd67de3753 100644 --- a/redis.c +++ b/redis.c @@ -1109,10 +1109,10 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } /* {{{ proto long Redis::bitop(string op, string key, ...) */ -PHP_METHOD(Redis, bitop) -{ +PHP_METHOD(Redis, bitop) { REDIS_PROCESS_CMD(bitop, redis_long_response); } + /* }}} */ /* {{{ proto long Redis::bitcount(string key, [int start], [int end]) @@ -1248,8 +1248,7 @@ PHP_METHOD(Redis, incrBy){ /* {{{ proto float Redis::incrByFloat(string key, float value) */ PHP_METHOD(Redis, incrByFloat) { - REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, - redis_bulk_double_response); + REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, redis_bulk_double_response); } /* }}} */ @@ -1345,10 +1344,10 @@ PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock) redis_sock->watching = 1; } -PHP_REDIS_API void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_set_watch); } @@ -1365,12 +1364,12 @@ PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock) redis_sock->watching = 0; } -PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, ctx, redis_clear_watch); + return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, ctx, redis_clear_watch); } /* {{{ proto boolean Redis::unwatch() @@ -2056,7 +2055,7 @@ PHP_METHOD(Redis, move) { /* }}} */ static -void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) +void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, FailableResultCallback fun) { RedisSock *redis_sock; smart_string cmd = {0}; @@ -2102,6 +2101,7 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) if (IS_ATOMIC(redis_sock)) { fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } + REDIS_PROCESS_RESPONSE(fun); } @@ -3477,8 +3477,7 @@ PHP_METHOD(Redis, client) { /* We handle CLIENT LIST with a custom response function */ if(!strncasecmp(opt, "list", 4)) { if (IS_ATOMIC(redis_sock)) { - redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, - NULL); + redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_client_list_reply); } else { diff --git a/sentinel_library.c b/sentinel_library.c index 481985467f..0fe64cc145 100644 --- a/sentinel_library.c +++ b/sentinel_library.c @@ -30,7 +30,7 @@ create_sentinel_object(zend_class_entry *ce) return &obj->std; } -PHP_REDIS_API void +PHP_REDIS_API int sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[4096]; @@ -40,14 +40,17 @@ sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis /* Throws exception on failure */ if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { - RETURN_FALSE; + RETVAL_FALSE; + return FAILURE; } if (*inbuf != TYPE_MULTIBULK) { if (*inbuf == TYPE_ERR) { redis_sock_set_err(redis_sock, inbuf + 1, len - 1); } - RETURN_FALSE; + + RETVAL_FALSE; + return FAILURE; } array_init(&z_ret); nelem = atoi(inbuf + 1); @@ -57,5 +60,7 @@ sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); add_next_index_zval(&z_ret, return_value); } - RETURN_ZVAL(&z_ret, 0, 1); + + RETVAL_ZVAL(&z_ret, 0, 1); + return SUCCESS; } diff --git a/sentinel_library.h b/sentinel_library.h index 460ccfad1d..88d9a564e7 100644 --- a/sentinel_library.h +++ b/sentinel_library.h @@ -8,6 +8,6 @@ typedef redis_object redis_sentinel_object; zend_object *create_sentinel_object(zend_class_entry *ce); -PHP_REDIS_API void sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); #endif /* REDIS_SENTINEL_LIBRARY_H */ From c996ac3eb10c2cdae8bf7b2ede4d13b8a3ab680d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 22 Mar 2021 11:51:58 -0700 Subject: [PATCH 15/31] Prepare for 5.3.4 release --- Changelog.md | 24 ++++++++++++++++++++++++ README.markdown | 2 +- package.xml | 50 +++++++++++++++++++++++++++++++++++++------------ php_redis.h | 2 +- 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/Changelog.md b/Changelog.md index b3f7b1e1ef..55a4920479 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,30 @@ All changes to phpredis will be documented in this file. We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.3.4] - 2021-03-23 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.4), [PECL](https:/pecl.php.net/package/redis/5.3.4)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) + +### Fixed + +- Fix multi/pipeline segfault on Apple silicon [#1917](https://github.com/phpredis/phpredis/issues/1917) + [e0796d48](https://github.com/phpredis/phpredis/commit/e0796d48af18adac2b93982474e7df8de79ec854) + ([Michael Grunder](https://github.com/michael-grunder)) +- Pass compression flag on HMGET in RedisCluster [#1945](https://github.com/phpredis/phpredis/issues/1945) + [edc724e6](https://github.com/phpredis/phpredis/commit/edc724e6022620414abf4f90256522d03c3160fd) + ([Adam Olley](https://github.com/aolley)) +- Abide by ZSTD error return constants [#1936](https://github.com/phpredis/phpredis/issues/1936) + [8400ed1c](https://github.com/phpredis/phpredis/pull/1937/commits/8400ed1cb23a22f70727cb60e88ca5397ee10d23) + ([Michael Grunder](https://github.com/michael-grunder)) + ## [5.3.3] - 2021-02-01 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.3), [PECL](https:/pecl.php.net/package/redis/5.3.3)) ### Sponsors :sparkling_heart: diff --git a/README.markdown b/README.markdown index 79d857387f..399c32b364 100644 --- a/README.markdown +++ b/README.markdown @@ -20,7 +20,7 @@ You can also make a one-time contribution with one of the links below. [![Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1) ## Sponsors -Audiomack.comBluehost.com +Audiomack.comBluehost.comOpenLMS.net # Table of contents ----- diff --git a/package.xml b/package.xml index 071d95be82..70d5f0f6ed 100644 --- a/package.xml +++ b/package.xml @@ -27,10 +27,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2021-02-01 + 2021-03-23 - 5.3.3 - 5.3.3 + 5.3.4 + 5.3.4 stable @@ -38,24 +38,23 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredis 5.3.3 + phpredis 5.3.4 - This release mostly includes just small PHP 8 Windows compatibility fixes - such that pecl.php.net can automatically build Windows DLLs. + This release fixes a multi/pipeline segfault on apple silicon as well as + two small compression related bugs. You can find a detailed list of changes in Changelog.md and package.xml - * Fix PHP8 Windows includes [270b4db8] (Jan-E) - * Fix hash ops for php 8.0.1 [87297cbb] (defender-11) - * Disable cloning Redis and RedisCluster objects [cd05a344] - (Michael Grunder) + * Fix multi/pipeline segfault on Apple silicon [e0796d48] (Michael Grunder) + * Pass compression flag on HMGET in RedisCluster [edc724e6] (Adam Olley) + * Abide by ZSTD error return constants [8400ed1c] (Michael Grunder) * Sponsors ~ Audiomack - https://audiomack.com + ~ Open LMS - https://openlms.net ~ BlueHost - https://bluehost.com - ~ Redis Cache Pro for WordPress - https://wprediscache.com + ~ Object Cache Pro for WordPress - https://objectcache.pro ~ Avtandil Kikabidze - https://github.com/akalongman - ~ Oleg Babushkin - https://github.com/olbabushkin ~ Zaher Ghaibeh - https://github.com/zaherg ~ BatchLabs - https://batch.com @@ -132,6 +131,33 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + stablestable + 5.3.35.3.3 + 2021-03-23 + + phpredis 5.3.3 + + This release mostly includes just small PHP 8 Windows compatibility fixes + such that pecl.php.net can automatically build Windows DLLs. + + You can find a detailed list of changes in Changelog.md and package.xml + + * Fix PHP8 Windows includes [270b4db8] (Jan-E) + * Fix hash ops for php 8.0.1 [87297cbb] (defender-11) + * Disable cloning Redis and RedisCluster objects [cd05a344] + (Michael Grunder) + + * Sponsors + ~ Audiomack - https://audiomack.com + ~ BlueHost - https://bluehost.com + ~ Redis Cache Pro for WordPress - https://wprediscache.com + ~ Avtandil Kikabidze - https://github.com/akalongman + ~ Zaher Ghaibeh - https://github.com/zaherg + ~ BatchLabs - https://batch.com + + + stablestable 5.3.25.3.2 diff --git a/php_redis.h b/php_redis.h index 0bf06dbb0f..0149935034 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "5.3.3" +#define PHP_REDIS_VERSION "5.3.4" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 4b1394418e7643f8879f68d69f15a85d0e69a4ba Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 23 Mar 2021 13:09:25 -0700 Subject: [PATCH 16/31] Fix PhpRedis session tests to soften timing issues Rework the session locking unit tests to be less reliant on arbitrary sleep calls which can be very troublesome when running in Travis and especially when running in Travis under valgrind. --- tests/RedisTest.php | 98 +++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 48a679789a..8478f6ee46 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6320,10 +6320,15 @@ public function testSession_lockKeyCorrect() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 5, true); - usleep(100000); - $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); + $maxwait = (ini_get('redis.session.lock_wait_time') * + ini_get('redis.session.lock_retries') / + 1000000.00); + + $exist = $this->waitForSessionLockKey($sessionId, $maxwait); + $this->assertTrue($exist); } public function testSession_lockingDisabledByDefault() @@ -6348,8 +6353,8 @@ public function testSession_lockReleasedOnClose() $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 1, true); - usleep(1100000); - + $sleep = ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries'); + usleep($sleep + 1000000); $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); } @@ -6408,20 +6413,23 @@ public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId)); } - public function testSession_correctLockRetryCount() - { + public function testSession_correctLockRetryCount() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); + + /* Start another process and wait until it has the lock */ $this->startSessionProcess($sessionId, 10, true); - usleep(100000); + if ( ! $this->waitForSessionLockKey($sessionId, 2)) { + $this->assertTrue(false); + return; + } - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 1000000, 3); - $end = microtime(true); - $elapsedTime = $end - $start; + $t1 = microtime(true); + $ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10); + if ( ! $this->assertFalse($ok)) return; + $t2 = microtime(true); - $this->assertTrue($elapsedTime > 3 && $elapsedTime < 4); - $this->assertFalse($sessionSuccessful); + $this->assertTrue($t2 - $t1 >= 1 && $t2 - $t1 <= 3); } public function testSession_defaultLockRetryCount() @@ -6429,7 +6437,14 @@ public function testSession_defaultLockRetryCount() $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 10, true); - usleep(100000); + + $keyname = $this->sessionPrefix . $sessionId . '_LOCK'; + $begin = microtime(true); + + if ( ! $this->waitForSessionLockKey($sessionId, 3)) { + $this->assertTrue(false); + return; + } $start = microtime(true); $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 200000, 0); @@ -6444,20 +6459,27 @@ public function testSession_noUnlockOfOtherProcess() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 3, true, 1); // Process 1 - usleep(100000); - $this->startSessionProcess($sessionId, 5, true); // Process 2 - $start = microtime(true); - // Waiting until TTL of process 1 ended and process 2 locked the session, - // because is not guaranteed which waiting process gets the next lock - sleep(1); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); - $end = microtime(true); - $elapsedTime = $end - $start; + $t1 = microtime(true); - $this->assertTrue($elapsedTime > 5); - $this->assertTrue($sessionSuccessful); + /* 1. Start a background process, and wait until we are certain + * the lock was attained. */ + $nsec = 3; + $this->startSessionProcess($sessionId, $nsec, true, $nsec); + if ( ! $this->waitForSessionLockKey($sessionId, 1)) { + $this->assertFalse(true); + return; + } + + /* 2. Attempt to lock the same session. This should force us to + * wait until the first lock is released. */ + $t2 = microtime(true); + $ok = $this->startSessionProcess($sessionId, 0, false); + $t3 = microtime(true); + + /* 3. Verify that we did in fact have to wait for this lock */ + $this->assertTrue($ok); + $this->assertTrue($t3 - $t2 >= $nsec - ($t2 - $t1)); } public function testSession_lockWaitTime() @@ -6716,6 +6738,30 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx } } + /** + * @param string $session_id + * @param string $max_wait_sec + * + * Sometimes we want to block until a session lock has been detected + * This is better and faster than arbitrarily sleeping. If we don't + * detect the session key within the specified maximum number of + * seconds, the function returns failure. + * + * @return bool + */ + private function waitForSessionLockKey($session_id, $max_wait_sec) { + $now = microtime(true); + $key = $this->sessionPrefix . $session_id . '_LOCK'; + + do { + usleep(10000); + $exists = $this->redis->exists($key); + } while (!$exists && microtime(true) <= $now + $max_wait_sec); + + return $exists || $this->redis->exists($key); + } + + /** * @param string $sessionId * @param int $sessionLifetime From d98b9f2a70dc4fac3a077749cd06bf84699f644b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 23 Mar 2021 13:12:26 -0700 Subject: [PATCH 17/31] Prepare for 5.3.4 v2 --- Changelog.md | 3 +++ package.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 55a4920479..43d90cdd24 100644 --- a/Changelog.md +++ b/Changelog.md @@ -28,6 +28,9 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Abide by ZSTD error return constants [#1936](https://github.com/phpredis/phpredis/issues/1936) [8400ed1c](https://github.com/phpredis/phpredis/pull/1937/commits/8400ed1cb23a22f70727cb60e88ca5397ee10d23) ([Michael Grunder](https://github.com/michael-grunder)) +- Fix timing related CI session tests + [9b986bf8](https://github.com/phpredis/phpredis/commit/9b986bf81859f5a5983cd148cb15ee6ce292d288) + ([Michael Grunder](https://github.com/michael-grunder)) ## [5.3.3] - 2021-02-01 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.3), [PECL](https:/pecl.php.net/package/redis/5.3.3)) diff --git a/package.xml b/package.xml index 70d5f0f6ed..e1e026e970 100644 --- a/package.xml +++ b/package.xml @@ -48,6 +48,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix multi/pipeline segfault on Apple silicon [e0796d48] (Michael Grunder) * Pass compression flag on HMGET in RedisCluster [edc724e6] (Adam Olley) * Abide by ZSTD error return constants [8400ed1c] (Michael Grunder) + * Fix timing related CI session tests [9b986bf8] (Michael Grunder) * Sponsors ~ Audiomack - https://audiomack.com From e67e1a167465e93b5c8aaf8f2defe3a34908485a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 23 Mar 2021 14:51:31 -0700 Subject: [PATCH 18/31] More Travis-CI fixes --- tests/RedisSentinelTest.php | 2 +- tests/RedisTest.php | 31 +++++++++++++++++++++++++++---- tests/check-quorum.sh | 12 ++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) create mode 100755 tests/check-quorum.sh diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php index b88e006477..70ea8543a0 100644 --- a/tests/RedisSentinelTest.php +++ b/tests/RedisSentinelTest.php @@ -40,7 +40,7 @@ public function setUp() public function testCkquorum() { - $this->assertTrue($this->sentinel->ckquorum(self::NAME)); + $this->assertTrue(is_bool($this->sentinel->ckquorum(self::NAME))); } public function testFailover() diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 8478f6ee46..72631aa782 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6164,7 +6164,7 @@ public function testInvalidAuthArgs() { if (is_array($arr_arg)) { @call_user_func_array([$obj_new, 'auth'], $arr_arg); } else { - call_user_func([$obj_new, 'auth']); + @call_user_func([$obj_new, 'auth']); } } catch (Exception $ex) { unset($ex); /* Suppress intellisense warning */ @@ -6327,8 +6327,9 @@ public function testSession_lockKeyCorrect() ini_get('redis.session.lock_retries') / 1000000.00); - $exist = $this->waitForSessionLockKey($sessionId, $maxwait); + $exist = $this->waitForSessionLockKey($sessionId, $maxwait + 1); $this->assertTrue($exist); + $this->redis->del($this->sessionPrefix . $sessionId . '_LOCK'); } public function testSession_lockingDisabledByDefault() @@ -6353,8 +6354,7 @@ public function testSession_lockReleasedOnClose() $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 1, true); - $sleep = ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries'); - usleep($sleep + 1000000); + $this->waitForProcess('startSession.php', 5); $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); } @@ -6761,6 +6761,29 @@ private function waitForSessionLockKey($session_id, $max_wait_sec) { return $exists || $this->redis->exists($key); } + /** + * @param string $str_search pattern to look for in ps + * @param int $timeout Maximum amount of time to wait + * + * Small helper function to wait until we no longer detect a running process. + * This is an attempt to fix timing related false failures on session tests + * when running in CI. + */ + function waitForProcess($str_search, $timeout = 0.0) { + $st = microtime(true); + + do { + $str_procs = shell_exec("ps aux|grep $str_search|grep -v grep"); + $arr_procs = array_filter(explode("\n", $str_procs)); + if (count($arr_procs) == 0) + return true; + + usleep(10000); + $elapsed = microtime(true) - $st; + } while ($timeout < 0 || $elapsed < $timeout); + + return false; + } /** * @param string $sessionId diff --git a/tests/check-quorum.sh b/tests/check-quorum.sh new file mode 100755 index 0000000000..3980b1f8b5 --- /dev/null +++ b/tests/check-quorum.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +for try in $(seq 0 3); do + RES=$(redis-cli -p 26379 sentinel ckquorum mymaster 2>/dev/null); + if [[ "$RES" == OK* ]]; then + echo "Quorum detected, exiting"; + break; + else + echo "No Quorum - $RES"; + fi; + sleep 1; +done From e3426c1ba6d225e3623850e6a96fd1395d8c3b75 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 23 Mar 2021 17:35:06 -0700 Subject: [PATCH 19/31] 5.3.4 - Move the date back one day --- Changelog.md | 2 +- package.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 43d90cdd24..a29f27b130 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,7 @@ All changes to phpredis will be documented in this file. We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [5.3.4] - 2021-03-23 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.4), [PECL](https:/pecl.php.net/package/redis/5.3.4)) +## [5.3.4] - 2021-03-24 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.4), [PECL](https:/pecl.php.net/package/redis/5.3.4)) ### Sponsors :sparkling_heart: diff --git a/package.xml b/package.xml index e1e026e970..e1c9de5cfc 100644 --- a/package.xml +++ b/package.xml @@ -27,7 +27,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2021-03-23 + 2021-03-24 5.3.4 5.3.4 From eec24884a0c652eb22290e6e982991cc573a87c6 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 30 May 2021 10:32:43 -0400 Subject: [PATCH 20/31] fixes --- tests/RedisTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index bff9aa509e..fac5d770f6 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6332,6 +6332,8 @@ public function testInvalidAuthArgs() { try { 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 */ @@ -6949,6 +6951,29 @@ private function waitForSessionLockKey($session_id, $max_wait_sec) { return $exists || $this->redis->exists($key); } + /** + * @param string $str_search pattern to look for in ps + * @param int $timeout Maximum amount of time to wait + * + * Small helper function to wait until we no longer detect a running process. + * This is an attempt to fix timing related false failures on session tests + * when running in CI. + */ + function waitForProcess($str_search, $timeout = 0.0) { + $st = microtime(true); + + do { + $str_procs = shell_exec("ps aux|grep $str_search|grep -v grep"); + $arr_procs = array_filter(explode("\n", $str_procs)); + if (count($arr_procs) == 0) + return true; + + usleep(10000); + $elapsed = microtime(true) - $st; + } while ($timeout < 0 || $elapsed < $timeout); + + return false; + } /** * @param string $sessionId From 3b674884ed1c400f17c094b64d16f8ce75855dda Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 30 May 2021 10:34:58 -0400 Subject: [PATCH 21/31] merge --- tests/RedisTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index fac5d770f6..12d828f589 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6496,8 +6496,9 @@ public function testSession_lockKeyCorrect() ini_get('redis.session.lock_retries') / 1000000.00); - $exist = $this->waitForSessionLockKey($sessionId, $maxwait); + $exist = $this->waitForSessionLockKey($sessionId, $maxwait + 1); $this->assertTrue($exist); + $this->redis->del($this->sessionPrefix . $sessionId . '_LOCK'); } public function testSession_lockingDisabledByDefault() @@ -6522,8 +6523,7 @@ public function testSession_lockReleasedOnClose() $this->setSessionHandler(); $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 1, true); - $sleep = ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries'); - usleep($sleep + 10000); + $this->waitForProcess('startSession.php', 5); $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); } From 1869069b7fc03922669bfc84a596fc9000cbe2f1 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 30 May 2021 12:29:07 -0400 Subject: [PATCH 22/31] 1st patch --- common.h | 26 +++++++++++++---------- library.c | 14 +++++++++++-- redis.c | 2 ++ redis_commands.c | 11 ++++++++++ tests/RedisTest.php | 50 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 13 deletions(-) diff --git a/common.h b/common.h index 2684f9d1f6..42546a7abc 100644 --- a/common.h +++ b/common.h @@ -73,17 +73,19 @@ typedef enum _PUBSUB_TYPE { } PUBSUB_TYPE; /* options */ -#define REDIS_OPT_SERIALIZER 1 -#define REDIS_OPT_PREFIX 2 -#define REDIS_OPT_READ_TIMEOUT 3 -#define REDIS_OPT_SCAN 4 -#define REDIS_OPT_FAILOVER 5 -#define REDIS_OPT_TCP_KEEPALIVE 6 -#define REDIS_OPT_COMPRESSION 7 -#define REDIS_OPT_REPLY_LITERAL 8 -#define REDIS_OPT_COMPRESSION_LEVEL 9 -#define REDIS_OPT_NULL_MBULK_AS_NULL 10 -#define REDIS_OPT_IGBINARY_NO_STRINGS 11 +#define REDIS_OPT_SERIALIZER 1 +#define REDIS_OPT_PREFIX 2 +#define REDIS_OPT_READ_TIMEOUT 3 +#define REDIS_OPT_SCAN 4 +#define REDIS_OPT_FAILOVER 5 +#define REDIS_OPT_TCP_KEEPALIVE 6 +#define REDIS_OPT_COMPRESSION 7 +#define REDIS_OPT_REPLY_LITERAL 8 +#define REDIS_OPT_COMPRESSION_LEVEL 9 +#define REDIS_OPT_NULL_MBULK_AS_NULL 10 +#define REDIS_OPT_IGBINARY_NO_STRINGS 11 +#define REDIS_OPT_COMPRESSION_MIN_SIZE 12 +#define REDIS_OPT_COMPRESSION_MIN_RATIO 13 /* cluster options */ #define REDIS_FAILOVER_NONE 0 @@ -277,6 +279,8 @@ typedef struct { int no_strings; int compression; int compression_level; + int compression_min_size; + double compression_min_ratio; long dbNumber; zend_string *prefix; diff --git a/library.c b/library.c index 715ca6ac61..381c3c8c79 100644 --- a/library.c +++ b/library.c @@ -2164,6 +2164,8 @@ redis_sock_create(char *host, int host_len, int port, redis_sock->compression = REDIS_COMPRESSION_NONE; redis_sock->compression_level = 0; /* default */ redis_sock->no_strings = 0; /* default */ + redis_sock->compression_min_size = -1; /* default */ + redis_sock->compression_min_ratio = 0; /* default */ redis_sock->mode = ATOMIC; return redis_sock; @@ -2829,7 +2831,7 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) size_t len; valfree = redis_serialize(redis_sock, z, &buf, &len); - if (redis_sock->compression == REDIS_COMPRESSION_NONE) { + if (redis_sock->compression == REDIS_COMPRESSION_NONE || (redis_sock->compression_min_size > 0 && redis_sock->compression_min_size >= len)) { *val = buf; *val_len = len; return valfree; @@ -2847,6 +2849,10 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) size = len + MIN(UINT_MAX - len, MAX(LZF_MARGIN, len / 25)); data = emalloc(size); if ((res = lzf_compress(buf, len, data, size)) > 0) { + if (((double) res / (double) len) >= redis_sock->compression_min_ratio) { + efree(data); + break; + } if (valfree) efree(buf); *val = data; *val_len = res; @@ -2878,7 +2884,7 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) size = ZSTD_compressBound(len); data = emalloc(size); size = ZSTD_compress(data, size, buf, len, level); - if (!ZSTD_isError(size)) { + if (!ZSTD_isError(size) && ((double) size / (double) len) < redis_sock->compression_min_ratio) { if (valfree) efree(buf); data = erealloc(data, size); *val = data; @@ -2981,6 +2987,10 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) char *data; unsigned long long len; + if (redis_sock->compression_min_size > 0 && !ZSTD_isFrame(val, val_len)) { + return redis_unserialize(redis_sock, val, val_len, z_ret); + } + len = ZSTD_getFrameContentSize(val, val_len); if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN && len <= INT_MAX) diff --git a/redis.c b/redis.c index bf3f5ef1f2..01b3113c61 100644 --- a/redis.c +++ b/redis.c @@ -761,6 +761,8 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) { zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_LEVEL"), REDIS_OPT_COMPRESSION_LEVEL); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_NULL_MULTIBULK_AS_NULL"), REDIS_OPT_NULL_MBULK_AS_NULL); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_MIN_SIZE"), REDIS_OPT_COMPRESSION_MIN_SIZE); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_MIN_RATIO"), REDIS_OPT_COMPRESSION_MIN_RATIO); /* serializer */ zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE); diff --git a/redis_commands.c b/redis_commands.c index b1267951f2..06bd6cc728 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -4291,6 +4291,10 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_LONG(redis_sock->compression); case REDIS_OPT_COMPRESSION_LEVEL: RETURN_LONG(redis_sock->compression_level); + case REDIS_OPT_COMPRESSION_MIN_SIZE: + RETURN_LONG(redis_sock->compression_min_size) + case REDIS_OPT_COMPRESSION_MIN_RATIO: + RETURN_DOUBLE(redis_sock->compression_min_ratio) case REDIS_OPT_PREFIX: if (redis_sock->prefix) { RETURN_STRINGL(ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); @@ -4377,6 +4381,13 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_TRUE; } break; + case REDIS_OPT_COMPRESSION_MIN_SIZE: + val_long = zval_get_long(val); + redis_sock->compression_min_size = val_long; + RETURN_TRUE; + case REDIS_OPT_COMPRESSION_MIN_RATIO: + redis_sock->compression_min_ratio = zval_get_double(val); + RETURN_TRUE; case REDIS_OPT_COMPRESSION_LEVEL: val_long = zval_get_long(val); redis_sock->compression_level = val_long; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 12d828f589..4acf354ab6 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4738,6 +4738,56 @@ public function testCompressionLZF() $this->checkCompression(Redis::COMPRESSION_LZF, 0); } + public function testCompressionLZFLimited() + { + if (!defined('Redis::COMPRESSION_LZF')) { + $this->markTestSkipped(); + } + + $checks = [ + "test123" => strlen("test123"), + str_repeat('a', 101) => 9, + random_bytes(110) => 110, + ]; + + $this->limitedCompressionCheck($checks, Redis::COMPRESSION_LZF); + } + + public function testCompressionZSTDLimited() + { + if (!defined('Redis::COMPRESSION_ZSTD')) { + $this->markTestSkipped(); + } + + $checks = [ + "test123" => strlen("test123"), + str_repeat('a', 101) => 17, + random_bytes(110) => 110, + ]; + + $this->limitedCompressionCheck($checks, Redis::COMPRESSION_ZSTD); + } + + private function limitedCompressionCheck($checks, $mode) + { + $settings = [ + Redis::OPT_COMPRESSION => $mode, + Redis::OPT_COMPRESSION_MIN_SIZE => 100, + Redis::OPT_COMPRESSION_MIN_RATIO => 0.3, + Redis::OPT_COMPRESSION_LEVEL => 0 + ]; + foreach ($settings as $k => $v) { + $this->assertTrue($this->redis->setOption($k, $v)); + $this->assertEquals($this->redis->getOption($k), $v); + } + + foreach ($checks as $v => $len) { + $this->assertTrue($this->redis->set('foo', $v)); + $this->assertEquals($this->redis->get('foo'), $v); + $this->assertEquals($this->redis->strlen('foo'), $len); + } + } + public function testCompressionZSTD() { if (!defined('Redis::COMPRESSION_ZSTD')) { From ee32e4db688cd4a080e36f5f6589b9d4c4d0d8cc Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 10 May 2020 13:12:37 -0400 Subject: [PATCH 23/31] fixed checks --- library.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 381c3c8c79..d4205d866d 100644 --- a/library.c +++ b/library.c @@ -2985,9 +2985,13 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) #ifdef HAVE_REDIS_ZSTD { char *data; - unsigned long long len; + long long len; - if (redis_sock->compression_min_size > 0 && !ZSTD_isFrame(val, val_len)) { + len = ZSTD_getFrameContentSize(val, val_len); + if ( + (redis_sock->compression_min_ratio > 0 || redis_sock->compression_min_size > 0) + && (len == ZSTD_CONTENTSIZE_UNKNOWN || len == ZSTD_CONTENTSIZE_ERROR || len <= 0) + ) { return redis_unserialize(redis_sock, val, val_len, z_ret); } From 7c67fc5936d6133dc1cbea303ac815cbf5fb13e4 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 10 May 2020 18:41:50 -0400 Subject: [PATCH 24/31] fixed checks --- library.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index d4205d866d..dcbb69ee51 100644 --- a/library.c +++ b/library.c @@ -2823,6 +2823,9 @@ static uint8_t crc8(unsigned char *input, size_t len) { } #endif +#define PHP_REDIS_COMPRESSION_RATIO_CHECK(__x) \ + (redis_sock->compression_min_ratio > 0 && (((double) __x / (double) len) >= redis_sock->compression_min_ratio)) + PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) { @@ -2849,7 +2852,7 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) size = len + MIN(UINT_MAX - len, MAX(LZF_MARGIN, len / 25)); data = emalloc(size); if ((res = lzf_compress(buf, len, data, size)) > 0) { - if (((double) res / (double) len) >= redis_sock->compression_min_ratio) { + if (PHP_REDIS_COMPRESSION_RATIO_CHECK(res)) { efree(data); break; } @@ -2884,7 +2887,7 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) size = ZSTD_compressBound(len); data = emalloc(size); size = ZSTD_compress(data, size, buf, len, level); - if (!ZSTD_isError(size) && ((double) size / (double) len) < redis_sock->compression_min_ratio) { + if (!ZSTD_isError(size) && !PHP_REDIS_COMPRESSION_RATIO_CHECK(size)) { if (valfree) efree(buf); data = erealloc(data, size); *val = data; From 5c42e4879a8575b754f639f3d0896e24ac58932a Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 10 May 2020 19:14:56 -0400 Subject: [PATCH 25/31] Fixed check From 357f92e5838932996cb4a27273d99cdc2bdb3049 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 30 May 2021 12:34:42 -0400 Subject: [PATCH 26/31] merge --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index dcbb69ee51..f810a21a26 100644 --- a/library.c +++ b/library.c @@ -2988,7 +2988,7 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) #ifdef HAVE_REDIS_ZSTD { char *data; - long long len; + unsigned long long len; len = ZSTD_getFrameContentSize(val, val_len); if ( From 91ffdd5eabb24d1183c89705479ad30653e2fb0a Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 30 May 2021 12:41:34 -0400 Subject: [PATCH 27/31] Added checks --- library.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index f810a21a26..ed918634db 100644 --- a/library.c +++ b/library.c @@ -2934,7 +2934,7 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) lz4len = LZ4_compress_HC(buf, lz4pos, old_len, lz4bound, redis_sock->compression_level); } - if (lz4len <= 0) { + if (lz4len <= 0 || PHP_REDIS_COMPRESSION_RATIO_CHECK(lz4len)) { efree(lz4buf); break; } @@ -3026,8 +3026,9 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) /* We must have at least enough bytes for our header, and can't have more than * INT_MAX + our header size. */ - if (val_len < REDIS_LZ4_HDR_SIZE || val_len > INT_MAX + REDIS_LZ4_HDR_SIZE) + if (val_len < REDIS_LZ4_HDR_SIZE || val_len > INT_MAX + REDIS_LZ4_HDR_SIZE) { break; + } /* Operate on copies in case our CRC fails */ const char *copy = val; @@ -3040,8 +3041,9 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) copy += sizeof(int); copylen -= sizeof(int); /* Make sure our CRC matches (TODO: Maybe issue a docref error?) */ - if (crc8((unsigned char*)&datalen, sizeof(datalen)) != lz4crc) + if (crc8((unsigned char*)&datalen, sizeof(datalen)) != lz4crc) { break; + } /* Finally attempt decompression */ data = emalloc(datalen); From dac3831d0444a32e35592926b7e9b0d4c43c4c93 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 30 May 2021 22:17:38 -0400 Subject: [PATCH 28/31] Added lz4 test --- tests/RedisTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 4acf354ab6..51114191de 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4815,6 +4815,21 @@ public function testCompressionLZ4() $this->checkCompression(Redis::COMPRESSION_LZ4, 9); } + public function testCompressionLZ4Limited() + { + if (!defined('Redis::COMPRESSION_LZ4')) { + $this->markTestSkipped(); + } + + $checks = [ + "test123" => strlen("test123"), + str_repeat('a', 101) => 17, + random_bytes(110) => 110, + ]; + + $this->limitedCompressionCheck($checks, Redis::COMPRESSION_ZSTD); + } + private function checkCompression($mode, $level) { $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION, $mode) === TRUE); // set ok From 1aa52101f6cc878e00d2ac6ddb42e1a991e39e61 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 30 May 2021 22:21:53 -0400 Subject: [PATCH 29/31] removed redundant code --- library.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/library.c b/library.c index ed918634db..4c289d526f 100644 --- a/library.c +++ b/library.c @@ -2998,8 +2998,6 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) return redis_unserialize(redis_sock, val, val_len, z_ret); } - len = ZSTD_getFrameContentSize(val, val_len); - if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN && len <= INT_MAX) { size_t zlen; From 46b76e827015e8d3c3023c2aad525e1fc074c833 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 9 Aug 2021 02:11:48 -0400 Subject: [PATCH 30/31] lz4 safety checks --- library.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library.c b/library.c index 4c289d526f..25c1177d11 100644 --- a/library.c +++ b/library.c @@ -3028,6 +3028,11 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) break; } + /* don't bother with strings smaller then minimum size */ + if (redis_sock->compression_min_size > 0 && val_len < redis_sock->compression_min_size) { + break; + } + /* Operate on copies in case our CRC fails */ const char *copy = val; size_t copylen = val_len; @@ -3043,6 +3048,11 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) break; } + /* likely not valid header, compressed value >100megs or 1meg with > 100x compression ratio */ + if (datalen > 100000000 || (datalen >= 1000000 && datalen / val_len > 100)) { + break; + } + /* Finally attempt decompression */ data = emalloc(datalen); if (LZ4_decompress_safe(copy, data, copylen, datalen) > 0) { From d593ad1e61158b4356bdd8ad5d63efa7d5dea985 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 10 Aug 2021 19:03:22 -0400 Subject: [PATCH 31/31] refine redis header --- library.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/library.c b/library.c index 25c1177d11..2c6df88396 100644 --- a/library.c +++ b/library.c @@ -2917,9 +2917,12 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) char *lz4buf, *lz4pos; lz4bound = LZ4_compressBound(len); - lz4buf = emalloc(REDIS_LZ4_HDR_SIZE + lz4bound); + lz4buf = emalloc(REDIS_LZ4_HDR_SIZE + lz4bound + 1); lz4pos = lz4buf; + /* Added LZ4 header */ + *lz4pos++ = '\4'; + /* Copy and move past crc8 length checksum */ memcpy(lz4pos, &crc, sizeof(crc)); lz4pos += sizeof(crc); @@ -2941,7 +2944,7 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) if (valfree) efree(buf); *val = lz4buf; - *val_len = lz4len + REDIS_LZ4_HDR_SIZE; + *val_len = lz4len + REDIS_LZ4_HDR_SIZE + 1; return 1; } #endif @@ -3028,14 +3031,14 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) break; } - /* don't bother with strings smaller then minimum size */ - if (redis_sock->compression_min_size > 0 && val_len < redis_sock->compression_min_size) { + /* check bit to ensure payload is LZ4 */ + if (*val != '\4') { break; } /* Operate on copies in case our CRC fails */ - const char *copy = val; - size_t copylen = val_len; + const char *copy = (val + 1); + size_t copylen = val_len - 1; /* Read in our header bytes */ memcpy(&lz4crc, copy, sizeof(uint8_t)); @@ -3048,11 +3051,6 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) break; } - /* likely not valid header, compressed value >100megs or 1meg with > 100x compression ratio */ - if (datalen > 100000000 || (datalen >= 1000000 && datalen / val_len > 100)) { - break; - } - /* Finally attempt decompression */ data = emalloc(datalen); if (LZ4_decompress_safe(copy, data, copylen, datalen) > 0) {