diff --git a/Changelog.md b/Changelog.md
index 34e6f1e02a..27541b5753 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,7 +5,58 @@ 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.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:
+
+- [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))
+- 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))
+
+### 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))
+- 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))
## [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/README.markdown b/README.markdown
index 4840f4cd78..f40e463ee2 100644
--- a/README.markdown
+++ b/README.markdown
@@ -21,7 +21,7 @@ You can also make a one-time contribution with one of the links below.
[](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)
## Sponsors
-
+

# Table of contents
-----
diff --git a/common.h b/common.h
index 2ad7c70865..42546a7abc 100644
--- a/common.h
+++ b/common.h
@@ -73,16 +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_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
@@ -273,8 +276,11 @@ typedef struct {
zend_string *persistent_id;
redis_serializer serializer;
+ 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 87b6bfa83e..2c6df88396 100644
--- a/library.c
+++ b/library.c
@@ -2162,6 +2162,10 @@ 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->compression_min_size = -1; /* default */
+ redis_sock->compression_min_ratio = 0; /* default */
redis_sock->mode = ATOMIC;
return redis_sock;
@@ -2819,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)
{
@@ -2827,7 +2834,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;
@@ -2845,6 +2852,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 (PHP_REDIS_COMPRESSION_RATIO_CHECK(res)) {
+ efree(data);
+ break;
+ }
if (valfree) efree(buf);
*val = data;
*val_len = res;
@@ -2876,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)) {
+ if (!ZSTD_isError(size) && !PHP_REDIS_COMPRESSION_RATIO_CHECK(size)) {
if (valfree) efree(buf);
data = erealloc(data, size);
*val = data;
@@ -2906,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);
@@ -2923,14 +2937,14 @@ 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;
}
if (valfree) efree(buf);
*val = lz4buf;
- *val_len = lz4len + REDIS_LZ4_HDR_SIZE;
+ *val_len = lz4len + REDIS_LZ4_HDR_SIZE + 1;
return 1;
}
#endif
@@ -2980,11 +2994,16 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
unsigned long long 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);
+ }
if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN && len <= INT_MAX)
{
size_t zlen;
-
data = emalloc(len);
zlen = ZSTD_decompress(data, len, val, val_len);
if (ZSTD_isError(zlen) || zlen != len) {
@@ -3008,12 +3027,18 @@ 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;
+ }
+
+ /* 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));
@@ -3022,8 +3047,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);
@@ -3107,6 +3133,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;
@@ -3178,6 +3209,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/package.xml b/package.xml
index 21339b3928..e1c9de5cfc 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-03-24
- 5.3.2
- 5.3.2
+ 5.3.4
+ 5.3.4
stable
@@ -38,46 +38,26 @@ http://pear.php.net/dtd/package-2.0.xsd">
PHP
- This release containse some bugfixes and small improvements.
+ phpredis 5.3.4
+
+ 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 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
+ ~ 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
-
- 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
@@ -152,6 +132,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 6b7b3be4ba..7fa7b0489e 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.4"
PHP_METHOD(Redis, __construct);
PHP_METHOD(Redis, __destruct);
diff --git a/redis.c b/redis.c
index 230859dbc4..01b3113c61 100644
--- a/redis.c
+++ b/redis.c
@@ -604,6 +604,51 @@ 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, old_object);
+ redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(old_object->ce));
+
+ if (old_redis->sock) {
+ 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 (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);
+ }
+ redis_free_socket(redis->sock);
+ redis->sock = NULL;
+ }
+ }
+
+ 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)
{
@@ -617,6 +662,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;
@@ -715,12 +761,15 @@ 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);
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_array.c b/redis_array.c
index 9d6883c305..2cf10ee0bf 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -196,6 +196,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 402c23b7cc..90e8dd9b2d 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;
diff --git a/redis_commands.c b/redis_commands.c
index 16bc8e8c7d..06bd6cc728 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4283,10 +4283,18 @@ 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:
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));
@@ -4346,6 +4354,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_NULL_MBULK_AS_NULL:
val_long = zval_get_long(val);
redis_sock->null_mbulk_as_null = val_long != 0;
@@ -4367,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/sentinel_library.c b/sentinel_library.c
index 0fe64cc145..475ebb72a9 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/RedisSentinelTest.php b/tests/RedisSentinelTest.php
index 4d941dd95a..bdc9aeaaf1 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 ed8b53f253..51114191de 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -138,6 +138,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());
@@ -4702,6 +4718,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')) {
@@ -4710,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')) {
@@ -4737,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
@@ -6304,6 +6397,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 */
@@ -6466,8 +6561,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()
@@ -6492,8 +6588,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'));
}
@@ -6921,6 +7016,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