Skip to content

Commit f7428d8

Browse files
committed
Add phpredis serialization and compression config support
1 parent 5321e34 commit f7428d8

File tree

6 files changed

+317
-51
lines changed

6 files changed

+317
-51
lines changed

.github/workflows/tests.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,12 @@ jobs:
4949
uses: shivammathur/setup-php@v2
5050
with:
5151
php-version: ${{ matrix.php }}
52-
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd, redis, memcached
52+
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd, redis-phpredis/[email protected], igbinary, msgpack, lzf, zstd, lz4, memcached
5353
tools: composer:v2
5454
coverage: none
55+
env:
56+
REDIS_CONFIGURE_OPTS: --enable-redis --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lzf --with-liblzf --enable-redis-zstd --with-libzstd --enable-redis-lz4 --with-liblz4
57+
REDIS_LIBS: liblz4-dev, liblzf-dev, libzstd-dev
5558

5659
- name: Set Minimum PHP 8.0 Versions
5760
uses: nick-invision/retry@v1

src/Illuminate/Cache/PhpRedisLock.php

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
namespace Illuminate\Cache;
44

55
use Illuminate\Redis\Connections\PhpRedisConnection;
6-
use Redis;
7-
use UnexpectedValueException;
86

97
class PhpRedisLock extends RedisLock
108
{
9+
/**
10+
* The phpredis factory implementation.
11+
*
12+
* @var \Illuminate\Redis\Connections\PhpredisConnection
13+
*/
14+
protected $redis;
15+
1116
/**
1217
* Create a new phpredis lock instance.
1318
*
@@ -31,7 +36,7 @@ public function release()
3136
LuaScripts::releaseLock(),
3237
1,
3338
$this->name,
34-
$this->serializedAndCompressedOwner()
39+
...$this->redis->pack([$this->owner])
3540
);
3641
}
3742

@@ -41,72 +46,64 @@ public function release()
4146
* @return string
4247
*
4348
* @throws \UnexpectedValueException
49+
*
50+
* @deprecated Will be removed in a later laravel version. Use PhpRedisConnection::pack.
51+
* @see \Illuminate\Redis\Connections\PhpRedisConnection::pack
4452
*/
4553
protected function serializedAndCompressedOwner(): string
4654
{
47-
$client = $this->redis->client();
48-
49-
$owner = $client->_serialize($this->owner);
50-
51-
// https://github.com/phpredis/phpredis/issues/1938
52-
if ($this->compressed()) {
53-
if ($this->lzfCompressed()) {
54-
$owner = \lzf_compress($owner);
55-
} elseif ($this->zstdCompressed()) {
56-
$owner = \zstd_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
57-
} elseif ($this->lz4Compressed()) {
58-
$owner = \lz4_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
59-
} else {
60-
throw new UnexpectedValueException(sprintf(
61-
'Unknown phpredis compression in use [%d]. Unable to release lock.',
62-
$client->getOption(Redis::OPT_COMPRESSION)
63-
));
64-
}
65-
}
66-
67-
return $owner;
55+
return $this->redis->pack([$this->owner])[0];
6856
}
6957

7058
/**
7159
* Determine if compression is enabled.
7260
*
7361
* @return bool
62+
*
63+
* @deprecated Will be removed in a later laravel version. Use PhpRedisConnection::compressed.
64+
* @see \Illuminate\Redis\Connections\PhpRedisConnection::compressed
7465
*/
7566
protected function compressed(): bool
7667
{
77-
return $this->redis->client()->getOption(Redis::OPT_COMPRESSION) !== Redis::COMPRESSION_NONE;
68+
return $this->redis->compressed();
7869
}
7970

8071
/**
8172
* Determine if LZF compression is enabled.
8273
*
8374
* @return bool
75+
*
76+
* @deprecated Will be removed in a later laravel version. Use PhpRedisConnection::lzfCompressed.
77+
* @see \Illuminate\Redis\Connections\PhpRedisConnection::lzfCompressed
8478
*/
8579
protected function lzfCompressed(): bool
8680
{
87-
return defined('Redis::COMPRESSION_LZF') &&
88-
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZF;
81+
return $this->redis->lzfCompressed();
8982
}
9083

9184
/**
9285
* Determine if ZSTD compression is enabled.
9386
*
9487
* @return bool
88+
*
89+
* @deprecated Will be removed in a later laravel version. Use PhpRedisConnection::zstdCompressed.
90+
* @see \Illuminate\Redis\Connections\PhpRedisConnection::zstdCompressed
9591
*/
9692
protected function zstdCompressed(): bool
9793
{
98-
return defined('Redis::COMPRESSION_ZSTD') &&
99-
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_ZSTD;
94+
return $this->redis->zstdCompressed();
10095
}
10196

10297
/**
10398
* Determine if LZ4 compression is enabled.
10499
*
105100
* @return bool
101+
*
102+
* @deprecated Will be removed in a later laravel version. Use PhpRedisConnection::lz4Compressed.
103+
* @see \Illuminate\Redis\Connections\PhpRedisConnection::lz4Compressed
106104
*/
107105
protected function lz4Compressed(): bool
108106
{
109-
return defined('Redis::COMPRESSION_LZ4') &&
110-
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZ4;
107+
return $this->redis->lz4Compressed();
111108
}
112109
}

src/Illuminate/Redis/Connections/PhpRedisConnection.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Redis;
1010
use RedisCluster;
1111
use RedisException;
12+
use RuntimeException;
13+
use UnexpectedValueException;
1214

1315
/**
1416
* @mixin \Redis
@@ -573,4 +575,104 @@ public function __call($method, $parameters)
573575
{
574576
return parent::__call(strtolower($method), $parameters);
575577
}
578+
579+
/**
580+
* Prepares values to be used with e.g. the `eval` command, because the
581+
* phpredis extension does not do it for us. This includes serialization and
582+
* compression if any of those settings are configured.
583+
*
584+
* @param array<int|string,string> $values
585+
* @return array<int|string,string>
586+
*/
587+
public function pack(array $values): array
588+
{
589+
if (empty($values)) {
590+
return $values;
591+
}
592+
593+
$phpredisVersion = phpversion('redis');
594+
595+
if (version_compare($phpredisVersion, '5.3.5', '>=')) {
596+
return array_map([$this->client, '_pack'], $values);
597+
}
598+
599+
if ($this->compressed()) {
600+
if (version_compare($phpredisVersion, '4.3.0', '>=') && $this->lzfCompressed()) {
601+
if (! function_exists('lzf_compress')) {
602+
throw new RuntimeException("Missing 'lzf' extension to call 'lzf_compress'.");
603+
}
604+
605+
$processor = function ($value) {
606+
return \lzf_compress($this->client->_serialize($value));
607+
};
608+
} elseif (version_compare($phpredisVersion, '5.1.0', '>=') && $this->zstdCompressed()) {
609+
if (! function_exists('zstd_compress')) {
610+
throw new RuntimeException("Missing 'zstd' extension to call 'zstd_compress'.");
611+
}
612+
613+
$compressionLevel = $this->client->getOption(Redis::OPT_COMPRESSION_LEVEL);
614+
$processor = function ($value) use ($compressionLevel) {
615+
return \zstd_compress(
616+
$this->client->_serialize($value),
617+
$compressionLevel === 0 ? Redis::COMPRESSION_ZSTD_DEFAULT : $compressionLevel
618+
);
619+
};
620+
} else {
621+
throw new UnexpectedValueException(sprintf(
622+
'Not supported phpredis compression in use [%d].',
623+
$this->client->getOption(Redis::OPT_COMPRESSION)
624+
));
625+
}
626+
} else {
627+
$processor = function ($value) {
628+
return $this->client->_serialize($value);
629+
};
630+
}
631+
632+
return array_map($processor, $values);
633+
}
634+
635+
/**
636+
* Determine if compression is enabled.
637+
*
638+
* @return bool
639+
*/
640+
public function compressed(): bool
641+
{
642+
return defined('Redis::OPT_COMPRESSION') &&
643+
$this->client->getOption(Redis::OPT_COMPRESSION) !== Redis::COMPRESSION_NONE;
644+
}
645+
646+
/**
647+
* Determine if LZF compression is enabled.
648+
*
649+
* @return bool
650+
*/
651+
public function lzfCompressed(): bool
652+
{
653+
return defined('Redis::COMPRESSION_LZF') &&
654+
$this->client->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZF;
655+
}
656+
657+
/**
658+
* Determine if ZSTD compression is enabled.
659+
*
660+
* @return bool
661+
*/
662+
public function zstdCompressed(): bool
663+
{
664+
return defined('Redis::COMPRESSION_ZSTD') &&
665+
$this->client->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_ZSTD;
666+
}
667+
668+
/**
669+
* Determine if LZ4 compression is enabled.
670+
*
671+
* @return bool
672+
*/
673+
public function lz4Compressed(): bool
674+
{
675+
return defined('Redis::COMPRESSION_LZ4') &&
676+
$this->client->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZ4;
677+
}
576678
}

src/Illuminate/Redis/Connectors/PhpRedisConnector.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ protected function createClient(array $config)
106106
if (! empty($config['name'])) {
107107
$client->client('SETNAME', $config['name']);
108108
}
109+
110+
if (array_key_exists('serializer', $config)) {
111+
$client->setOption(Redis::OPT_SERIALIZER, $config['serializer']);
112+
}
113+
114+
if (array_key_exists('compression', $config)) {
115+
$client->setOption(Redis::OPT_COMPRESSION, $config['compression']);
116+
}
117+
118+
if (array_key_exists('compression_level', $config)) {
119+
$client->setOption(Redis::OPT_COMPRESSION_LEVEL, $config['compression_level']);
120+
}
109121
});
110122
}
111123

@@ -184,6 +196,18 @@ protected function createRedisClusterInstance(array $servers, array $options)
184196
if (! empty($options['name'])) {
185197
$client->client('SETNAME', $options['name']);
186198
}
199+
200+
if (array_key_exists('serializer', $options)) {
201+
$client->setOption(RedisCluster::OPT_SERIALIZER, $options['serializer']);
202+
}
203+
204+
if (array_key_exists('compression', $options)) {
205+
$client->setOption(RedisCluster::OPT_COMPRESSION, $options['compression']);
206+
}
207+
208+
if (array_key_exists('compression_level', $options)) {
209+
$client->setOption(RedisCluster::OPT_COMPRESSION_LEVEL, $options['compression_level']);
210+
}
187211
});
188212
}
189213

tests/Integration/Cache/PhpRedisCacheLockTest.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,6 @@ public function testRedisLockCanBeAcquiredAndReleasedWithLz4Compression()
224224
$this->markTestSkipped('Redis extension is not configured to support the lz4 compression.');
225225
}
226226

227-
$this->markTestIncomplete(
228-
'phpredis extension does not compress consistently with the php '.
229-
'extension lz4. See: https://github.com/phpredis/phpredis/issues/1939'
230-
);
231-
232227
$this->app['config']->set('database.redis.client', 'phpredis');
233228
$this->app['config']->set('cache.stores.redis.connection', 'default');
234229
$this->app['config']->set('cache.stores.redis.lock_connection', 'default');

0 commit comments

Comments
 (0)