From 591aba8a474702ac3e3d638aa68809e38230bd94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Till=20Kr=C3=BCss?= Date: Mon, 15 Jun 2020 11:37:34 -0700 Subject: [PATCH 01/50] add Credis_Client::VERSION constant (#136) --- Client.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Client.php b/Client.php index fbb7fe1..ca5b7c1 100755 --- a/Client.php +++ b/Client.php @@ -168,12 +168,15 @@ public function __construct($message, $code = 0, $exception = NULL) */ class Credis_Client { + const VERSION = '1.11.1'; + const TYPE_STRING = 'string'; const TYPE_LIST = 'list'; const TYPE_SET = 'set'; const TYPE_ZSET = 'zset'; const TYPE_HASH = 'hash'; const TYPE_NONE = 'none'; + const FREAD_BLOCK_SIZE = 8192; /** From 0895ca29552ea112b2f9ef489ed4aa8add7e4071 Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Mon, 15 Jun 2020 14:38:15 -0400 Subject: [PATCH 02/50] Update version constant for new tag. --- Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client.php b/Client.php index ca5b7c1..b2df84a 100755 --- a/Client.php +++ b/Client.php @@ -168,7 +168,7 @@ public function __construct($message, $code = 0, $exception = NULL) */ class Credis_Client { - const VERSION = '1.11.1'; + const VERSION = '1.11.2; const TYPE_STRING = 'string'; const TYPE_LIST = 'list'; From b8b2bd6b87d2d4df67065f3510efb80d5f9c4e53 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Mon, 15 Jun 2020 20:25:47 +0100 Subject: [PATCH 03/50] fix syntax error urgently (#138) closes #137 --- Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client.php b/Client.php index b2df84a..6583105 100755 --- a/Client.php +++ b/Client.php @@ -168,7 +168,7 @@ public function __construct($message, $code = 0, $exception = NULL) */ class Credis_Client { - const VERSION = '1.11.2; + const VERSION = '1.11.2'; const TYPE_STRING = 'string'; const TYPE_LIST = 'list'; From 0a28686879d0420747100baf64c13e16cdc1d9a9 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 16 Jun 2020 22:34:02 +0800 Subject: [PATCH 04/50] add php 7.4 to travis tests (#139) --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 97d413c..242a8b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,10 @@ matrix: env: - PHPREDIS_VERSION=redis-5.0.0 - PHPUNIT_VERSION=^7.5 + - php: 7.4 + env: + - PHPREDIS_VERSION=redis-5.0.0 + - PHPUNIT_VERSION=^7.5 install: - yes '' | pecl install -f $PHPREDIS_VERSION From e217dc3d5f035d360028fcb1d7911028fff8fdaf Mon Sep 17 00:00:00 2001 From: Charley Wu Date: Tue, 13 Oct 2020 04:34:53 +0800 Subject: [PATCH 05/50] Fixes #140: Update test suites (#142) * Update Dockerfile - Removed PHP 5.5 & Added PHP 7.4 - PHP Redis Extension 4.3.0 is the latest version to support PHP 5 - Update Redis to 6.0.8 and enable TLS support for PHP 7 * Update .travis.yml - Update php-redis to 5.3.0 - Update redis-server to 6.0.8 * Fixes test failures - Use [assertIsArray](https://github.com/sebastianbergmann/phpunit/issues/3368) method since PHPUnit v7.5 - Fixes AUTH error message changed since Redis v6 - `getMock()` is deprecated since PHPUnit v5.4 * Update Client.php - PHP Redis extension support TLS since [5.3.0](https://pecl.php.net/package/redis/5.3.0) - Fixes AUTH may not cleanup last error --- .travis.yml | 23 +++++++------ Client.php | 8 +++-- testenv/docker-compose.yml | 10 +++--- testenv/env/php-5.6/Dockerfile | 2 +- testenv/env/php-7.0/Dockerfile | 7 ++-- testenv/env/php-7.1/Dockerfile | 7 ++-- testenv/env/php-7.2/Dockerfile | 5 +-- testenv/env/php-7.3/Dockerfile | 5 +-- testenv/env/{php-5.5 => php-7.4}/Dockerfile | 9 ++--- tests/CredisClusterTest.php | 6 +++- tests/CredisSentinelTest.php | 38 +++++++++++++++++---- tests/CredisTest.php | 5 ++- tests/CredisTestCommon.php | 17 --------- 13 files changed, 81 insertions(+), 61 deletions(-) rename testenv/env/{php-5.5 => php-7.4}/Dockerfile (82%) diff --git a/.travis.yml b/.travis.yml index 242a8b5..8ebc667 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,31 +8,32 @@ matrix: - PHPUNIT_VERSION=^5.7 - php: 7.0 env: - - PHPREDIS_VERSION=redis-5.0.0 - - PHPUNIT_VERSION=^6.4 + - PHPREDIS_VERSION=redis-5.3.0 + - PHPUNIT_VERSION=^6.5 - php: 7.1 env: - - PHPREDIS_VERSION=redis-5.0.0 - - PHPUNIT_VERSION=^6.4 + - PHPREDIS_VERSION=redis-5.3.0 + - PHPUNIT_VERSION=^7.5 - php: 7.2 env: - - PHPREDIS_VERSION=redis-5.0.0 + - PHPREDIS_VERSION=redis-5.3.0 - PHPUNIT_VERSION=^7.5 - php: 7.3 env: - - PHPREDIS_VERSION=redis-5.0.0 + - PHPREDIS_VERSION=redis-5.3.0 - PHPUNIT_VERSION=^7.5 - php: 7.4 env: - - PHPREDIS_VERSION=redis-5.0.0 + - PHPREDIS_VERSION=redis-5.3.0 - PHPUNIT_VERSION=^7.5 install: - yes '' | pecl install -f $PHPREDIS_VERSION - - wget http://download.redis.io/releases/redis-5.0.5.tar.gz - - tar -xzf redis-5.0.5.tar.gz - - make -s -C redis-5.0.5 -j4 - - export PATH=$PATH:$PWD/redis-5.0.5/src/ + - wget http://download.redis.io/releases/redis-6.0.8.tar.gz + - tar -xzf redis-6.0.8.tar.gz + - export BUILD_TLS=yes + - make -s -C redis-6.0.8 -j4 + - export PATH=$PWD/redis-6.0.8/src/:$PATH - | if [ ! -z "$PHPUNIT_VERSION" ]; then composer require "phpunit/phpunit:${PHPUNIT_VERSION}" --dev --no-update -n diff --git a/Client.php b/Client.php index 6583105..e01f423 100755 --- a/Client.php +++ b/Client.php @@ -326,8 +326,8 @@ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $this->authPassword = $password; $this->selectedDb = (int)$db; $this->convertHost(); - if ($this->scheme == 'tls') { - // PHP Redis extension doesn't work with TLS + // PHP Redis extension support TLS since 5.3.0 + if ($this->scheme == 'tls' && !$this->standalone && version_compare(phpversion('redis'),'5.3.0','<')){ $this->standalone = true; } } @@ -1231,6 +1231,10 @@ public function __call($name, $args) } } break; + case 'auth': + if (is_bool($response) && $response === true){ + $this->redis->clearLastError(); + } default: $error = $this->redis->getLastError(); $this->redis->clearLastError(); diff --git a/testenv/docker-compose.yml b/testenv/docker-compose.yml index ba320d1..c527d8f 100644 --- a/testenv/docker-compose.yml +++ b/testenv/docker-compose.yml @@ -1,11 +1,6 @@ version: '2' services: - php-55: - build: env/php-5.5/ - volumes: - - ../:/src/ - php-56: build: env/php-5.6/ volumes: @@ -30,3 +25,8 @@ services: build: env/php-7.3/ volumes: - ../:/src/ + + php-74: + build: env/php-7.4/ + volumes: + - ../:/src/ diff --git a/testenv/env/php-5.6/Dockerfile b/testenv/env/php-5.6/Dockerfile index 19b96cd..c6378a4 100644 --- a/testenv/env/php-5.6/Dockerfile +++ b/testenv/env/php-5.6/Dockerfile @@ -10,7 +10,7 @@ RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit # install php extension -RUN yes '' | pecl install -f redis && \ +RUN yes '' | pecl install -f redis-4.3.0 && \ docker-php-ext-enable redis # install redis server diff --git a/testenv/env/php-7.0/Dockerfile b/testenv/env/php-7.0/Dockerfile index c9616bc..82dfef9 100644 --- a/testenv/env/php-7.0/Dockerfile +++ b/testenv/env/php-7.0/Dockerfile @@ -1,9 +1,9 @@ FROM php:7.0 -ENV phpunit_verison 6.4 -ENV redis_version 4.0.11 +ENV phpunit_verison 6.5 +ENV redis_version 6.0.8 RUN apt-get update && \ - apt-get install -y wget + apt-get install -y wget libssl-dev RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ chmod +x phpunit-${phpunit_verison}.phar && \ @@ -16,6 +16,7 @@ RUN yes '' | pecl install -f redis && \ # install redis server RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ tar -xzf redis-${redis_version}.tar.gz && \ + export BUILD_TLS=yes && \ make -s -C redis-${redis_version} -j CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ diff --git a/testenv/env/php-7.1/Dockerfile b/testenv/env/php-7.1/Dockerfile index 871adf7..e75a4c0 100644 --- a/testenv/env/php-7.1/Dockerfile +++ b/testenv/env/php-7.1/Dockerfile @@ -1,9 +1,9 @@ FROM php:7.1 -ENV phpunit_verison 6.4 -ENV redis_version 4.0.11 +ENV phpunit_verison 7.5 +ENV redis_version 6.0.8 RUN apt-get update && \ - apt-get install -y wget + apt-get install -y wget libssl-dev RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ chmod +x phpunit-${phpunit_verison}.phar && \ @@ -16,6 +16,7 @@ RUN yes '' | pecl install -f redis && \ # install redis server RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ tar -xzf redis-${redis_version}.tar.gz && \ + export BUILD_TLS=yes && \ make -s -C redis-${redis_version} -j CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ diff --git a/testenv/env/php-7.2/Dockerfile b/testenv/env/php-7.2/Dockerfile index 0ce06af..0e34aab 100644 --- a/testenv/env/php-7.2/Dockerfile +++ b/testenv/env/php-7.2/Dockerfile @@ -1,9 +1,9 @@ FROM php:7.2 ENV phpunit_verison 7.5 -ENV redis_version 4.0.11 +ENV redis_version 6.0.8 RUN apt-get update && \ - apt-get install -y wget + apt-get install -y wget libssl-dev RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ chmod +x phpunit-${phpunit_verison}.phar && \ @@ -16,6 +16,7 @@ RUN yes '' | pecl install -f redis && \ # install redis server RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ tar -xzf redis-${redis_version}.tar.gz && \ + export BUILD_TLS=yes && \ make -s -C redis-${redis_version} -j CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ diff --git a/testenv/env/php-7.3/Dockerfile b/testenv/env/php-7.3/Dockerfile index 834df7b..279ffa9 100644 --- a/testenv/env/php-7.3/Dockerfile +++ b/testenv/env/php-7.3/Dockerfile @@ -1,9 +1,9 @@ FROM php:7.3 ENV phpunit_verison 7.5 -ENV redis_version 4.0.11 +ENV redis_version 6.0.8 RUN apt-get update && \ - apt-get install -y wget + apt-get install -y wget libssl-dev RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ chmod +x phpunit-${phpunit_verison}.phar && \ @@ -16,6 +16,7 @@ RUN yes '' | pecl install -f redis && \ # install redis server RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ tar -xzf redis-${redis_version}.tar.gz && \ + export BUILD_TLS=yes && \ make -s -C redis-${redis_version} -j CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ diff --git a/testenv/env/php-5.5/Dockerfile b/testenv/env/php-7.4/Dockerfile similarity index 82% rename from testenv/env/php-5.5/Dockerfile rename to testenv/env/php-7.4/Dockerfile index 87dfd27..5de537e 100644 --- a/testenv/env/php-5.5/Dockerfile +++ b/testenv/env/php-7.4/Dockerfile @@ -1,9 +1,9 @@ -FROM php:5.5 -ENV phpunit_verison 4.8 -ENV redis_version 4.0.11 +FROM php:7.4 +ENV phpunit_verison 7.5 +ENV redis_version 6.0.8 RUN apt-get update && \ - apt-get install -y wget + apt-get install -y wget libssl-dev RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ chmod +x phpunit-${phpunit_verison}.phar && \ @@ -16,6 +16,7 @@ RUN yes '' | pecl install -f redis && \ # install redis server RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ tar -xzf redis-${redis_version}.tar.gz && \ + export BUILD_TLS=yes && \ make -s -C redis-${redis_version} -j CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ diff --git a/tests/CredisClusterTest.php b/tests/CredisClusterTest.php index 311ce5f..8a673ec 100644 --- a/tests/CredisClusterTest.php +++ b/tests/CredisClusterTest.php @@ -106,7 +106,11 @@ public function testMasterWithoutSlavesAndWriteOnlyFlag() } public function testDontHashForCodeCoverage() { - $this->assertInternalType('array',$this->cluster->info()); + if (method_exists($this,'assertIsArray')){ + $this->assertIsArray($this->cluster->info()); + } else { + $this->assertInternalType('array',$this->cluster->info()); + } } public function testByHash() { diff --git a/tests/CredisSentinelTest.php b/tests/CredisSentinelTest.php index 29206f9..f596140 100644 --- a/tests/CredisSentinelTest.php +++ b/tests/CredisSentinelTest.php @@ -76,7 +76,11 @@ public function testMasterClient() public function testMasters() { $masters = $this->sentinel->masters(); - $this->assertInternalType('array',$masters); + if (method_exists($this,'assertIsArray')){ + $this->assertIsArray($masters); + } else { + $this->assertInternalType('array',$masters); + } $this->assertCount(2,$masters); $this->assertArrayHasKey(0,$masters); $this->assertArrayHasKey(1,$masters); @@ -95,7 +99,11 @@ public function testMasters() public function testMaster() { $master = $this->sentinel->master($this->sentinelConfig->clustername); - $this->assertInternalType('array',$master); + if (method_exists($this,'assertIsArray')){ + $this->assertIsArray($master); + } else { + $this->assertInternalType('array',$master); + } $this->assertArrayHasKey(1,$master); $this->assertArrayHasKey(5,$master); $this->assertEquals($this->sentinelConfig->clustername,$master[1]); @@ -107,7 +115,11 @@ public function testMaster() public function testSlaveClient() { $slaves = $this->sentinel->getSlaveClients($this->sentinelConfig->clustername); - $this->assertInternalType('array',$slaves); + if (method_exists($this,'assertIsArray')){ + $this->assertIsArray($slaves); + } else { + $this->assertInternalType('array',$slaves); + } $this->assertCount(1,$slaves); foreach($slaves as $slave){ $this->assertInstanceOf('Credis_Client',$slave); @@ -118,14 +130,22 @@ public function testSlaveClient() public function testSlaves() { $slaves = $this->sentinel->slaves($this->sentinelConfig->clustername); - $this->assertInternalType('array',$slaves); + if (method_exists($this,'assertIsArray')){ + $this->assertIsArray($slaves); + } else { + $this->assertInternalType('array',$slaves); + } $this->assertCount(1,$slaves); $this->assertArrayHasKey(0,$slaves); $this->assertArrayHasKey(5,$slaves[0]); $this->assertEquals(6385,$slaves[0][5]); $slaves = $this->sentinel->slaves('masterdown'); - $this->assertInternalType('array',$slaves); + if (method_exists($this,'assertIsArray')){ + $this->assertIsArray($slaves); + } else { + $this->assertInternalType('array',$slaves); + } $this->assertCount(0,$slaves); $this->setExpectedExceptionShim('CredisException','No such master with that name'); @@ -165,7 +185,11 @@ public function testGetClusterOnDbNumber2() public function testGetMasterAddressByName() { $address = $this->sentinel->getMasterAddressByName($this->sentinelConfig->clustername); - $this->assertInternalType('array',$address); + if (method_exists($this,'assertIsArray')){ + $this->assertIsArray($address); + } else { + $this->assertInternalType('array',$address); + } $this->assertCount(2,$address); $this->assertArrayHasKey(0,$address); $this->assertArrayHasKey(1,$address); @@ -184,7 +208,7 @@ public function testGetHostAndPort() $host = 'localhost'; $port = '123456'; - $client = $this->createMockShim('\Credis_Client'); + $client = $this->createMock('\Credis_Client'); $sentinel = new Credis_Sentinel($client); $client->expects($this->once())->method('getHost')->willReturn($host); diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 7169769..ccd8a84 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -578,7 +578,7 @@ public function testPassword() } catch(CredisException $e) { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); $this->credis->close(); } $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0); @@ -592,7 +592,6 @@ public function testPassword() catch(CredisException $e) { $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); - } try { @@ -600,7 +599,7 @@ public function testPassword() } catch(CredisException $e) { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); } $this->assertTrue($this->credis->auth('thepassword')); $this->assertTrue($this->credis->set('key','value')); diff --git a/tests/CredisTestCommon.php b/tests/CredisTestCommon.php index af6a811..8e07df1 100644 --- a/tests/CredisTestCommon.php +++ b/tests/CredisTestCommon.php @@ -149,23 +149,6 @@ public static function tearDownAfterClass() } } - - // - /** - * php 7.2 compat fix, as directly polyfilling for older PHPUnit causes a function signature compatibility issue - * This is due to the defined return type - * - * Polyfill for older PHPUnit - */ - protected function createMockShim($originalClassName) - { - if (method_exists($this, 'getMock')) { - return $this->getMock($originalClassName); - } else { - return parent::createMock($originalClassName); - } - } - /** * php 7.2 compat fix, as directly polyfilling for older PHPUnit causes a function signature compatibility issue * This is due to the defined return type From dc200278d644be1de8c99a17462aa9ef70cedc89 Mon Sep 17 00:00:00 2001 From: Charley Wu Date: Tue, 13 Oct 2020 04:38:47 +0800 Subject: [PATCH 06/50] Fixes connectionStrings parsing (#141) * travis has built-in redis-server 6.0.5 - make downloaded redis-server PATH priority higher then built-in one * Fixes connectionStrings parsing - Fixes port always be 6379 even no port specified in host string and specified the port number other then 6379 in constructor - Fixes persistent too * Added test cases - Code Cleanup Co-authored-by: Colin Mollenhour --- Client.php | 4 ++-- tests/CredisTest.php | 36 ++++++++++++------------------------ 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/Client.php b/Client.php index e01f423..5bbb6f7 100755 --- a/Client.php +++ b/Client.php @@ -423,8 +423,8 @@ protected function convertHost() throw new CredisException('Invalid host format; expected '.$this->scheme.'://host[:port][/persistence_identifier]'); } $this->host = $matches[1]; - $this->port = (int) (isset($matches[3]) ? $matches[3] : 6379); - $this->persistent = isset($matches[5]) ? $matches[5] : ''; + $this->port = (int) (isset($matches[3]) ? $matches[3] : $this->port); + $this->persistent = isset($matches[5]) ? $matches[5] : $this->persistent; } else { $this->host = $matches[2]; $this->port = NULL; diff --git a/tests/CredisTest.php b/tests/CredisTest.php index ccd8a84..579c5bf 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -631,44 +631,32 @@ public function testGettersAndSetters() public function testConnectionStrings() { - $this->credis->close(); $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host']); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getPort(),6379); $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertEquals('abc123',$this->credis->getPersistence()); + $this->assertEquals($this->credis->getPersistence(),'abc123'); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'],6380); + $this->assertEquals($this->credis->getPort(),6380); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'],NULL,NULL,"abc123"); + $this->assertEquals($this->credis->getPersistence(),'abc123'); } public function testConnectionStringsTls() { - $this->credis->close(); $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host']); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getPort(),6379); $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertEquals('abc123',$this->credis->getPersistence()); + $this->assertEquals($this->credis->getPersistence(),'abc123'); + $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'],6380); + $this->assertEquals($this->credis->getPort(),6380); + $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'],NULL,NULL,"abc123"); + $this->assertEquals($this->credis->getPersistence(),'abc123'); } /** From b458b7c65d156744f5f0c4667c0f8ce45d955435 Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Tue, 13 Oct 2020 19:55:13 -0400 Subject: [PATCH 07/50] Update VERSION constant --- Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client.php b/Client.php index 5bbb6f7..75b4066 100755 --- a/Client.php +++ b/Client.php @@ -168,7 +168,7 @@ public function __construct($message, $code = 0, $exception = NULL) */ class Credis_Client { - const VERSION = '1.11.2'; + const VERSION = '1.11.4'; const TYPE_STRING = 'string'; const TYPE_LIST = 'list'; From efdf60db997e8f4330b24cfb7e5c5dc0bd612a48 Mon Sep 17 00:00:00 2001 From: Zhangchuang Date: Tue, 3 Nov 2020 01:20:09 +0800 Subject: [PATCH 08/50] -add master only mode (#145) Co-authored-by: Chuang Zhang --- Sentinel.php | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/Sentinel.php b/Sentinel.php index 4064001..a3b84af 100644 --- a/Sentinel.php +++ b/Sentinel.php @@ -200,17 +200,18 @@ public function getSlaveClients($name) * When $selectRandomSlave is true, only one random slave is passed. * When $selectRandomSlave is false, all clients are passed and hashing is applied in Credis_Cluster * When $writeOnly is false, the master server will also be used for read commands. - * + * When $masterOnly is true, only the master server will also be used for both read and write commands. $writeOnly will be ignored and forced to set to false. * @param string $name * @param int $db * @param int $replicas * @param bool $selectRandomSlave * @param bool $writeOnly + * @param bool $masterOnly * @return Credis_Cluster * @throws CredisException * @deprecated */ - public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false) + public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false, $masterOnly=false) { $clients = array(); $workingClients = array(); @@ -218,21 +219,25 @@ public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=tr if(strstr($master[9],'s_down') || strstr($master[9],'disconnected')) { throw new CredisException('The master is down'); } - $slaves = $this->slaves($name); - foreach($slaves as $slave){ - if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) { - $workingClients[] = array('host'=>$slave[3],'port'=>$slave[5],'master'=>false,'db'=>$db,'password'=>$this->_password); + if (!$masterOnly) { + $slaves = $this->slaves($name); + foreach($slaves as $slave){ + if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) { + $workingClients[] = array('host'=>$slave[3],'port'=>$slave[5],'master'=>false,'db'=>$db,'password'=>$this->_password); + } } - } - if(count($workingClients)>0){ - if($selectRandomSlave){ - if(!$writeOnly){ - $workingClients[] = array('host'=>$master[3],'port'=>$master[5],'master'=>false,'db'=>$db,'password'=>$this->_password); + if(count($workingClients)>0){ + if($selectRandomSlave){ + if(!$writeOnly){ + $workingClients[] = array('host'=>$master[3],'port'=>$master[5],'master'=>false,'db'=>$db,'password'=>$this->_password); + } + $clients[] = $workingClients[rand(0,count($workingClients)-1)]; + } else { + $clients = $workingClients; } - $clients[] = $workingClients[rand(0,count($workingClients)-1)]; - } else { - $clients = $workingClients; } + } else { + $writeOnly = false; } $clients[] = array('host'=>$master[3],'port'=>$master[5], 'db'=>$db ,'master'=>true,'write_only'=>$writeOnly,'password'=>$this->_password); return new Credis_Cluster($clients,$replicas,$this->_standAlone); From c27faa11724229986335c23f4b6d0f1d8d6547fb Mon Sep 17 00:00:00 2001 From: Zhangchuang Date: Sat, 7 Nov 2020 00:09:14 +0800 Subject: [PATCH 09/50] -add masterOnly parameter in getCluster fuction (#146) Co-authored-by: Chuang Zhang --- Sentinel.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sentinel.php b/Sentinel.php index a3b84af..5a7c2a4 100644 --- a/Sentinel.php +++ b/Sentinel.php @@ -250,13 +250,15 @@ public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=tr * @param int $replicas * @param bool $selectRandomSlave * @param bool $writeOnly + * @param bool $masterOnly * @return Credis_Cluster + * @throws CredisException * @deprecated */ - public function getCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false) + public function getCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false, $masterOnly=false) { if(!isset($this->_cluster[$name])){ - $this->_cluster[$name] = $this->createCluster($name, $db, $replicas, $selectRandomSlave, $writeOnly); + $this->_cluster[$name] = $this->createCluster($name, $db, $replicas, $selectRandomSlave, $writeOnly, $masterOnly); } return $this->_cluster[$name]; } From ff0e98f7ace03add94702f24c865e8dea71c00d7 Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 12 Dec 2020 10:55:09 +0800 Subject: [PATCH 10/50] Add method definition for dbsize() (#147) --- Client.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Client.php b/Client.php index 75b4066..311a08f 100755 --- a/Client.php +++ b/Client.php @@ -56,6 +56,7 @@ public function __construct($message, $code = 0, $exception = NULL) * @method bool|array|Credis_Client config(string $setGet, string $key, string $value = null) * @method array|Credis_Client role() * @method array|Credis_Client time() + * @method int|Credis_Client dbsize() * * Keys: * @method int|Credis_Client del(string $key) From e63f0b0d374362277f4a65a88b5aacd4087f8af6 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 25 May 2021 23:24:33 +0800 Subject: [PATCH 11/50] Add method definition for dbsize() (#148) From 464804e67d0fe5707c9035c986f3850f83d9d177 Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 5 Jun 2021 07:29:41 +0800 Subject: [PATCH 12/50] Add "rawCommand" command (#150) - Allows arbitrary redis commands in standalone mode or with php-redis extension active --- Client.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Client.php b/Client.php index 311a08f..f4389df 100755 --- a/Client.php +++ b/Client.php @@ -809,6 +809,25 @@ public function ping($name = null) return $this->__call('ping', $name ? array($name) : array()); } + /** + * @param string $command + * @param array $args + * + * @return array|Credis_Client + */ + public function rawCommand($command, array $args) + { + if($this->standalone) + { + return $this->__call($command, $args); + } + else + { + \array_unshift($args, $command); + return $this->__call('rawCommand', $args); + } + } + public function __call($name, $args) { // Lazy connection From 2b58303e21ce2d4e0a69f6031a3a3ab6c0b03eee Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Tue, 14 Sep 2021 16:50:26 -0400 Subject: [PATCH 13/50] Update build badge to use travis-ci.com --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 2e3bdc1..aa04c17 100644 --- a/README.markdown +++ b/README.markdown @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/colinmollenhour/credis.svg?branch=master)](https://travis-ci.org/colinmollenhour/credis) +[![Build Status](https://app.travis-ci.com/colinmollenhour/credis.svg?branch=master)](https://travis-ci.org/colinmollenhour/credis) # Credis From ff579f100d0f69ae268754b239d2cf01a7ae6f2a Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 4 Dec 2021 15:16:30 +0800 Subject: [PATCH 14/50] Instruct composer to ignore exporting various directories --- .gitattributes | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9f9a0c8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +/tests export-ignore +/testenv export-ignore +/.github export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +/composer.lock export-ignore +/phpunit.xml export-ignore From 9c3e7be962e01b4972f6e676bdb631c8e4f53f08 Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 4 Dec 2021 15:40:31 +0800 Subject: [PATCH 15/50] Use github actions not travis --- .gitattributes | 2 +- .github/workflows/ci.yml | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml diff --git a/.gitattributes b/.gitattributes index 9f9a0c8..e5421a9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ /tests export-ignore -/testenv export-ignore +/testend export-ignore /.github export-ignore /.gitattributes export-ignore /.gitignore export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0688d11 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI +on: [push, pull_request] +jobs: + all: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - php-version: '5.6' + phpunit: '^5.7' + - php-version: '7.0' + phpunit: '^6.5' + - php-version: '7.1' + phpunit: '^7.5' + - php-version: '7.2' + phpunit: '^7.5' + - php-version: '7.3' + phpunit: '^7.5' + - php-version: '7.4' + phpunit: '^7.5' + - php-version: '8.0' + phpunit: '^8' + - php-version: '8.1' + phpunit: '^8' + name: PHP ${{ matrix.php-version }} + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: redis + + - name: Setup Redis + run: sudo apt install redis -y ; sudo systemctl stop redis\* ; redis-cli --version + + - name: Install phpunit + env: + PHPUNIT_VERSION: ${{ matrix.phpunit }} + run: | + if [ ! -z "$PHPUNIT_VERSION" ]; then + composer require "phpunit/phpunit:${PHPUNIT_VERSION}" --dev --no-update -n + composer install --dev -n + fi + + - name: Unit tests + run: ./vendor/bin/phpunit \ No newline at end of file From 8d9dcba3de4b2488813c20166701f9d707bab64d Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 4 Dec 2021 16:18:36 +0800 Subject: [PATCH 16/50] Add build status --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index aa04c17..bf1c8e9 100644 --- a/README.markdown +++ b/README.markdown @@ -1,4 +1,4 @@ -[![Build Status](https://app.travis-ci.com/colinmollenhour/credis.svg?branch=master)](https://travis-ci.org/colinmollenhour/credis) +![Build Status](https://github.com/colinmollenhour/credis/actions/workflows/ci.yml/badge.svg) # Credis From 343bb08a907aa07f736b35ea3591fdce51fa0fbe Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 4 Dec 2021 16:20:09 +0800 Subject: [PATCH 17/50] Remove .travis.yml --- .travis.yml | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8ebc667..0000000 --- a/.travis.yml +++ /dev/null @@ -1,44 +0,0 @@ -language: php - -matrix: - include: - - php: 5.6 - env: - - PHPREDIS_VERSION=redis-4.3.0 - - PHPUNIT_VERSION=^5.7 - - php: 7.0 - env: - - PHPREDIS_VERSION=redis-5.3.0 - - PHPUNIT_VERSION=^6.5 - - php: 7.1 - env: - - PHPREDIS_VERSION=redis-5.3.0 - - PHPUNIT_VERSION=^7.5 - - php: 7.2 - env: - - PHPREDIS_VERSION=redis-5.3.0 - - PHPUNIT_VERSION=^7.5 - - php: 7.3 - env: - - PHPREDIS_VERSION=redis-5.3.0 - - PHPUNIT_VERSION=^7.5 - - php: 7.4 - env: - - PHPREDIS_VERSION=redis-5.3.0 - - PHPUNIT_VERSION=^7.5 - -install: - - yes '' | pecl install -f $PHPREDIS_VERSION - - wget http://download.redis.io/releases/redis-6.0.8.tar.gz - - tar -xzf redis-6.0.8.tar.gz - - export BUILD_TLS=yes - - make -s -C redis-6.0.8 -j4 - - export PATH=$PWD/redis-6.0.8/src/:$PATH - - | - if [ ! -z "$PHPUNIT_VERSION" ]; then - composer require "phpunit/phpunit:${PHPUNIT_VERSION}" --dev --no-update -n - composer install --dev -n - fi - -script: - - vendor/bin/phpunit From 9bc4f425561efd78a87bc9286231bca44edb908d Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 4 Dec 2021 16:44:57 +0800 Subject: [PATCH 18/50] Use phpunit9 for php +8 while supporting php7.x still --- .github/workflows/ci.yml | 4 ++-- tests/CredisClusterTest.php | 6 +++--- tests/CredisSentinelTest.php | 14 ++++++------- tests/CredisStandaloneClusterTest.php | 2 +- tests/CredisTest.php | 6 +++--- tests/CredisTestCommon.php | 14 +++++++++---- tests/phpunit-shims/php7.php | 28 ++++++++++++++++++++++++++ tests/phpunit-shims/php8.php | 29 +++++++++++++++++++++++++++ 8 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 tests/phpunit-shims/php7.php create mode 100644 tests/phpunit-shims/php8.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0688d11..1eaccff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,9 +20,9 @@ jobs: - php-version: '7.4' phpunit: '^7.5' - php-version: '8.0' - phpunit: '^8' + phpunit: '^9' - php-version: '8.1' - phpunit: '^8' + phpunit: '^9' name: PHP ${{ matrix.php-version }} steps: - name: Checkout diff --git a/tests/CredisClusterTest.php b/tests/CredisClusterTest.php index 8a673ec..eeb1f52 100644 --- a/tests/CredisClusterTest.php +++ b/tests/CredisClusterTest.php @@ -9,15 +9,15 @@ class CredisClusterTest extends CredisTestCommon /** @var Credis_Cluster */ protected $cluster; - protected function setUp() + protected function setUpInternal() { - parent::setUp(); + parent::setUpInternal(); $clients = array_slice($this->redisConfig,0,4); $this->cluster = new Credis_Cluster($clients,2,$this->useStandalone); } - protected function tearDown() + protected function tearDownInternal() { if($this->cluster) { $this->cluster->flushAll(); diff --git a/tests/CredisSentinelTest.php b/tests/CredisSentinelTest.php index f596140..4944f7b 100644 --- a/tests/CredisSentinelTest.php +++ b/tests/CredisSentinelTest.php @@ -12,9 +12,9 @@ class CredisSentinelTest extends CredisTestCommon protected $sentinelConfig; - protected function setUp() + protected function setUpInternal() { - parent::setUp(); + parent::setUpInternal(); if($this->sentinelConfig === NULL) { $configFile = dirname(__FILE__).'/sentinel_config.json'; if( ! file_exists($configFile) || ! ($config = file_get_contents($configFile))) { @@ -32,9 +32,9 @@ protected function setUp() $this->waitForSlaveReplication(); } - public static function setUpBeforeClass() + public static function setUpBeforeClassInternal() { - parent::setUpBeforeClass(); + parent::setUpBeforeClassInternal(); if(preg_match('/^WIN/',strtoupper(PHP_OS))){ echo "\tredis-server redis-sentinel.conf --sentinel".PHP_EOL.PHP_EOL; } else { @@ -47,9 +47,9 @@ public static function setUpBeforeClass() } } - public static function tearDownAfterClass() + public static function tearDownAfterClassInternal() { - parent::tearDownAfterClass(); + parent::tearDownAfterClassInternal(); if(preg_match('/^WIN/',strtoupper(PHP_OS))){ echo "Please kill all Redis instances manually:".PHP_EOL; } else { @@ -59,7 +59,7 @@ public static function tearDownAfterClass() } } - protected function tearDown() + protected function tearDownInternal() { if($this->sentinel) { $this->sentinel = NULL; diff --git a/tests/CredisStandaloneClusterTest.php b/tests/CredisStandaloneClusterTest.php index 400e7d6..f3a6915 100644 --- a/tests/CredisStandaloneClusterTest.php +++ b/tests/CredisStandaloneClusterTest.php @@ -5,7 +5,7 @@ class CredisStandaloneClusterTest extends CredisClusterTest { protected $useStandalone = TRUE; - protected function tearDown() + protected function tearDownInternal() { if($this->cluster) { foreach($this->cluster->clients() as $client){ diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 579c5bf..cfaafe3 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -8,16 +8,16 @@ class CredisTest extends CredisTestCommon /** @var Credis_Client */ protected $credis; - protected function setUp() + protected function setUpInternal() { - parent::setUp(); + parent::setUpInternal(); $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout']); if($this->useStandalone) { $this->credis->forceStandalone(); } $this->credis->flushDb(); } - protected function tearDown() + protected function tearDownInternal() { if($this->credis) { $this->credis->close(); diff --git a/tests/CredisTestCommon.php b/tests/CredisTestCommon.php index 8e07df1..fe4d6c1 100644 --- a/tests/CredisTestCommon.php +++ b/tests/CredisTestCommon.php @@ -4,13 +4,19 @@ class_alias('\PHPUnit_Framework_TestCase', '\PHPUnit\Framework\TestCase'); } -class CredisTestCommon extends \PHPUnit\Framework\TestCase +if (version_compare(phpversion(), '8.0.0', '>=')) { + include 'phpunit-shims/php8.php'; +} else { + include 'phpunit-shims/php7.php'; +} + +class CredisTestCommon extends CredisTestCommonShim { protected $useStandalone = false; protected $redisConfig = null; protected $slaveConfig = null; - protected function setUp() + protected function setUpInternal() { if ($this->redisConfig === null) { @@ -95,7 +101,7 @@ protected function waitForSlaveReplication() return false; } - public static function setUpBeforeClass() + public static function setUpBeforeClassInternal() { if(preg_match('/^WIN/',strtoupper(PHP_OS))){ echo "Unit tests will not work automatically on Windows. Please setup all Redis instances manually:".PHP_EOL; @@ -122,7 +128,7 @@ public static function setUpBeforeClass() } } - public static function tearDownAfterClass() + public static function tearDownAfterClassInternal() { if(preg_match('/^WIN/',strtoupper(PHP_OS))){ echo "Please kill all Redis instances manually:".PHP_EOL; diff --git a/tests/phpunit-shims/php7.php b/tests/phpunit-shims/php7.php new file mode 100644 index 0000000..d364f32 --- /dev/null +++ b/tests/phpunit-shims/php7.php @@ -0,0 +1,28 @@ +setUpInternal(); + } + protected function tearDown() + { + $this->tearDownInternal(); + } + + public static function setUpBeforeClass() + { + static::setUpBeforeClassInternal(); + } + + public static function tearDownAfterClass() + { + static::tearDownAfterClassInternal(); + } +} \ No newline at end of file diff --git a/tests/phpunit-shims/php8.php b/tests/phpunit-shims/php8.php new file mode 100644 index 0000000..2034829 --- /dev/null +++ b/tests/phpunit-shims/php8.php @@ -0,0 +1,29 @@ +setUpInternal(); + } + protected function tearDown(): void + { + $this->tearDownInternal(); + } + + public static function setUpBeforeClass(): void + { + static::setUpBeforeClassInternal(); + } + + public static function tearDownAfterClass(): void + { + static::tearDownAfterClassInternal(); + } +} \ No newline at end of file From 52c25b499d761c212e2a91809110b5f13562b51e Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 4 Dec 2021 16:07:49 +0800 Subject: [PATCH 19/50] Fix test for invalid username/password check for redis 6.1+ --- tests/CredisTest.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/CredisTest.php b/tests/CredisTest.php index cfaafe3..c8fbdf3 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -578,7 +578,15 @@ public function testPassword() } catch(CredisException $e) { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + if (strpos($e->getMessage(), 'username') !== false) + { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + $this->credis->close(); } $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0); @@ -599,7 +607,14 @@ public function testPassword() } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) + { $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } } $this->assertTrue($this->credis->auth('thepassword')); $this->assertTrue($this->credis->set('key','value')); From 4cd73f9304aea4b2d5ef57d19b3be360fba7fe65 Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 4 Dec 2021 16:49:08 +0800 Subject: [PATCH 20/50] Redis Sentinel changes it's error message on non-existent method --- tests/CredisSentinelTest.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/CredisSentinelTest.php b/tests/CredisSentinelTest.php index 4944f7b..eca6ef9 100644 --- a/tests/CredisSentinelTest.php +++ b/tests/CredisSentinelTest.php @@ -219,7 +219,20 @@ public function testGetHostAndPort() } public function testNonExistingMethod() { - $this->setExpectedExceptionShim('CredisException','Unknown sentinel subcommand \'bla\''); - $this->sentinel->bla(); + try + { + $this->sentinel->bla(); + } + catch(CredisException $e) + { + if (strpos($e->getMessage(), 'sentinel subcommand') !== false) + { + $this->assertStringStartsWith('Unknown sentinel subcommand \'bla\'', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR Unknown subcommand or wrong number of arguments', $e->getMessage()); + } + } } } From b4ca8404b8d40608f2bf28637ab9c78a61a8ffcf Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Thu, 9 Dec 2021 13:57:17 -0500 Subject: [PATCH 21/50] Fix broken Sentinel test. --- tests/CredisSentinelTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CredisSentinelTest.php b/tests/CredisSentinelTest.php index eca6ef9..50d532f 100644 --- a/tests/CredisSentinelTest.php +++ b/tests/CredisSentinelTest.php @@ -227,7 +227,7 @@ public function testNonExistingMethod() { if (strpos($e->getMessage(), 'sentinel subcommand') !== false) { - $this->assertStringStartsWith('Unknown sentinel subcommand \'bla\'', $e->getMessage()); + $this->assertStringStartsWith('ERR Unknown sentinel subcommand \'bla\'', $e->getMessage()); } else { From 9c5a77d156fc670c25384d85f84d97977be6f56c Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 8 Mar 2022 07:54:44 +0800 Subject: [PATCH 22/50] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c82fb83..06082ab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ vendor phpunit.phar phpunit_*.log +/.phpunit.result.cache From 49e1f7d66a6a835bc58b8b7e31898101b8051be9 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 8 Mar 2022 08:10:59 +0800 Subject: [PATCH 23/50] This test doesn't actually do anything --- tests/CredisStandaloneTest.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/CredisStandaloneTest.php b/tests/CredisStandaloneTest.php index f47514d..46f41ca 100644 --- a/tests/CredisStandaloneTest.php +++ b/tests/CredisStandaloneTest.php @@ -6,19 +6,6 @@ class CredisStandaloneTest extends CredisTest { protected $useStandalone = TRUE; - /** - * @group UnixSocket - */ - public function testInvalidPersistentConnectionOnUnixSocket() - { - $this->credis->close(); - $this->credis = new Credis_Client('unix://'.realpath(__DIR__).'/redis.sock',0,null,'persistent'); - $this->credis->forceStandalone(); - //$this->setExpectedException('CredisException','Persistent connections to UNIX sockets are not supported in standalone mode.'); - $this->credis->connect(); - $this->assertTrue($this->credis->isConnected()); - } - public function testPersistentConnectionsOnStandAloneTcpConnection() { $this->credis->close(); From 9182d889210dbfad2b1e6ef1b0d5e1e72cbe16bb Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 8 Mar 2022 08:12:32 +0800 Subject: [PATCH 24/50] Adjust socket tests to capture testing phpredis --- tests/CredisTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/CredisTest.php b/tests/CredisTest.php index c8fbdf3..3e1f392 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -679,11 +679,12 @@ public function testConnectionStringsTls() */ public function testConnectionStringsSocket() { - $this->credis = new Credis_Client(realpath(__DIR__).'/redis.sock',0,null,'persistent'); + $this->credis = new Credis_Client(realpath(__DIR__).'/redis.sock'); if ($this->useStandalone) { $this->credis->forceStandalone(); } $this->credis->connect(); + $this->assertTrue($this->credis->isConnected()); $this->credis->set('key','value'); $this->assertEquals('value',$this->credis->get('key')); } @@ -708,6 +709,9 @@ public function testInvalidTlsConnectionString() } } + /** + * @group UnixSocket + */ public function testInvalidUnixSocketConnectionString() { $this->credis->close(); From 1f759b06570095b397b8b17c0be85379b8867da4 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 8 Mar 2022 07:51:35 +0800 Subject: [PATCH 25/50] phpredis connect's port is int and doesn't actually accept nulls, 0 is the documented value for unix connection strings, even for older versions --- Client.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Client.php b/Client.php index f4389df..72b209a 100755 --- a/Client.php +++ b/Client.php @@ -201,7 +201,7 @@ class Credis_Client { /** * Port on which the Redis server is running - * @var integer + * @var integer|null */ protected $port; @@ -358,7 +358,7 @@ public function getHost() } /** * Return the port of the Redis instance - * @return int + * @return int|null */ public function getPort() { @@ -474,8 +474,8 @@ public function connect() try { $result = $this->persistent - ? $this->redis->pconnect($this->host, $this->port, $socketTimeout, $this->persistent) - : $this->redis->connect($this->host, $this->port, $socketTimeout); + ? $this->redis->pconnect($this->host, (int)$this->port, $socketTimeout, $this->persistent) + : $this->redis->connect($this->host, (int)$this->port, $socketTimeout); } catch(Exception $e) { From b09b4ca44667d1a07c109b941f894cb4b0da6cb8 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 8 Mar 2022 08:28:12 +0800 Subject: [PATCH 26/50] Add suggests statement to composer.json --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index 9223e13..9f69167 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,9 @@ "require": { "php": ">=5.4.0" }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, "autoload": { "classmap": [ "Client.php", From c3622c13514b8af6ff20f356a53c43f2357d0286 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 8 Mar 2022 08:29:03 +0800 Subject: [PATCH 27/50] Update minimum php version supported to 5.6 to match tests --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9f69167..2b781e6 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": ">=5.4.0" + "php": ">=5.6.0" }, "suggest": { "ext-redis": "Improved performance for communicating with redis" From afec8e58ec93d2291c127fa19709a048f28641e5 Mon Sep 17 00:00:00 2001 From: Xon Date: Thu, 7 Apr 2022 22:57:22 +0800 Subject: [PATCH 28/50] Support username for ACL auth for Redis v6+ (#161) * Pass scheme to redis connections * Update tests to use upstream redis * ACL Auth requires phpredis v5.3+ * Improve compatibility with phpredis pre-v5.3 Co-authored-by: kodumbeats --- .github/workflows/ci.yml | 2 +- Client.php | 35 ++++++++++++++++----- tests/CredisTest.php | 67 ++++++++++++++++++++++++++++++++++++++++ tests/redis_config.json | 3 +- 4 files changed, 98 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1eaccff..d2b3966 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: extensions: redis - name: Setup Redis - run: sudo apt install redis -y ; sudo systemctl stop redis\* ; redis-cli --version + run: sudo add-apt-repository ppa:redislabs/redis -y -u ; sudo apt install redis -y ; sudo systemctl stop redis\* ; redis-cli --version - name: Install phpunit env: diff --git a/Client.php b/Client.php index 72b209a..4a253da 100755 --- a/Client.php +++ b/Client.php @@ -273,6 +273,11 @@ class Credis_Client { */ protected $isWatching = FALSE; + /** + * @var string + */ + protected $authUsername; + /** * @var string */ @@ -315,8 +320,9 @@ class Credis_Client { * @param string $persistent Flag to establish persistent connection * @param int $db The selected datbase of the Redis server * @param string $password The authentication password of the Redis server + * @param string $username The authentication username of the Redis server */ - public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null) + public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null) { $this->host = (string) $host; $this->port = (int) $port; @@ -325,10 +331,15 @@ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $this->persistent = (string) $persistent; $this->standalone = ! extension_loaded('redis'); $this->authPassword = $password; + $this->authUsername = $username; $this->selectedDb = (int)$db; $this->convertHost(); - // PHP Redis extension support TLS since 5.3.0 - if ($this->scheme == 'tls' && !$this->standalone && version_compare(phpversion('redis'),'5.3.0','<')){ + // PHP Redis extension support TLS/ACL AUTH since 5.3.0 + if (( + $this->scheme === 'tls' + || $this->authUsername !== null + ) + && !$this->standalone && version_compare(phpversion('redis'),'5.3.0','<')){ $this->standalone = true; } } @@ -504,9 +515,8 @@ public function connect() if ($this->readTimeout) { $this->setReadTimeout($this->readTimeout); } - if($this->authPassword) { - $this->auth($this->authPassword); + $this->auth($this->authPassword, $this->authUsername); } if($this->selectedDb !== 0) { $this->select($this->selectedDb); @@ -641,11 +651,17 @@ public function getRenamedCommand($command) /** * @param string $password + * @param string|null $username * @return bool */ - public function auth($password) + public function auth($password, $username = null) { - $response = $this->__call('auth', array($password)); + if ($username !== null) { + $response = $this->__call('auth', array($username, $password)); + $this->authUsername= $username; + } else { + $response = $this->__call('auth', array($password)); + } $this->authPassword = $password; return $response; } @@ -1150,6 +1166,10 @@ public function __call($name, $args) // allow phpredis to see the caller's reference //$param_ref =& $args[0]; break; + case 'auth': + // For phpredis pre-v5.3, the type signature is string, not array|string + $args = (is_array($args) && count($args) === 1) ? $args : array($args); + break; default: // Flatten arguments $args = self::_flattenArguments($args); @@ -1185,6 +1205,7 @@ public function __call($name, $args) return $this; } + // Send request, retry one time when using persistent connections on the first request only $this->requests++; try { diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 3e1f392..6c0f38a 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -620,6 +620,73 @@ public function testPassword() $this->assertTrue($this->credis->set('key','value')); } + /** + * @group Auth + */ + public function testUsernameAndPassword() + { + $this->tearDown(); + $this->assertArrayHasKey('password',$this->redisConfig[7]); + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, $this->redisConfig[7]['password'], $this->redisConfig[7]['username']); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertInstanceOf('Credis_Client',$this->credis->connect()); + $this->assertTrue($this->credis->set('key','value')); + $this->credis->close(); + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, 'wrongpassword', $this->redisConfig[7]['username']); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try + { + $this->credis->connect(); + $this->fail('connect should fail with wrong password'); + } + catch(CredisException $e) + { + if (strpos($e->getMessage(), 'username') !== false) + { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + + $this->credis->close(); + } + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try + { + $this->credis->set('key', 'value'); + } + catch(CredisException $e) + { + $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); + } + try + { + $this->credis->auth('anotherwrongpassword'); + } + catch(CredisException $e) + { + if (strpos($e->getMessage(), 'username') !== false) + { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + } + $this->assertTrue($this->credis->auth('thepassword')); + $this->assertTrue($this->credis->set('key','value')); + } + public function testGettersAndSetters() { $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); diff --git a/tests/redis_config.json b/tests/redis_config.json index c696fb3..f153f34 100644 --- a/tests/redis_config.json +++ b/tests/redis_config.json @@ -5,5 +5,6 @@ {"host":"127.0.0.1","port":6382,"timeout":2.5,"alias":"fourth"}, {"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"auth", "password": "thepassword"}, {"host":"127.0.0.1","port":6384,"timeout":2.5,"alias":"socket"}, - {"host":"127.0.0.1","port":6385,"timeout":2.5,"alias":"slave"} + {"host":"127.0.0.1","port":6385,"timeout":2.5,"alias":"slave"}, + {"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"userauth", "username": "default", "password": "thepassword"} ] \ No newline at end of file From 15afcf36dca4271906f4ff9c9df5b2a953f57bd7 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 10 May 2022 07:42:36 +0800 Subject: [PATCH 29/50] Fix Redis 7 sentinel support (use 'replicas' naming if needed) (#164) --- Sentinel.php | 73 ++++++++++++++++++++++++++++++++---- tests/CredisSentinelTest.php | 11 +++++- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/Sentinel.php b/Sentinel.php index 5a7c2a4..b5f4116 100644 --- a/Sentinel.php +++ b/Sentinel.php @@ -52,22 +52,45 @@ class Credis_Sentinel * @var string */ protected $_password = ''; - /** + * Store the AUTH username used by Credis_Client instances (Redis v6+) + * @var string + */ + protected $_username = ''; + /** + * @var null|float + */ + protected $_timeout; + /** + * @var string + */ + protected $_persistent; + /** + * @var int + */ + protected $_db; + /** + * @var string|null + */ + protected $_replicaCmd = null; + /** + * @var string|null + */ + protected $_redisVersion = null; + + /** * Connect with a Sentinel node. Sentinel will do the master and slave discovery * * @param Credis_Client $client * @param string $password (deprecated - use setClientPassword) * @throws CredisException */ - public function __construct(Credis_Client $client, $password = NULL) + public function __construct(Credis_Client $client, $password = NULL, $username = NULL) { - if(!$client instanceof Credis_Client){ - throw new CredisException('Sentinel client should be an instance of Credis_Client'); - } $client->forceStandalone(); // SENTINEL command not currently supported by phpredis $this->_client = $client; $this->_password = $password; + $this->_username = $username; $this->_timeout = NULL; $this->_persistent = ''; $this->_db = 0; @@ -121,6 +144,37 @@ public function setClientPassword($password) return $this; } + /** + * @param null|string $username + * @return $this + */ + public function setClientUsername($username) + { + $this->_username = $username; + return $this; + } + + /** + * @param null|string $replicaCmd + * @return $this + */ + public function setReplicaCommand($replicaCmd) + { + $this->_replicaCmd = $replicaCmd; + return $this; + } + + public function detectRedisVersion() + { + if ($this->_redisVersion !== null && $this->_replicaCmd !== null) { + return; + } + $serverInfo = $this->info('server'); + $this->_redisVersion = $serverInfo['redis_version']; + // Redis v7+ renames the replica command to 'replicas' instead of 'slaves' + $this->_replicaCmd = version_compare($this->_redisVersion, '7.0.0', '>=') ? 'replicas' : 'slaves'; + } + /** * @return Credis_Sentinel * @deprecated @@ -144,7 +198,7 @@ public function createMasterClient($name) if(!isset($master[0]) || !isset($master[1])){ throw new CredisException('Master not found'); } - return new Credis_Client($master[0], $master[1], $this->_timeout, $this->_persistent, $this->_db, $this->_password); + return new Credis_Client($master[0], $master[1], $this->_timeout, $this->_persistent, $this->_db, $this->_password, $this->_username); } /** @@ -176,7 +230,7 @@ public function createSlaveClients($name) throw new CredisException('Can\' retrieve slave status'); } if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) { - $workingSlaves[] = new Credis_Client($slave[3], $slave[5], $this->_timeout, $this->_persistent, $this->_db, $this->_password); + $workingSlaves[] = new Credis_Client($slave[3], $slave[5], $this->_timeout, $this->_persistent, $this->_db, $this->_password, $this->_username); } } return $workingSlaves; @@ -307,7 +361,10 @@ public function masters() */ public function slaves($name) { - return $this->_client->sentinel('slaves',$name); + if ($this->_replicaCmd === null) { + $this->detectRedisVersion(); + } + return $this->_client->sentinel($this->_replicaCmd,$name); } /** diff --git a/tests/CredisSentinelTest.php b/tests/CredisSentinelTest.php index 50d532f..10503a0 100644 --- a/tests/CredisSentinelTest.php +++ b/tests/CredisSentinelTest.php @@ -225,9 +225,16 @@ public function testNonExistingMethod() } catch(CredisException $e) { - if (strpos($e->getMessage(), 'sentinel subcommand') !== false) + if (strpos($e->getMessage(), 'bla') !== false) { - $this->assertStringStartsWith('ERR Unknown sentinel subcommand \'bla\'', $e->getMessage()); + if (strpos($e->getMessage(), 'unknown subcommand') !== false) + { + $this->assertStringStartsWith('ERR unknown subcommand \'bla\'', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR Unknown sentinel subcommand \'bla\'', $e->getMessage()); + } } else { From fbb869fd1a0a97e0a2e955963889fbdafdff854b Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 14 May 2022 06:37:32 +0800 Subject: [PATCH 30/50] Add support for SSL and TLS connection protocols and SSL/TLS options. (#163) * feature: Allow versions in TLS * do not change travis url * Fix regression after merge. * Add protocol to connection error message. * - Fix phpredis TLS support - Expose the SSL context as a connection option, allowing self-signed or non-public ca to be used. * Ensure tests report what phpredis version is used * Add TLS connection test * Commit a pre-built redis.dh to reduce test time * No-op testTLSConnection test as getting php 5.6-php7.1 to reliably work with SSL/TLS is challanging as it depends on OS/php/redis compiling settings * Document tls footguns * Support ssl:// prefix as php supports multiple ways to configure an ssl connection :( * Fix testGettersAndSetters test * Assert various TLS/SSL prefix parsing passes * use ssl to ensure SSL tests actually pass Co-authored-by: waynetheisinger Co-authored-by: Colin Mollenhour --- .github/workflows/ci.yml | 3 + Client.php | 114 ++++++++++++++++++++++++++++++++----- README.markdown | 8 +++ tests/CredisTest.php | 57 +++++++++++++++---- tests/CredisTestCommon.php | 5 ++ tests/gen-test-certs.sh | 70 +++++++++++++++++++++++ tests/redis-tls.conf | 12 ++++ tests/redis_config.json | 3 +- tests/tls/redis.dh | 8 +++ 9 files changed, 254 insertions(+), 26 deletions(-) create mode 100755 tests/gen-test-certs.sh create mode 100644 tests/redis-tls.conf create mode 100644 tests/tls/redis.dh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2b3966..1cc359c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,5 +46,8 @@ jobs: composer install --dev -n fi + - name: Report phpredis version + run: php -r 'echo phpversion("redis")."\n";' + - name: Unit tests run: ./vendor/bin/phpunit \ No newline at end of file diff --git a/Client.php b/Client.php index 4a253da..404a415 100755 --- a/Client.php +++ b/Client.php @@ -194,11 +194,17 @@ class Credis_Client { protected $host; /** - * Scheme of the Redis server (tcp, tls, unix) + * Scheme of the Redis server (tcp, tls, tlsv1.2, unix) * @var string */ protected $scheme; + /** + * SSL Meta information + * @var string + */ + protected $sslMeta; + /** * Port on which the Redis server is running * @var integer|null @@ -309,6 +315,27 @@ class Credis_Client { */ protected $subscribed = false; + /** @var bool */ + protected $oldPhpRedis = false; + + /** @var array */ + protected $tlsOptions = []; + + + /** + * @var bool + */ + protected $isTls = false; + + /** + * Gets Useful Meta debug information about the SSL + * + * @return string + */ + public function getSslMeta() + { + return $this->sslMeta; + } /** * Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}. @@ -321,8 +348,9 @@ class Credis_Client { * @param int $db The selected datbase of the Redis server * @param string $password The authentication password of the Redis server * @param string $username The authentication username of the Redis server + * @param array|null $tlsOptions The TLS/SSL context options. See https://www.php.net/manual/en/context.ssl.php for details */ - public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null) + public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null, array $tlsOptions = null) { $this->host = (string) $host; $this->port = (int) $port; @@ -334,12 +362,16 @@ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $this->authUsername = $username; $this->selectedDb = (int)$db; $this->convertHost(); + if ($tlsOptions) { + $this->setTlsOptions($tlsOptions); + } // PHP Redis extension support TLS/ACL AUTH since 5.3.0 + $this->oldPhpRedis = (bool)version_compare(phpversion('redis'),'5.3.0','<'); if (( - $this->scheme === 'tls' + $this->isTls || $this->authUsername !== null ) - && !$this->standalone && version_compare(phpversion('redis'),'5.3.0','<')){ + && !$this->standalone && $this->oldPhpRedis){ $this->standalone = true; } } @@ -376,6 +408,14 @@ public function getPort() return $this->port; } + /** + * @return bool + */ + public function isTls() + { + return $this->isTls; + } + /** * Return the selected database * @return int @@ -426,10 +466,20 @@ public function setCloseOnDestruct($flag) $this->closeOnDestruct = $flag; return $this; } + + public function setTlsOptions(array $tlsOptions) + { + if($this->connected) { + throw new CredisException('Cannot change TLS options after a connection has already been established.'); + } + $this->tlsOptions = $tlsOptions; + } + protected function convertHost() { - if (preg_match('#^(tcp|tls|unix)://(.*)$#', $this->host, $matches)) { - if($matches[1] == 'tcp' || $matches[1] == 'tls') { + if (preg_match('#^(tcp|tls|ssl|tlsv\d(?:\.\d)?|unix)://(.+)$#', $this->host, $matches)) { + $this->isTls = strpos($matches[1], 'tls') === 0 || strpos($matches[1], 'ssl') === 0; + if($this->isTls || $matches[1] === 'tcp') { $this->scheme = $matches[1]; if ( ! preg_match('#^([^:]+)(:([0-9]+))?(/(.+))?$#', $matches[2], $matches)) { throw new CredisException('Invalid host format; expected '.$this->scheme.'://host[:port][/persistence_identifier]'); @@ -454,6 +504,7 @@ protected function convertHost() $this->scheme = 'tcp'; } } + /** * @throws CredisException * @return Credis_Client @@ -464,7 +515,7 @@ public function connect() return $this; } $this->close(true); - + $tlsOptions = $this->isTls ? $this->tlsOptions : []; if ($this->standalone) { $flags = STREAM_CLIENT_CONNECT; $remote_socket = $this->port === NULL @@ -475,18 +526,49 @@ public function connect() $remote_socket .= '/'.$this->persistent; $flags = $flags | STREAM_CLIENT_PERSISTENT; } - $result = $this->redis = @stream_socket_client($remote_socket, $errno, $errstr, $this->timeout !== null ? $this->timeout : 2.5, $flags); + if ($this->isTls) { + $tlsOptions = array_merge($tlsOptions, [ + 'capture_peer_cert' => true, + 'capture_peer_cert_chain' => true, + 'capture_session_meta' => true, + ]); + } + + // passing $context as null errors before php 8.0 + $context = stream_context_create(['ssl' => $tlsOptions]); + + $result = $this->redis = @stream_socket_client($remote_socket, $errno, $errstr, $this->timeout !== null ? $this->timeout : 2.5, $flags, $context); + + if ($result && $this->isTls) { + $this->sslMeta = stream_context_get_options($context); + } } else { if ( ! $this->redis) { $this->redis = new Redis; } - $socketTimeout = $this->timeout ? $this->timeout : 0.0; + $socketTimeout = $this->timeout ?: 0.0; try { + if ($this->oldPhpRedis) + { + $result = $this->persistent + ? $this->redis->pconnect($this->host, (int)$this->port, $socketTimeout, $this->persistent) + : $this->redis->connect($this->host, (int)$this->port, $socketTimeout); + } + else + { + // 7th argument is non-documented TLS options. But it only exists on the newer versions of phpredis + if ($tlsOptions) { + $context = ['stream' => $tlsOptions]; + } else { + $context = []; + } + /** @noinspection PhpMethodParametersCountMismatchInspection */ $result = $this->persistent - ? $this->redis->pconnect($this->host, (int)$this->port, $socketTimeout, $this->persistent) - : $this->redis->connect($this->host, (int)$this->port, $socketTimeout); + ? $this->redis->pconnect($this->scheme.'://'.$this->host, (int)$this->port, $socketTimeout, $this->persistent, 0, 0.0, $context) + : $this->redis->connect($this->scheme.'://'.$this->host, (int)$this->port, $socketTimeout, null, 0, 0.0, $context); + } } catch(Exception $e) { @@ -505,7 +587,13 @@ public function connect() } $failures = $this->connectFailures; $this->connectFailures = 0; - throw new CredisException("Connection to Redis {$this->host}:{$this->port} failed after $failures failures." . (isset($errno) && isset($errstr) ? "Last Error : ({$errno}) {$errstr}" : "")); + throw new CredisException(sprintf("Connection to Redis%s %s://%s failed after %s failures.%s", + $this->standalone ? ' standalone' : '', + $this->scheme, + $this->host.($this->port ? ':'.$this->port : ''), + $failures, + (isset($errno) && isset($errstr) ? "Last Error : ({$errno}) {$errstr}" : "") + )); } $this->connectFailures = 0; @@ -1168,7 +1256,7 @@ public function __call($name, $args) break; case 'auth': // For phpredis pre-v5.3, the type signature is string, not array|string - $args = (is_array($args) && count($args) === 1) ? $args : array($args); + $args = $this->oldPhpRedis ? $args : array($args); break; default: // Flatten arguments diff --git a/README.markdown b/README.markdown index bf1c8e9..0a9f09a 100644 --- a/README.markdown +++ b/README.markdown @@ -44,6 +44,14 @@ $redis = new Credis_Client(/* connection string */); `tls://host[:port][/persistence_identifier]` +or + +`tlsv1.2://host[:port][/persistence_identifier]` + +Before php 7.2, `tls://` only supports TLSv1.0, either `ssl://` or `tlsv1.2` can be used to force TLSv1.2 support. + +Recent versions of redis do not support the protocols/cyphers that older versions of php default to, which may result in cryptic connection failures. + #### Enable transport level security (TLS) Use TLS connection string `tls://127.0.0.1:6379` instead of TCP connection `tcp://127.0.0.1:6379` string in order to enable transport level security. diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 6c0f38a..803fa64 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -707,7 +707,11 @@ public function testGettersAndSetters() $this->credis->forceStandalone(); } $this->credis->setMaxConnectRetries(1); - $this->setExpectedExceptionShim('CredisException','Connection to Redis localhost:12345 failed after 2 failures.'); + if ($this->useStandalone) { + $this->setExpectedExceptionShim('CredisException','Connection to Redis standalone tcp://localhost:12345 failed after 2 failures.'); + } else { + $this->setExpectedExceptionShim('CredisException','Connection to Redis tcp://localhost:12345 failed after 2 failures.'); + } $this->credis->connect(); } @@ -728,17 +732,46 @@ public function testConnectionStrings() public function testConnectionStringsTls() { - $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); - $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); - $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(),6379); - $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); - $this->assertEquals($this->credis->getPersistence(),'abc123'); - $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'],6380); - $this->assertEquals($this->credis->getPort(),6380); - $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'],NULL,NULL,"abc123"); - $this->assertEquals($this->credis->getPersistence(),'abc123'); + foreach(['ssl','tls','tlsv1.0','tlsv1.1','tlsv1.2'] as $prefix){ + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(),6379); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); + $this->assertEquals($this->credis->getPersistence(),'abc123'); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'],6380); + $this->assertEquals($this->credis->getPort(),6380); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'],NULL,NULL,"abc123"); + $this->assertEquals($this->credis->getPersistence(),'abc123'); + $this->assertTrue($this->credis->isTls()); + } + } + + public function testTLSConnection() + { + /** + https://www.php.net/manual/en/context.ssl.php + > Oddly, if "tls://" is set below, then TLSv1 is forced, but using "ssl://" allows TLSv1.2 + + and + >Recommended use "ssl://" transport. + > in php 5.5 ~ 7.1 + > ssl:// transport = ssl_v2|ssl_v3|tls_v1.0|tls_v1.1|tls_v1.2 + > tls:// transport = tls_v1.0 + > after 7.2 ssl:// and tls:// transports is same + > php 7.2 ~ 7.3 = tls_v1.0|tls_v1.1|tls_v1.2 + > php 7.4 ~ 8.1 = tls_v1.0|tls_v1.1|tls_v1.2|tls_v1.3 + */ + + $this->credis = new Credis_Client('ssl://'.$this->redisConfig[8]['host'] . ':' . $this->redisConfig[8]['port']); + $this->credis->setTlsOptions((array)$this->redisConfig[8]['ssl']); + $this->credis->connect(); + $this->assertTrue(true); } /** diff --git a/tests/CredisTestCommon.php b/tests/CredisTestCommon.php index fe4d6c1..8fd774a 100644 --- a/tests/CredisTestCommon.php +++ b/tests/CredisTestCommon.php @@ -113,6 +113,11 @@ public static function setUpBeforeClassInternal() echo "\tredis-server redis-auth.conf".PHP_EOL; echo "\tredis-server redis-socket.conf".PHP_EOL.PHP_EOL; } else { + chdir(__DIR__.'/../'); + if (!file_exists('./tests/tls/ca.crt') || !file_exists('./tests/tls/server.crt')) { + // generate SSL keys + system('./tests/gen-test-certs.sh'); + } chdir(__DIR__); $directoryIterator = new DirectoryIterator(__DIR__); foreach($directoryIterator as $item){ diff --git a/tests/gen-test-certs.sh b/tests/gen-test-certs.sh new file mode 100755 index 0000000..6dcd691 --- /dev/null +++ b/tests/gen-test-certs.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# https://raw.githubusercontent.com/redis/redis/4930d19e70c391750479951022e207e19111eb55/utils/gen-test-certs.sh +# Copyright (c) 2006-2020, Salvatore Sanfilippo +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# * Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Generate some test certificates which are used by the regression test suite: +# +# tests/tls/ca.{crt,key} Self signed CA certificate. +# tests/tls/redis.{crt,key} A certificate with no key usage/policy restrictions. +# tests/tls/client.{crt,key} A certificate restricted for SSL client usage. +# tests/tls/server.{crt,key} A certificate restricted fro SSL server usage. +# tests/tls/redis.dh DH Params file. + +generate_cert() { + local name=$1 + local cn="$2" + local opts="$3" + + local keyfile=tests/tls/${name}.key + local certfile=tests/tls/${name}.crt + + [ -f $keyfile ] || openssl genrsa -out $keyfile 2048 + openssl req \ + -new -sha256 \ + -subj "/O=Redis Test/CN=$cn" \ + -key $keyfile | \ + openssl x509 \ + -req -sha256 \ + -CA tests/tls/ca.crt \ + -CAkey tests/tls/ca.key \ + -CAserial tests/tls/ca.txt \ + -CAcreateserial \ + -days 365 \ + $opts \ + -out $certfile +} + +mkdir -p tests/tls +[ -f tests/tls/ca.key ] || openssl genrsa -out tests/tls/ca.key 4096 +openssl req \ + -x509 -new -nodes -sha256 \ + -key tests/tls/ca.key \ + -days 3650 \ + -subj '/O=Redis Test/CN=Certificate Authority' \ + -out tests/tls/ca.crt + +cat > tests/tls/openssl.cnf <<_END_ +[ server_cert ] +keyUsage = digitalSignature, keyEncipherment +nsCertType = server + +[ client_cert ] +keyUsage = digitalSignature, keyEncipherment +nsCertType = client +_END_ + +generate_cert server "Server-only" "-extfile tests/tls/openssl.cnf -extensions server_cert" +generate_cert client "Client-only" "-extfile tests/tls/openssl.cnf -extensions client_cert" +generate_cert redis "Generic-cert" + +[ -f tests/tls/redis.dh ] || openssl dhparam -out tests/tls/redis.dh 2048 \ No newline at end of file diff --git a/tests/redis-tls.conf b/tests/redis-tls.conf new file mode 100644 index 0000000..a8efb38 --- /dev/null +++ b/tests/redis-tls.conf @@ -0,0 +1,12 @@ +daemonize yes +port 0 +tls-port 6386 +tls-cert-file ./tls/redis.crt +tls-key-file ./tls/redis.key +tls-ca-cert-file ./tls/ca.crt +tls-dh-params-file ./tls/redis.dh +tls-auth-clients no +logfile redis-tls.log +dir ./ +pidfile redis-tls.pid +timeout 300 diff --git a/tests/redis_config.json b/tests/redis_config.json index f153f34..a1f7678 100644 --- a/tests/redis_config.json +++ b/tests/redis_config.json @@ -6,5 +6,6 @@ {"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"auth", "password": "thepassword"}, {"host":"127.0.0.1","port":6384,"timeout":2.5,"alias":"socket"}, {"host":"127.0.0.1","port":6385,"timeout":2.5,"alias":"slave"}, - {"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"userauth", "username": "default", "password": "thepassword"} + {"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"userauth", "username": "default", "password": "thepassword"}, + {"host":"127.0.0.1","port":6386,"timeout":2.5,"alias":"tls", "ssl": {"verify_peer_name": 0, "verify_peer": 0, "allow_self_signed": 1}} ] \ No newline at end of file diff --git a/tests/tls/redis.dh b/tests/tls/redis.dh new file mode 100644 index 0000000..a0dbc99 --- /dev/null +++ b/tests/tls/redis.dh @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEApf41bsCPlZ48z3CeiGhPO/8SlB8YfP8C7wLRKDBI1LbfLOQSBazT +ExMqKGL1ZC1XSugpB7nzNt2qVVj80eAKvZEUA45o7YhfcacS+DpfQbwyhMlJ6hAB +RUOqgYIwF2+zaKp86eYD+38nfA3HY3AIeK9fAhtPQLvHz3xyPBgd8G3e+CqfFK7z +sRX4F95JtPG+XlD1v8rU6T2hUKIdyRWFvAidSQIADpix8bbs5LnSbmTl9bQFe45H +7+duvJDy/v7KLZWIWAOjM+Ki6o//BFhPldsAJZ0dAJh9MQRgVUPHL9a8pPZh/Muc +lMVeqb3NNFR3blpmeCK9NMn/lC3KoWVp8wIBAg== +-----END DH PARAMETERS----- From b63f7725119970a0d33f5d6c53934ca013111c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Dav=C3=ADdek?= Date: Thu, 9 Jun 2022 04:03:11 +0200 Subject: [PATCH 31/50] Improved phpdoc of Credis_Client (#165) --- Client.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Client.php b/Client.php index 404a415..4d84562 100755 --- a/Client.php +++ b/Client.php @@ -59,7 +59,7 @@ public function __construct($message, $code = 0, $exception = NULL) * @method int|Credis_Client dbsize() * * Keys: - * @method int|Credis_Client del(string $key) + * @method int|Credis_Client del(string|array $key) * @method int|Credis_Client exists(string $key) * @method int|Credis_Client expire(string $key, int $seconds) * @method int|Credis_Client expireAt(string $key, int $timestamp) @@ -75,13 +75,13 @@ public function __construct($message, $code = 0, $exception = NULL) * @method int|Credis_Client append(string $key, string $value) * @method int|Credis_Client decr(string $key) * @method int|Credis_Client decrBy(string $key, int $decrement) - * @method bool|string|Credis_Client get(string $key) + * @method false|string|Credis_Client get(string $key) * @method int|Credis_Client getBit(string $key, int $offset) * @method string|Credis_Client getRange(string $key, int $start, int $end) * @method string|Credis_Client getSet(string $key, string $value) * @method int|Credis_Client incr(string $key) * @method int|Credis_Client incrBy(string $key, int $decrement) - * @method array|Credis_Client mGet(array $keys) + * @method false|array|Credis_Client mGet(array $keys) * @method bool|Credis_Client mSet(array $keysValues) * @method int|Credis_Client mSetNx(array $keysValues) * @method bool|Credis_Client set(string $key, string $value, int | array $options = null) From 85df015088e00daf8ce395189de22c8eb45c8d49 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 21 Jun 2022 06:56:59 +0800 Subject: [PATCH 32/50] Fix incorrect type hints, and ensure port's nullability is respected (#167) --- Client.php | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Client.php b/Client.php index 4d84562..97c18f1 100755 --- a/Client.php +++ b/Client.php @@ -195,7 +195,7 @@ class Credis_Client { /** * Scheme of the Redis server (tcp, tls, tlsv1.2, unix) - * @var string + * @var string|null */ protected $scheme; @@ -207,19 +207,19 @@ class Credis_Client { /** * Port on which the Redis server is running - * @var integer|null + * @var int|null */ protected $port; /** * Timeout for connecting to Redis server - * @var float + * @var float|null */ protected $timeout; /** * Timeout for reading response from Redis server - * @var float + * @var float|null */ protected $readTimeout; @@ -280,12 +280,12 @@ class Credis_Client { protected $isWatching = FALSE; /** - * @var string + * @var string|null */ protected $authUsername; /** - * @var string + * @var string|null */ protected $authPassword; @@ -301,7 +301,7 @@ class Credis_Client { protected $wrapperMethods = array('delete' => 'del', 'getkeys' => 'keys', 'sremove' => 'srem'); /** - * @var array + * @var array|callable|null */ protected $renamedCommands; @@ -342,18 +342,20 @@ public function getSslMeta() * $host may also be a path to a unix socket or a string in the form of tcp://[hostname]:[port] or unix://[path] * * @param string $host The hostname of the Redis server - * @param integer $port The port number of the Redis server - * @param float $timeout Timeout period in seconds + * @param int|null $port The port number of the Redis server + * @param float|null $timeout Timeout period in seconds * @param string $persistent Flag to establish persistent connection - * @param int $db The selected datbase of the Redis server - * @param string $password The authentication password of the Redis server - * @param string $username The authentication username of the Redis server + * @param int $db The selected database of the Redis server + * @param string|null $password The authentication password of the Redis server + * @param string|null $username The authentication username of the Redis server * @param array|null $tlsOptions The TLS/SSL context options. See https://www.php.net/manual/en/context.ssl.php for details */ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null, array $tlsOptions = null) { $this->host = (string) $host; - $this->port = (int) $port; + if ($port !== null) { + $this->port = (int) $port; + } $this->scheme = null; $this->timeout = $timeout; $this->persistent = (string) $persistent; @@ -622,7 +624,7 @@ public function isConnected() * Set the read timeout for the connection. Use 0 to disable timeouts entirely (or use a very long timeout * if not supported). * - * @param int $timeout 0 (or -1) for no timeout, otherwise number of seconds + * @param float $timeout 0 (or -1) for no timeout, otherwise number of seconds * @throws CredisException * @return Credis_Client */ From dccc8a46586475075fbb012d8bd523b8a938c2dc Mon Sep 17 00:00:00 2001 From: Xon Date: Wed, 9 Nov 2022 09:18:39 +0800 Subject: [PATCH 33/50] php 8.2 compatibility update (#171) * php 8.2 compatibility update * Allow overriding of _map --- Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client.php b/Client.php index 97c18f1..4689a1b 100755 --- a/Client.php +++ b/Client.php @@ -1587,7 +1587,7 @@ protected function decode_reply($name, $response, array &$arguments = array() ) */ private static function _prepare_command($args) { - return sprintf('*%d%s%s%s', count($args), CRLF, implode(CRLF, array_map(array('self', '_map'), $args)), CRLF); + return sprintf('*%d%s%s%s', count($args), CRLF, implode(CRLF, array_map([static::class, '_map'], $args)), CRLF); } private static function _map($arg) From 2db953a57488ed9eab8efefab35aae6cbb0c8930 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 14 Jan 2023 01:01:04 +0800 Subject: [PATCH 34/50] Use "\r\n" directly instead of define(CRLF) (#174) --- Client.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Client.php b/Client.php index 4689a1b..8542a63 100755 --- a/Client.php +++ b/Client.php @@ -20,7 +20,6 @@ * @package Credis_Client */ -if( ! defined('CRLF')) define('CRLF', sprintf('%s%s', chr(13), chr(10))); /** * Credis-specific errors, wraps native Redis errors @@ -1423,7 +1422,7 @@ protected function read_reply($name = '', $returnQueued = false) throw new CredisException('Lost connection to Redis server.', CredisException::CODE_DISCONNECTED); } } - $reply = rtrim($reply, CRLF); + $reply = rtrim($reply, "\r\n"); #echo "> $name: $reply\n"; $replyType = substr($reply, 0, 1); switch ($replyType) { @@ -1498,7 +1497,7 @@ protected function decode_reply($name, $response, array &$arguments = array() ) $response = count($keys) ? array_combine($keys, $values) : array(); break; case 'info': - $lines = explode(CRLF, trim($response, CRLF)); + $lines = explode("\r\n", trim($response, "\r\n")); $response = array(); foreach ($lines as $line) { @@ -1587,12 +1586,12 @@ protected function decode_reply($name, $response, array &$arguments = array() ) */ private static function _prepare_command($args) { - return sprintf('*%d%s%s%s', count($args), CRLF, implode(CRLF, array_map([static::class, '_map'], $args)), CRLF); + return sprintf('*%d%s%s%s', count($args), "\r\n", implode("\r\n", array_map([static::class, '_map'], $args)), "\r\n"); } private static function _map($arg) { - return sprintf('$%d%s%s', strlen($arg), CRLF, $arg); + return sprintf('$%d%s%s', strlen($arg), "\r\n", $arg); } /** From 7a3319d0ec04cb8942066c1c5ef48647c06a17fc Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 27 Jan 2023 14:18:39 +0800 Subject: [PATCH 35/50] Reformat code, add PHP CS Fixer, update comment of Credis_Client __construct (#175) --- .github/workflows/ci.yml | 17 +- .gitignore | 2 + .php-cs-fixer.dist.php | 19 + Client.php | 621 ++++++++++++------------- Cluster.php | 597 ++++++++++++------------ Module.php | 2 +- Sentinel.php | 93 ++-- tests/CredisClusterTest.php | 385 ++++++++------- tests/CredisSentinelTest.php | 457 +++++++++--------- tests/CredisStandaloneClusterTest.php | 40 +- tests/CredisStandaloneSentinelTest.php | 2 +- tests/CredisStandaloneTest.php | 43 +- tests/CredisTest.php | 330 ++++++------- tests/CredisTestCommon.php | 73 ++- tests/phpunit-shims/php7.php | 48 +- tests/phpunit-shims/php8.php | 49 +- 16 files changed, 1357 insertions(+), 1421 deletions(-) create mode 100644 .php-cs-fixer.dist.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cc359c..8046ac5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,17 +37,30 @@ jobs: - name: Setup Redis run: sudo add-apt-repository ppa:redislabs/redis -y -u ; sudo apt install redis -y ; sudo systemctl stop redis\* ; redis-cli --version - - name: Install phpunit + - name: Install dependencies env: PHPUNIT_VERSION: ${{ matrix.phpunit }} run: | if [ ! -z "$PHPUNIT_VERSION" ]; then composer require "phpunit/phpunit:${PHPUNIT_VERSION}" --dev --no-update -n + need_install_dev=1 + fi + if php -r 'exit(version_compare(PHP_VERSION, "8.0") >= 0 ? 0 : 1);'; then + composer require "friendsofphp/php-cs-fixer:^3.13" --dev --no-update -n + need_install_dev=1 + fi + if [ ! -z "$need_install_dev" ]; then composer install --dev -n fi + - name: Run PHP CS Fixer + run: | + if [ -f ./vendor/bin/php-cs-fixer ]; then + ./vendor/bin/php-cs-fixer fix --diff --dry-run + fi + - name: Report phpredis version run: php -r 'echo phpversion("redis")."\n";' - name: Unit tests - run: ./vendor/bin/phpunit \ No newline at end of file + run: ./vendor/bin/phpunit diff --git a/.gitignore b/.gitignore index 06082ab..c1f9b7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .idea +composer.lock vendor phpunit.phar phpunit_*.log /.phpunit.result.cache +/.php-cs-fixer.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..6a41e79 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,19 @@ +setRules([ + '@PSR12' => true, + 'visibility_required' => false, // php 5.6 doesn't support "public const ..." + ]) + ->setFinder(PhpCsFixer\Finder::create() + ->in(__DIR__) + ->name('*.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true) + ) +; diff --git a/Client.php b/Client.php index 8542a63..d957779 100755 --- a/Client.php +++ b/Client.php @@ -26,18 +26,16 @@ */ class CredisException extends Exception { - const CODE_TIMED_OUT = 1; const CODE_DISCONNECTED = 2; - public function __construct($message, $code = 0, $exception = NULL) + public function __construct($message, $code = 0, $exception = null) { - if ($exception && get_class($exception) == 'RedisException' && strpos($message,'read error on connection') === 0) { + if ($exception && get_class($exception) == 'RedisException' && strpos($message, 'read error on connection') === 0) { $code = CredisException::CODE_DISCONNECTED; } parent::__construct($message, $code, $exception); } - } /** @@ -166,18 +164,14 @@ public function __construct($message, $code = 0, $exception = NULL) * @method string|int|array|bool|Credis_Client eval(string $script, array $keys = null, array $args = null) * @method string|int|array|bool|Credis_Client evalSha(string $script, array $keys = null, array $args = null) */ -class Credis_Client { - - const VERSION = '1.11.4'; - - const TYPE_STRING = 'string'; - const TYPE_LIST = 'list'; - const TYPE_SET = 'set'; - const TYPE_ZSET = 'zset'; - const TYPE_HASH = 'hash'; - const TYPE_NONE = 'none'; - - const FREAD_BLOCK_SIZE = 8192; +class Credis_Client +{ + const TYPE_STRING = 'string'; + const TYPE_LIST = 'list'; + const TYPE_SET = 'set'; + const TYPE_ZSET = 'zset'; + const TYPE_HASH = 'hash'; + const TYPE_NONE = 'none'; /** * Socket connection to the Redis server or Redis library instance @@ -231,12 +225,12 @@ class Credis_Client { /** * @var bool */ - protected $closeOnDestruct = TRUE; + protected $closeOnDestruct = true; /** * @var bool */ - protected $connected = FALSE; + protected $connected = false; /** * @var bool @@ -256,7 +250,7 @@ class Credis_Client { /** * @var bool */ - protected $usePipeline = FALSE; + protected $usePipeline = false; /** * @var array @@ -271,12 +265,12 @@ class Credis_Client { /** * @var bool */ - protected $isMulti = FALSE; + protected $isMulti = false; /** * @var bool */ - protected $isWatching = FALSE; + protected $isWatching = false; /** * @var string|null @@ -314,7 +308,7 @@ class Credis_Client { */ protected $subscribed = false; - /** @var bool */ + /** @var bool */ protected $oldPhpRedis = false; /** @var array */ @@ -337,13 +331,13 @@ public function getSslMeta() } /** - * Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}. + * Creates a connection to the Redis server on host {@link $host} and port {@link $port}. * $host may also be a path to a unix socket or a string in the form of tcp://[hostname]:[port] or unix://[path] * * @param string $host The hostname of the Redis server * @param int|null $port The port number of the Redis server - * @param float|null $timeout Timeout period in seconds - * @param string $persistent Flag to establish persistent connection + * @param float|null $timeout Timeout period in seconds + * @param string $persistent Flag to establish persistent connection * @param int $db The selected database of the Redis server * @param string|null $password The authentication password of the Redis server * @param string|null $username The authentication username of the Redis server @@ -351,28 +345,28 @@ public function getSslMeta() */ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null, array $tlsOptions = null) { - $this->host = (string) $host; + $this->host = (string)$host; if ($port !== null) { - $this->port = (int) $port; + $this->port = (int)$port; } $this->scheme = null; $this->timeout = $timeout; - $this->persistent = (string) $persistent; - $this->standalone = ! extension_loaded('redis'); + $this->persistent = (string)$persistent; + $this->standalone = !extension_loaded('redis'); $this->authPassword = $password; $this->authUsername = $username; $this->selectedDb = (int)$db; $this->convertHost(); if ($tlsOptions) { - $this->setTlsOptions($tlsOptions); + $this->setTlsOptions($tlsOptions); } // PHP Redis extension support TLS/ACL AUTH since 5.3.0 - $this->oldPhpRedis = (bool)version_compare(phpversion('redis'),'5.3.0','<'); + $this->oldPhpRedis = (bool)version_compare(phpversion('redis'), '5.3.0', '<'); if (( - $this->isTls - || $this->authUsername !== null - ) - && !$this->standalone && $this->oldPhpRedis){ + $this->isTls + || $this->authUsername !== null + ) + && !$this->standalone && $this->oldPhpRedis) { $this->standalone = true; } } @@ -389,7 +383,7 @@ public function __destruct() */ public function isSubscribed() { - return $this->subscribed; + return $this->subscribed; } /** @@ -400,6 +394,7 @@ public function getHost() { return $this->host; } + /** * Return the port of the Redis instance * @return int|null @@ -414,7 +409,7 @@ public function getPort() */ public function isTls() { - return $this->isTls; + return $this->isTls; } /** @@ -425,6 +420,7 @@ public function getSelectedDb() { return $this->selectedDb; } + /** * @return string */ @@ -432,19 +428,20 @@ public function getPersistence() { return $this->persistent; } + /** - * @throws CredisException * @return Credis_Client + * @throws CredisException */ public function forceStandalone() { if ($this->standalone) { return $this; } - if($this->connected) { + if ($this->connected) { throw new CredisException('Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.'); } - $this->standalone = TRUE; + $this->standalone = true; return $this; } @@ -470,35 +467,35 @@ public function setCloseOnDestruct($flag) public function setTlsOptions(array $tlsOptions) { - if($this->connected) { - throw new CredisException('Cannot change TLS options after a connection has already been established.'); - } - $this->tlsOptions = $tlsOptions; + if ($this->connected) { + throw new CredisException('Cannot change TLS options after a connection has already been established.'); + } + $this->tlsOptions = $tlsOptions; } protected function convertHost() { if (preg_match('#^(tcp|tls|ssl|tlsv\d(?:\.\d)?|unix)://(.+)$#', $this->host, $matches)) { $this->isTls = strpos($matches[1], 'tls') === 0 || strpos($matches[1], 'ssl') === 0; - if($this->isTls || $matches[1] === 'tcp') { + if ($this->isTls || $matches[1] === 'tcp') { $this->scheme = $matches[1]; - if ( ! preg_match('#^([^:]+)(:([0-9]+))?(/(.+))?$#', $matches[2], $matches)) { - throw new CredisException('Invalid host format; expected '.$this->scheme.'://host[:port][/persistence_identifier]'); + if (!preg_match('#^([^:]+)(:([0-9]+))?(/(.+))?$#', $matches[2], $matches)) { + throw new CredisException('Invalid host format; expected ' . $this->scheme . '://host[:port][/persistence_identifier]'); } $this->host = $matches[1]; - $this->port = (int) (isset($matches[3]) ? $matches[3] : $this->port); + $this->port = (int)(isset($matches[3]) ? $matches[3] : $this->port); $this->persistent = isset($matches[5]) ? $matches[5] : $this->persistent; } else { $this->host = $matches[2]; - $this->port = NULL; + $this->port = null; $this->scheme = 'unix'; - if (substr($this->host,0,1) != '/') { + if (substr($this->host, 0, 1) != '/') { throw new CredisException('Invalid unix socket format; expected unix:///path/to/redis.sock'); } } } - if ($this->port !== NULL && substr($this->host,0,1) == '/') { - $this->port = NULL; + if ($this->port !== null && substr($this->host, 0, 1) == '/') { + $this->port = null; $this->scheme = 'unix'; } if (!$this->scheme) { @@ -507,8 +504,8 @@ protected function convertHost() } /** - * @throws CredisException * @return Credis_Client + * @throws CredisException */ public function connect() { @@ -519,12 +516,12 @@ public function connect() $tlsOptions = $this->isTls ? $this->tlsOptions : []; if ($this->standalone) { $flags = STREAM_CLIENT_CONNECT; - $remote_socket = $this->port === NULL - ? $this->scheme.'://'.$this->host - : $this->scheme.'://'.$this->host.':'.$this->port; - if ($this->persistent && $this->port !== NULL) { + $remote_socket = $this->port === null + ? $this->scheme . '://' . $this->host + : $this->scheme . '://' . $this->host . ':' . $this->port; + if ($this->persistent && $this->port !== null) { // Persistent connections to UNIX sockets are not supported - $remote_socket .= '/'.$this->persistent; + $remote_socket .= '/' . $this->persistent; $flags = $flags | STREAM_CLIENT_PERSISTENT; } if ($this->isTls) { @@ -543,36 +540,29 @@ public function connect() if ($result && $this->isTls) { $this->sslMeta = stream_context_get_options($context); } - } - else { - if ( ! $this->redis) { - $this->redis = new Redis; + } else { + if (!$this->redis) { + $this->redis = new Redis(); } $socketTimeout = $this->timeout ?: 0.0; - try - { - if ($this->oldPhpRedis) - { - $result = $this->persistent - ? $this->redis->pconnect($this->host, (int)$this->port, $socketTimeout, $this->persistent) - : $this->redis->connect($this->host, (int)$this->port, $socketTimeout); - } - else - { - // 7th argument is non-documented TLS options. But it only exists on the newer versions of phpredis - if ($tlsOptions) { - $context = ['stream' => $tlsOptions]; + try { + if ($this->oldPhpRedis) { + $result = $this->persistent + ? $this->redis->pconnect($this->host, (int)$this->port, $socketTimeout, $this->persistent) + : $this->redis->connect($this->host, (int)$this->port, $socketTimeout); } else { - $context = []; + // 7th argument is non-documented TLS options. But it only exists on the newer versions of phpredis + if ($tlsOptions) { + $context = ['stream' => $tlsOptions]; + } else { + $context = []; + } + /** @noinspection PhpMethodParametersCountMismatchInspection */ + $result = $this->persistent + ? $this->redis->pconnect($this->scheme . '://' . $this->host, (int)$this->port, $socketTimeout, $this->persistent, 0, 0.0, $context) + : $this->redis->connect($this->scheme . '://' . $this->host, (int)$this->port, $socketTimeout, null, 0, 0.0, $context); } - /** @noinspection PhpMethodParametersCountMismatchInspection */ - $result = $this->persistent - ? $this->redis->pconnect($this->scheme.'://'.$this->host, (int)$this->port, $socketTimeout, $this->persistent, 0, 0.0, $context) - : $this->redis->connect($this->scheme.'://'.$this->host, (int)$this->port, $socketTimeout, null, 0, 0.0, $context); - } - } - catch(Exception $e) - { + } catch (Exception $e) { // Some applications will capture the php error that phpredis can sometimes generate and throw it as an Exception $result = false; $errno = 1; @@ -581,37 +571,39 @@ public function connect() } // Use recursion for connection retries - if ( ! $result) { + if (!$result) { $this->connectFailures++; if ($this->connectFailures <= $this->maxConnectRetries) { return $this->connect(); } $failures = $this->connectFailures; $this->connectFailures = 0; - throw new CredisException(sprintf("Connection to Redis%s %s://%s failed after %s failures.%s", + throw new CredisException(sprintf( + "Connection to Redis%s %s://%s failed after %s failures.%s", $this->standalone ? ' standalone' : '', $this->scheme, - $this->host.($this->port ? ':'.$this->port : ''), + $this->host . ($this->port ? ':' . $this->port : ''), $failures, (isset($errno) && isset($errstr) ? "Last Error : ({$errno}) {$errstr}" : "") )); } $this->connectFailures = 0; - $this->connected = TRUE; + $this->connected = true; // Set read timeout if ($this->readTimeout) { $this->setReadTimeout($this->readTimeout); } - if($this->authPassword) { + if ($this->authPassword) { $this->auth($this->authPassword, $this->authUsername); } - if($this->selectedDb !== 0) { + if ($this->selectedDb !== 0) { $this->select($this->selectedDb); } return $this; } + /** * @return bool */ @@ -619,13 +611,14 @@ public function isConnected() { return $this->connected; } + /** * Set the read timeout for the connection. Use 0 to disable timeouts entirely (or use a very long timeout * if not supported). * * @param float $timeout 0 (or -1) for no timeout, otherwise number of seconds - * @throws CredisException * @return Credis_Client + * @throws CredisException */ public function setReadTimeout($timeout) { @@ -636,9 +629,9 @@ public function setReadTimeout($timeout) if ($this->isConnected()) { if ($this->standalone) { $timeout = $timeout <= 0 ? 315360000 : $timeout; // Ten-year timeout - stream_set_blocking($this->redis, TRUE); - stream_set_timeout($this->redis, (int) floor($timeout), ($timeout - floor($timeout)) * 1000000); - } else if (defined('Redis::OPT_READ_TIMEOUT')) { + stream_set_blocking($this->redis, true); + stream_set_timeout($this->redis, (int)floor($timeout), ($timeout - floor($timeout)) * 1000000); + } elseif (defined('Redis::OPT_READ_TIMEOUT')) { // supported in phpredis 2.2.3 // a timeout value of -1 means reads will not timeout $timeout = $timeout == 0 ? -1 : $timeout; @@ -651,10 +644,10 @@ public function setReadTimeout($timeout) /** * @return bool */ - public function close($force = FALSE) + public function close($force = false) { - $result = TRUE; - if ($this->redis && ($force || $this->connected && ! $this->persistent)) { + $result = true; + if ($this->redis && ($force || $this->connected && !$this->persistent)) { try { if (is_callable(array($this->redis, 'close'))) { $this->redis->close(); @@ -665,7 +658,7 @@ public function close($force = FALSE) } catch (Exception $e) { ; // Ignore exceptions on close } - $this->connected = $this->usePipeline = $this->isMulti = $this->isWatching = FALSE; + $this->connected = $this->usePipeline = $this->isMulti = $this->isWatching = false; } return $result; } @@ -682,15 +675,15 @@ public function close($force = FALSE) * @param string|null $alias * @return $this */ - public function renameCommand($command, $alias = NULL) + public function renameCommand($command, $alias = null) { - if ( ! $this->standalone) { + if (!$this->standalone) { $this->forceStandalone(); } - if ($alias === NULL) { + if ($alias === null) { $this->renamedCommands = $command; } else { - if ( ! $this->renamedCommands) { + if (!$this->renamedCommands) { $this->renamedCommands = array(); } $this->renamedCommands[$command] = $alias; @@ -707,12 +700,12 @@ public function getRenamedCommand($command) static $map; // Command renaming not enabled - if ($this->renamedCommands === NULL) { + if ($this->renamedCommands === null) { return $command; } // Initialize command map - if ($map === NULL) { + if ($map === null) { if (is_array($this->renamedCommands)) { $map = $this->renamedCommands; } else { @@ -721,17 +714,15 @@ public function getRenamedCommand($command) } // Generate and return cached result - if ( ! isset($map[$command])) { + if (!isset($map[$command])) { // String means all commands are hashed with salted md5 if (is_string($this->renamedCommands)) { - $map[$command] = md5($this->renamedCommands.$command); - } - // Would already be set in $map if it was intended to be renamed - else if (is_array($this->renamedCommands)) { + $map[$command] = md5($this->renamedCommands . $command); + } // Would already be set in $map if it was intended to be renamed + elseif (is_array($this->renamedCommands)) { return $command; - } - // User-supplied function - else if (is_callable($this->renamedCommands)) { + } // User-supplied function + elseif (is_callable($this->renamedCommands)) { $map[$command] = call_user_func($this->renamedCommands, $command); } } @@ -747,7 +738,7 @@ public function auth($password, $username = null) { if ($username !== null) { $response = $this->__call('auth', array($username, $password)); - $this->authUsername= $username; + $this->authUsername = $username; } else { $response = $this->__call('auth', array($password)); } @@ -762,7 +753,7 @@ public function auth($password, $username = null) public function select($index) { $response = $this->__call('select', array($index)); - $this->selectedDb = (int) $index; + $this->selectedDb = (int)$index; return $response; } @@ -772,9 +763,9 @@ public function select($index) */ public function pUnsubscribe() { - list($command, $channel, $subscribedChannels) = $this->__call('punsubscribe', func_get_args()); - $this->subscribed = $subscribedChannels > 0; - return array($command, $channel, $subscribedChannels); + list($command, $channel, $subscribedChannels) = $this->__call('punsubscribe', func_get_args()); + $this->subscribed = $subscribedChannels > 0; + return array($command, $channel, $subscribedChannels); } /** @@ -789,16 +780,16 @@ public function scan(&$Iterator, $pattern = null, $count = null) } /** - * @param int $Iterator - * @param string $field - * @param string $pattern - * @param int $count - * @return bool|array - */ - public function hscan(&$Iterator, $field, $pattern = null, $count = null) - { - return $this->__call('hscan', array($field, &$Iterator, $pattern, $count)); - } + * @param int $Iterator + * @param string $field + * @param string $pattern + * @param int $count + * @return bool|array + */ + public function hscan(&$Iterator, $field, $pattern = null, $count = null) + { + return $this->__call('hscan', array($field, &$Iterator, $pattern, $count)); + } /** * @param int $Iterator @@ -832,7 +823,7 @@ public function zscan(&$Iterator, $field, $pattern = null, $count = null) */ public function pSubscribe($patterns, $callback) { - if ( ! $this->standalone) { + if (!$this->standalone) { return $this->__call('pSubscribe', array((array)$patterns, $callback)); } @@ -845,7 +836,7 @@ public function pSubscribe($patterns, $callback) list($command, $pattern, $status) = $this->__call('psubscribe', array($patterns)); } $this->subscribed = $status > 0; - if ( ! $status) { + if (!$status) { throw new CredisException('Invalid pSubscribe response.'); } } @@ -865,20 +856,20 @@ public function pSubscribe($patterns, $callback) */ public function unsubscribe() { - list($command, $channel, $subscribedChannels) = $this->__call('unsubscribe', func_get_args()); - $this->subscribed = $subscribedChannels > 0; - return array($command, $channel, $subscribedChannels); + list($command, $channel, $subscribedChannels) = $this->__call('unsubscribe', func_get_args()); + $this->subscribed = $subscribedChannels > 0; + return array($command, $channel, $subscribedChannels); } /** * @param string|array $channels * @param $callback - * @throws CredisException * @return $this|array|bool|Credis_Client|mixed|null|string + * @throws CredisException */ public function subscribe($channels, $callback) { - if ( ! $this->standalone) { + if (!$this->standalone) { return $this->__call('subscribe', array((array)$channels, $callback)); } @@ -891,7 +882,7 @@ public function subscribe($channels, $callback) list($command, $channel, $status) = $this->__call('subscribe', array($channels)); } $this->subscribed = $status > 0; - if ( ! $status) { + if (!$status) { throw new CredisException('Invalid subscribe response.'); } } @@ -911,27 +902,24 @@ public function subscribe($channels, $callback) */ public function ping($name = null) { - return $this->__call('ping', $name ? array($name) : array()); + return $this->__call('ping', $name ? array($name) : array()); } - /** - * @param string $command - * @param array $args - * - * @return array|Credis_Client - */ - public function rawCommand($command, array $args) - { - if($this->standalone) - { - return $this->__call($command, $args); - } - else - { - \array_unshift($args, $command); - return $this->__call('rawCommand', $args); - } - } + /** + * @param string $command + * @param array $args + * + * @return array|Credis_Client + */ + public function rawCommand($command, array $args) + { + if ($this->standalone) { + return $this->__call($command, $args); + } else { + \array_unshift($args, $command); + return $this->__call('rawCommand', $args); + } + } public function __call($name, $args) { @@ -941,26 +929,25 @@ public function __call($name, $args) $name = strtolower($name); // Send request via native PHP - if($this->standalone) - { + if ($this->standalone) { $trackedArgs = array(); switch ($name) { case 'eval': case 'evalsha': $script = array_shift($args); - $keys = (array) array_shift($args); - $eArgs = (array) array_shift($args); + $keys = (array)array_shift($args); + $eArgs = (array)array_shift($args); $args = array($script, count($keys), $keys, $eArgs); break; case 'zinterstore': case 'zunionstore': $dest = array_shift($args); - $keys = (array) array_shift($args); + $keys = (array)array_shift($args); $weights = array_shift($args); $aggregate = array_shift($args); $args = array($dest, count($keys), $keys); if ($weights) { - $args[] = (array) $weights; + $args[] = (array)$weights; } if ($aggregate) { $args[] = $aggregate; @@ -974,9 +961,9 @@ public function __call($name, $args) } elseif (count($args) === 3 && is_array($args[2])) { $tmp_args = $args; $args = array($tmp_args[0], $tmp_args[1]); - foreach ($tmp_args[2] as $k=>$v) { + foreach ($tmp_args[2] as $k => $v) { if (is_string($k)) { - $args[] = array($k,$v); + $args[] = array($k, $v); } elseif (is_int($k)) { $args[] = $v; } @@ -986,18 +973,15 @@ public function __call($name, $args) break; case 'scan': $trackedArgs = array(&$args[0]); - if (empty($trackedArgs[0])) - { + if (empty($trackedArgs[0])) { $trackedArgs[0] = 0; } $eArgs = array($trackedArgs[0]); - if (!empty($args[1])) - { + if (!empty($args[1])) { $eArgs[] = 'MATCH'; $eArgs[] = $args[1]; } - if (!empty($args[2])) - { + if (!empty($args[2])) { $eArgs[] = 'COUNT'; $eArgs[] = $args[2]; } @@ -1006,24 +990,21 @@ public function __call($name, $args) case 'sscan': case 'zscan': case 'hscan': - $trackedArgs = array(&$args[1]); - if (empty($trackedArgs[0])) - { - $trackedArgs[0] = 0; - } - $eArgs = array($args[0],$trackedArgs[0]); - if (!empty($args[2])) - { - $eArgs[] = 'MATCH'; - $eArgs[] = $args[2]; - } - if (!empty($args[3])) - { - $eArgs[] = 'COUNT'; - $eArgs[] = $args[3]; - } - $args = $eArgs; - break; + $trackedArgs = array(&$args[1]); + if (empty($trackedArgs[0])) { + $trackedArgs[0] = 0; + } + $eArgs = array($args[0], $trackedArgs[0]); + if (!empty($args[2])) { + $eArgs[] = 'MATCH'; + $eArgs[] = $args[2]; + } + if (!empty($args[3])) { + $eArgs[] = 'COUNT'; + $eArgs[] = $args[3]; + } + $args = $eArgs; + break; case 'zrangebyscore': case 'zrevrangebyscore': case 'zrange': @@ -1042,17 +1023,14 @@ public function __call($name, $args) } break; case 'mget': - if (isset($args[0]) && is_array($args[0])) - { + if (isset($args[0]) && is_array($args[0])) { $args = array_values($args[0]); } break; case 'hmset': - if (isset($args[1]) && is_array($args[1])) - { + if (isset($args[1]) && is_array($args[1])) { $cArgs = array(); - foreach($args[1] as $id => $value) - { + foreach ($args[1] as $id => $value) { $cArgs[] = $id; $cArgs[] = $value; } @@ -1067,8 +1045,7 @@ public function __call($name, $args) break; case 'hmget': // hmget needs to track the keys for rehydrating the results - if (isset($args[1])) - { + if (isset($args[1])) { $trackedArgs = $args[1]; } break; @@ -1077,62 +1054,53 @@ public function __call($name, $args) $args = self::_flattenArguments($args); // In pipeline mode - if($this->usePipeline) - { - if($name === 'pipeline') { + if ($this->usePipeline) { + if ($name === 'pipeline') { throw new CredisException('A pipeline is already in use and only one pipeline is supported.'); - } - else if($name === 'exec') { - if($this->isMulti) { + } elseif ($name === 'exec') { + if ($this->isMulti) { $this->commandNames[] = array($name, $trackedArgs); $this->commands .= self::_prepare_command(array($this->getRenamedCommand($name))); } // Write request - if($this->commands) { + if ($this->commands) { $this->write_command($this->commands); } - $this->commands = NULL; + $this->commands = null; // Read response $queuedResponses = array(); $response = array(); - foreach($this->commandNames as $command) { + foreach ($this->commandNames as $command) { list($name, $arguments) = $command; $result = $this->read_reply($name, true); - if ($result !== null) - { + if ($result !== null) { $result = $this->decode_reply($name, $result, $arguments); - } - else - { + } else { $queuedResponses[] = $command; } $response[] = $result; } - if($this->isMulti) { + if ($this->isMulti) { $response = array_pop($response); - foreach($queuedResponses as $key => $command) - { + foreach ($queuedResponses as $key => $command) { list($name, $arguments) = $command; $response[$key] = $this->decode_reply($name, $response[$key], $arguments); } } - $this->commandNames = NULL; - $this->usePipeline = $this->isMulti = FALSE; + $this->commandNames = null; + $this->usePipeline = $this->isMulti = false; return $response; - } - else if ($name === 'discard') - { - $this->commands = NULL; - $this->commandNames = NULL; - $this->usePipeline = $this->isMulti = FALSE; - } - else { - if($name === 'multi') { - $this->isMulti = TRUE; + } elseif ($name === 'discard') { + $this->commands = null; + $this->commandNames = null; + $this->usePipeline = $this->isMulti = false; + } else { + if ($name === 'multi') { + $this->isMulti = true; } array_unshift($args, $this->getRenamedCommand($name)); $this->commandNames[] = array($name, $trackedArgs); @@ -1142,17 +1110,16 @@ public function __call($name, $args) } // Start pipeline mode - if($name === 'pipeline') - { - $this->usePipeline = TRUE; + if ($name === 'pipeline') { + $this->usePipeline = true; $this->commandNames = array(); $this->commands = ''; return $this; } // If unwatching, allow reconnect with no error thrown - if($name === 'unwatch') { - $this->isWatching = FALSE; + if ($name === 'unwatch') { + $this->isWatching = false; } // Non-pipeline mode @@ -1163,25 +1130,20 @@ public function __call($name, $args) $response = $this->decode_reply($name, $response, $trackedArgs); // Watch mode disables reconnect so error is thrown - if($name == 'watch') { - $this->isWatching = TRUE; - } - // Transaction mode - else if($this->isMulti && ($name == 'exec' || $name == 'discard')) { - $this->isMulti = FALSE; - } - // Started transaction - else if($this->isMulti || $name == 'multi') { - $this->isMulti = TRUE; + if ($name == 'watch') { + $this->isWatching = true; + } // Transaction mode + elseif ($this->isMulti && ($name == 'exec' || $name == 'discard')) { + $this->isMulti = false; + } // Started transaction + elseif ($this->isMulti || $name == 'multi') { + $this->isMulti = true; $response = $this; } - } - - // Send request via phpredis client - else - { + } // Send request via phpredis client + else { // Tweak arguments - switch($name) { + switch ($name) { case 'get': // optimize common cases case 'set': case 'hget': @@ -1194,11 +1156,10 @@ public function __call($name, $args) case 'del': case 'zrangebyscore': case 'zrevrangebyscore': - break; + break; case 'zrange': case 'zrevrange': - if (isset($args[3]) && is_array($args[3])) - { + if (isset($args[3]) && is_array($args[3])) { $cArgs = $args[3]; $args[3] = !empty($cArgs['withscores']); } @@ -1209,18 +1170,18 @@ public function __call($name, $args) $cArgs = array(); $cArgs[] = array_shift($args); // destination $cArgs[] = array_shift($args); // keys - if(isset($args[0]) and isset($args[0]['weights'])) { - $cArgs[] = (array) $args[0]['weights']; + if (isset($args[0]) and isset($args[0]['weights'])) { + $cArgs[] = (array)$args[0]['weights']; } else { $cArgs[] = null; } - if(isset($args[0]) and isset($args[0]['aggregate'])) { + if (isset($args[0]) and isset($args[0]['aggregate'])) { $cArgs[] = strtoupper($args[0]['aggregate']); } $args = $cArgs; break; case 'mget': - if(isset($args[0]) && ! is_array($args[0])) { + if (isset($args[0]) && !is_array($args[0])) { $args = array($args); } break; @@ -1266,30 +1227,29 @@ public function __call($name, $args) try { // Proxy pipeline mode to the phpredis library - if($name == 'pipeline' || $name == 'multi') { - if($this->isMulti) { + if ($name == 'pipeline' || $name == 'multi') { + if ($this->isMulti) { return $this; } else { - $this->isMulti = TRUE; + $this->isMulti = true; $this->redisMulti = call_user_func_array(array($this->redis, $name), $args); return $this; } - } - else if($name == 'exec' || $name == 'discard') { - $this->isMulti = FALSE; + } elseif ($name == 'exec' || $name == 'discard') { + $this->isMulti = false; $response = $this->redisMulti->$name(); - $this->redisMulti = NULL; + $this->redisMulti = null; #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n"; return $response; } // Use aliases to be compatible with phpredis wrapper - if(isset($this->wrapperMethods[$name])) { + if (isset($this->wrapperMethods[$name])) { $name = $this->wrapperMethods[$name]; } // Multi and pipeline return self for chaining - if($this->isMulti) { + if ($this->isMulti) { call_user_func_array(array($this->redisMulti, $name), $args); return $this; } @@ -1308,11 +1268,10 @@ public function __call($name, $args) throw $e; } } - } - // Wrap exceptions - catch(RedisException $e) { + } // Wrap exceptions + catch (RedisException $e) { $code = 0; - if ( ! ($result = $this->redis->IsConnected())) { + if (!($result = $this->redis->IsConnected())) { $this->close(true); $code = CredisException::CODE_DISCONNECTED; } @@ -1322,49 +1281,49 @@ public function __call($name, $args) #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n"; // change return values where it is too difficult to minim in standalone mode - switch($name) - { + switch ($name) { case 'type': $typeMap = array( - self::TYPE_NONE, - self::TYPE_STRING, - self::TYPE_SET, - self::TYPE_LIST, - self::TYPE_ZSET, - self::TYPE_HASH, + self::TYPE_NONE, + self::TYPE_STRING, + self::TYPE_SET, + self::TYPE_LIST, + self::TYPE_ZSET, + self::TYPE_HASH, ); $response = $typeMap[$response]; break; - // Handle scripting errors + // Handle scripting errors case 'eval': case 'evalsha': case 'script': $error = $this->redis->getLastError(); $this->redis->clearLastError(); - if ($error && substr($error,0,8) == 'NOSCRIPT') { - $response = NULL; - } else if ($error) { + if ($error && substr($error, 0, 8) == 'NOSCRIPT') { + $response = null; + } elseif ($error) { throw new CredisException($error); } break; case 'exists': // smooth over phpredis-v4 vs earlier difference to match documented credis return results - $response = (int) $response; + $response = (int)$response; break; case 'ping': if ($response) { - if ($response === true) { - $response = isset($args[0]) ? $args[0] : "PONG"; - } else if ($response[0] === '+') { - $response = substr($response, 1); - } + if ($response === true) { + $response = isset($args[0]) ? $args[0] : "PONG"; + } elseif ($response[0] === '+') { + $response = substr($response, 1); + } } break; case 'auth': - if (is_bool($response) && $response === true){ + if (is_bool($response) && $response === true) { $this->redis->clearLastError(); } + // no break default: $error = $this->redis->getLastError(); $this->redis->clearLastError(); @@ -1381,28 +1340,28 @@ public function __call($name, $args) protected function write_command($command) { // Reconnect on lost connection (Redis server "timeout" exceeded since last command) - if(feof($this->redis)) { + if (feof($this->redis)) { // If a watch or transaction was in progress and connection was lost, throw error rather than reconnect // since transaction/watch state will be lost. - if(($this->isMulti && ! $this->usePipeline) || $this->isWatching) { + if (($this->isMulti && !$this->usePipeline) || $this->isWatching) { $this->close(true); throw new CredisException('Lost connection to Redis server during watch or transaction.'); } $this->close(true); $this->connect(); - if($this->authPassword) { + if ($this->authPassword) { $this->auth($this->authPassword); } - if($this->selectedDb != 0) { + if ($this->selectedDb != 0) { $this->select($this->selectedDb); } } $commandLen = strlen($command); - $lastFailed = FALSE; + $lastFailed = false; for ($written = 0; $written < $commandLen; $written += $fwrite) { $fwrite = fwrite($this->redis, substr($command, $written)); - if ($fwrite === FALSE || ($fwrite == 0 && $lastFailed)) { + if ($fwrite === false || ($fwrite == 0 && $lastFailed)) { $this->close(true); throw new CredisException('Failed to write entire command to stream'); } @@ -1413,7 +1372,7 @@ protected function write_command($command) protected function read_reply($name = '', $returnQueued = false) { $reply = fgets($this->redis); - if($reply === FALSE) { + if ($reply === false) { $info = stream_get_meta_data($this->redis); $this->close(true); if ($info['timed_out']) { @@ -1428,69 +1387,71 @@ protected function read_reply($name = '', $returnQueued = false) switch ($replyType) { /* Error reply */ case '-': - if($this->isMulti || $this->usePipeline) { - $response = FALSE; - } else if ($name == 'evalsha' && substr($reply,0,9) == '-NOSCRIPT') { - $response = NULL; + if ($this->isMulti || $this->usePipeline) { + $response = false; + } elseif ($name == 'evalsha' && substr($reply, 0, 9) == '-NOSCRIPT') { + $response = null; } else { - throw new CredisException(substr($reply,0,4) == '-ERR' ? 'ERR '.substr($reply, 5) : substr($reply,1)); + throw new CredisException(substr($reply, 0, 4) == '-ERR' ? 'ERR ' . substr($reply, 5) : substr($reply, 1)); } break; - /* Inline reply */ + /* Inline reply */ case '+': $response = substr($reply, 1); - if($response == 'OK') { - return TRUE; + if ($response == 'OK') { + return true; } - if($response == 'QUEUED') { + if ($response == 'QUEUED') { return $returnQueued ? null : true; } break; - /* Bulk reply */ + /* Bulk reply */ case '$': - if ($reply == '$-1') return FALSE; - $size = (int) substr($reply, 1); + if ($reply == '$-1') { + return false; + } + $size = (int)substr($reply, 1); $response = stream_get_contents($this->redis, $size + 2); - if( ! $response) { + if (!$response) { $this->close(true); throw new CredisException('Error reading reply.'); } $response = substr($response, 0, $size); break; - /* Multi-bulk reply */ + /* Multi-bulk reply */ case '*': $count = substr($reply, 1); - if ($count == '-1') return FALSE; + if ($count == '-1') { + return false; + } $response = array(); for ($i = 0; $i < $count; $i++) { - $response[] = $this->read_reply(); + $response[] = $this->read_reply(); } break; - /* Integer reply */ + /* Integer reply */ case ':': $response = intval(substr($reply, 1)); break; default: - throw new CredisException('Invalid response: '.print_r($reply, TRUE)); + throw new CredisException('Invalid response: ' . print_r($reply, true)); break; } return $response; } - protected function decode_reply($name, $response, array &$arguments = array() ) + protected function decode_reply($name, $response, array &$arguments = array()) { // Smooth over differences between phpredis and standalone response - switch ($name) - { + switch ($name) { case '': // Minor optimization for multi-bulk replies break; case 'config': case 'hgetall': $keys = $values = array(); - while ($response) - { + while ($response) { $keys[] = array_shift($response); $values[] = array_shift($response); } @@ -1499,10 +1460,8 @@ protected function decode_reply($name, $response, array &$arguments = array() ) case 'info': $lines = explode("\r\n", trim($response, "\r\n")); $response = array(); - foreach ($lines as $line) - { - if (!$line || substr($line, 0, 1) == '#') - { + foreach ($lines as $line) { + if (!$line || substr($line, 0, 1) == '#') { continue; } list($key, $value) = explode(':', $line, 2); @@ -1510,17 +1469,16 @@ protected function decode_reply($name, $response, array &$arguments = array() ) } break; case 'ttl': - if ($response === -1) - { + if ($response === -1) { $response = false; } break; case 'hmget': - if (count($arguments) != count($response)) - { + if (count($arguments) != count($response)) { throw new CredisException( 'hmget arguments and response do not match: ' . print_r($arguments, true) . ' ' . print_r( - $response, true + $response, + true ) ); } @@ -1537,12 +1495,10 @@ protected function decode_reply($name, $response, array &$arguments = array() ) case 'zscan': $arguments[0] = intval(array_shift($response)); $response = empty($response[0]) ? array() : $response[0]; - if (!empty($response) && is_array($response)) - { + if (!empty($response) && is_array($response)) { $count = count($response); $out = array(); - for ($i = 0; $i < $count; $i += 2) - { + for ($i = 0; $i < $count; $i += 2) { $out[$response[$i]] = $response[$i + 1]; } $response = $out; @@ -1552,19 +1508,14 @@ protected function decode_reply($name, $response, array &$arguments = array() ) case 'zrevrangebyscore': case 'zrange': case 'zrevrange': - if (in_array('withscores', $arguments, true)) - { + if (in_array('withscores', $arguments, true)) { // Map array of values into key=>score list like phpRedis does $item = null; $out = array(); - foreach ($response as $value) - { - if ($item == null) - { + foreach ($response as $value) { + if ($item == null) { $item = $value; - } - else - { + } else { // 2nd value is the score $out[$item] = (float)$value; $item = null; diff --git a/Cluster.php b/Cluster.php index eda43b8..108fdd5 100644 --- a/Cluster.php +++ b/Cluster.php @@ -15,329 +15,324 @@ */ class Credis_Cluster { - /** - * Collection of Credis_Client objects attached to Redis servers - * @var Credis_Client[] - */ - protected $clients; - /** - * If a server is set as master, all write commands go to that one - * @var Credis_Client - */ - protected $masterClient; - /** - * Aliases of Credis_Client objects attached to Redis servers, used to route commands to specific servers - * @see Credis_Cluster::to - * @var array - */ - protected $aliases; + /** + * Collection of Credis_Client objects attached to Redis servers + * @var Credis_Client[] + */ + protected $clients; + /** + * If a server is set as master, all write commands go to that one + * @var Credis_Client + */ + protected $masterClient; + /** + * Aliases of Credis_Client objects attached to Redis servers, used to route commands to specific servers + * @see Credis_Cluster::to + * @var array + */ + protected $aliases; - /** - * Hash ring of Redis server nodes - * @var array - */ - protected $ring; + /** + * Hash ring of Redis server nodes + * @var array + */ + protected $ring; - /** - * Individual nodes of pointers to Redis servers on the hash ring - * @var array - */ - protected $nodes; + /** + * Individual nodes of pointers to Redis servers on the hash ring + * @var array + */ + protected $nodes; - /** - * The commands that are not subject to hashing - * @var array - * @access protected - */ - protected $dont_hash; + /** + * The commands that are not subject to hashing + * @var array + * @access protected + */ + protected $dont_hash; - /** - * Currently working cluster-wide database number. - * @var int - */ - protected $selectedDb = 0; + /** + * Currently working cluster-wide database number. + * @var int + */ + protected $selectedDb = 0; - /** - * Creates an interface to a cluster of Redis servers - * Each server should be in the format: - * array( - * 'host' => hostname, - * 'port' => port, - * 'db' => db, - * 'password' => password, - * 'timeout' => timeout, - * 'alias' => alias, - * 'persistent' => persistence_identifier, - * 'master' => master - * 'write_only'=> true/false - * ) - * - * @param array $servers The Redis servers in the cluster. - * @param int $replicas - * @param bool $standAlone - * @throws CredisException - */ - public function __construct($servers, $replicas = 128, $standAlone = false) - { - $this->clients = array(); - $this->masterClient = null; - $this->aliases = array(); - $this->ring = array(); - $this->replicas = (int)$replicas; - $client = null; - foreach ($servers as $server) + /** + * Creates an interface to a cluster of Redis servers + * Each server should be in the format: + * array( + * 'host' => hostname, + * 'port' => port, + * 'db' => db, + * 'password' => password, + * 'timeout' => timeout, + * 'alias' => alias, + * 'persistent' => persistence_identifier, + * 'master' => master + * 'write_only'=> true/false + * ) + * + * @param array $servers The Redis servers in the cluster. + * @param int $replicas + * @param bool $standAlone + * @throws CredisException + */ + public function __construct($servers, $replicas = 128, $standAlone = false) { - if(is_array($server)){ - $client = new Credis_Client( - $server['host'], - $server['port'], - isset($server['timeout']) ? $server['timeout'] : 2.5, - isset($server['persistent']) ? $server['persistent'] : '', - isset($server['db']) ? $server['db'] : 0, - isset($server['password']) ? $server['password'] : null - ); - if (isset($server['alias'])) { - $this->aliases[$server['alias']] = $client; - } - if(isset($server['master']) && $server['master'] === true){ - $this->masterClient = $client; - if(isset($server['write_only']) && $server['write_only'] === true){ - continue; + $this->clients = array(); + $this->masterClient = null; + $this->aliases = array(); + $this->ring = array(); + $this->replicas = (int)$replicas; + $client = null; + foreach ($servers as $server) { + if (is_array($server)) { + $client = new Credis_Client( + $server['host'], + $server['port'], + isset($server['timeout']) ? $server['timeout'] : 2.5, + isset($server['persistent']) ? $server['persistent'] : '', + isset($server['db']) ? $server['db'] : 0, + isset($server['password']) ? $server['password'] : null + ); + if (isset($server['alias'])) { + $this->aliases[$server['alias']] = $client; + } + if (isset($server['master']) && $server['master'] === true) { + $this->masterClient = $client; + if (isset($server['write_only']) && $server['write_only'] === true) { + continue; + } + } + } elseif ($server instanceof Credis_Client) { + $client = $server; + } else { + throw new CredisException('Server should either be an array or an instance of Credis_Client'); + } + if ($standAlone) { + $client->forceStandalone(); + } + $this->clients[] = $client; + for ($replica = 0; $replica <= $this->replicas; $replica++) { + $md5num = hexdec(substr(md5($client->getHost() . ':' . $client->getPort() . '-' . $replica), 0, 7)); + $this->ring[$md5num] = count($this->clients) - 1; } - } - } elseif($server instanceof Credis_Client){ - $client = $server; - } else { - throw new CredisException('Server should either be an array or an instance of Credis_Client'); - } - if($standAlone) { - $client->forceStandalone(); - } - $this->clients[] = $client; - for ($replica = 0; $replica <= $this->replicas; $replica++) { - $md5num = hexdec(substr(md5($client->getHost().':'.$client->getPort().'-'.$replica),0,7)); - $this->ring[$md5num] = count($this->clients)-1; - } - } - ksort($this->ring, SORT_NUMERIC); - $this->nodes = array_keys($this->ring); - $this->dont_hash = array_flip(array( - 'RANDOMKEY', 'DBSIZE', 'PIPELINE', 'EXEC', - 'SELECT', 'MOVE', 'FLUSHDB', 'FLUSHALL', - 'SAVE', 'BGSAVE', 'LASTSAVE', 'SHUTDOWN', - 'INFO', 'MONITOR', 'SLAVEOF' - )); - if($this->masterClient !== null && count($this->clients()) == 0){ - $this->clients[] = $this->masterClient; - for ($replica = 0; $replica <= $this->replicas; $replica++) { - $md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7)); - $this->ring[$md5num] = count($this->clients)-1; } + ksort($this->ring, SORT_NUMERIC); $this->nodes = array_keys($this->ring); + $this->dont_hash = array_flip(array( + 'RANDOMKEY', 'DBSIZE', 'PIPELINE', 'EXEC', + 'SELECT', 'MOVE', 'FLUSHDB', 'FLUSHALL', + 'SAVE', 'BGSAVE', 'LASTSAVE', 'SHUTDOWN', + 'INFO', 'MONITOR', 'SLAVEOF' + )); + if ($this->masterClient !== null && count($this->clients()) == 0) { + $this->clients[] = $this->masterClient; + for ($replica = 0; $replica <= $this->replicas; $replica++) { + $md5num = hexdec(substr(md5($this->masterClient->getHost() . ':' . $this->masterClient->getHost() . '-' . $replica), 0, 7)); + $this->ring[$md5num] = count($this->clients) - 1; + } + $this->nodes = array_keys($this->ring); + } } - } - /** - * @param Credis_Client $masterClient - * @param bool $writeOnly - * @return Credis_Cluster - */ - public function setMasterClient(Credis_Client $masterClient, $writeOnly=false) - { - if(!$masterClient instanceof Credis_Client){ - throw new CredisException('Master client should be an instance of Credis_Client'); - } - $this->masterClient = $masterClient; - if (!isset($this->aliases['master'])) { - $this->aliases['master'] = $masterClient; - } - if(!$writeOnly){ - $this->clients[] = $this->masterClient; - for ($replica = 0; $replica <= $this->replicas; $replica++) { - $md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7)); - $this->ring[$md5num] = count($this->clients)-1; + /** + * @param Credis_Client $masterClient + * @param bool $writeOnly + * @return Credis_Cluster + */ + public function setMasterClient(Credis_Client $masterClient, $writeOnly = false) + { + if (!$masterClient instanceof Credis_Client) { + throw new CredisException('Master client should be an instance of Credis_Client'); } - $this->nodes = array_keys($this->ring); - } - return $this; - } - /** - * Get a client by index or alias. - * - * @param string|int $alias - * @throws CredisException - * @return Credis_Client - */ - public function client($alias) - { - if (is_int($alias) && isset($this->clients[$alias])) { - return $this->clients[$alias]; - } - else if (isset($this->aliases[$alias])) { - return $this->aliases[$alias]; + $this->masterClient = $masterClient; + if (!isset($this->aliases['master'])) { + $this->aliases['master'] = $masterClient; + } + if (!$writeOnly) { + $this->clients[] = $this->masterClient; + for ($replica = 0; $replica <= $this->replicas; $replica++) { + $md5num = hexdec(substr(md5($this->masterClient->getHost() . ':' . $this->masterClient->getHost() . '-' . $replica), 0, 7)); + $this->ring[$md5num] = count($this->clients) - 1; + } + $this->nodes = array_keys($this->ring); + } + return $this; } - throw new CredisException("Client $alias does not exist."); - } - /** - * Get an array of all clients - * - * @return array|Credis_Client[] - */ - public function clients() - { - return $this->clients; - } - - /** - * Execute a command on all clients - * - * @return array - */ - public function all() - { - $args = func_get_args(); - $name = array_shift($args); - $results = array(); - foreach($this->clients as $client) { - $results[] = call_user_func_array([$client, $name], $args); + /** + * Get a client by index or alias. + * + * @param string|int $alias + * @return Credis_Client + * @throws CredisException + */ + public function client($alias) + { + if (is_int($alias) && isset($this->clients[$alias])) { + return $this->clients[$alias]; + } elseif (isset($this->aliases[$alias])) { + return $this->aliases[$alias]; + } + throw new CredisException("Client $alias does not exist."); } - return $results; - } - /** - * Get the client that the key would hash to. - * - * @param string $key - * @return \Credis_Client - */ - public function byHash($key) - { - return $this->clients[$this->hash($key)]; - } + /** + * Get an array of all clients + * + * @return array|Credis_Client[] + */ + public function clients() + { + return $this->clients; + } - /** - * @param int $index - * @return void - */ - public function select($index) - { - $this->selectedDb = (int) $index; - } + /** + * Execute a command on all clients + * + * @return array + */ + public function all() + { + $args = func_get_args(); + $name = array_shift($args); + $results = array(); + foreach ($this->clients as $client) { + $results[] = call_user_func_array([$client, $name], $args); + } + return $results; + } - /** - * Execute a Redis command on the cluster with automatic consistent hashing and read/write splitting - * - * @param string $name - * @param array $args - * @return mixed - */ - public function __call($name, $args) - { - if($this->masterClient !== null && !$this->isReadOnlyCommand($name)){ - $client = $this->masterClient; - }elseif (count($this->clients()) == 1 || isset($this->dont_hash[strtoupper($name)]) || !isset($args[0])) { - $client = $this->clients[0]; + /** + * Get the client that the key would hash to. + * + * @param string $key + * @return \Credis_Client + */ + public function byHash($key) + { + return $this->clients[$this->hash($key)]; } - else { - $hashKey = $args[0]; - if (is_array($hashKey)) { - $hashKey = join('|', $hashKey); - } - $client = $this->byHash($hashKey); + + /** + * @param int $index + * @return void + */ + public function select($index) + { + $this->selectedDb = (int)$index; } - // Ensure that current client is working on the same database as expected. - if ($client->getSelectedDb() != $this->selectedDb) { - $client->select($this->selectedDb); + + /** + * Execute a Redis command on the cluster with automatic consistent hashing and read/write splitting + * + * @param string $name + * @param array $args + * @return mixed + */ + public function __call($name, $args) + { + if ($this->masterClient !== null && !$this->isReadOnlyCommand($name)) { + $client = $this->masterClient; + } elseif (count($this->clients()) == 1 || isset($this->dont_hash[strtoupper($name)]) || !isset($args[0])) { + $client = $this->clients[0]; + } else { + $hashKey = $args[0]; + if (is_array($hashKey)) { + $hashKey = join('|', $hashKey); + } + $client = $this->byHash($hashKey); + } + // Ensure that current client is working on the same database as expected. + if ($client->getSelectedDb() != $this->selectedDb) { + $client->select($this->selectedDb); + } + return call_user_func_array([$client, $name], $args); } - return call_user_func_array([$client, $name], $args); - } - /** - * Get client index for a key by searching ring with binary search - * - * @param string $key The key to hash - * @return int The index of the client object associated with the hash of the key - */ - public function hash($key) - { - $needle = hexdec(substr(md5($key),0,7)); - $server = $min = 0; - $max = count($this->nodes) - 1; - while ($max >= $min) { - $position = (int) (($min + $max) / 2); - $server = $this->nodes[$position]; - if ($needle < $server) { - $max = $position - 1; - } - else if ($needle > $server) { - $min = $position + 1; - } - else { - break; - } + /** + * Get client index for a key by searching ring with binary search + * + * @param string $key The key to hash + * @return int The index of the client object associated with the hash of the key + */ + public function hash($key) + { + $needle = hexdec(substr(md5($key), 0, 7)); + $server = $min = 0; + $max = count($this->nodes) - 1; + while ($max >= $min) { + $position = (int)(($min + $max) / 2); + $server = $this->nodes[$position]; + if ($needle < $server) { + $max = $position - 1; + } elseif ($needle > $server) { + $min = $position + 1; + } else { + break; + } + } + return $this->ring[$server]; } - return $this->ring[$server]; - } - public function isReadOnlyCommand($command) - { - static $readOnlyCommands = array( - 'DBSIZE' => true, - 'INFO' => true, - 'MONITOR' => true, - 'EXISTS' => true, - 'TYPE' => true, - 'KEYS' => true, - 'SCAN' => true, - 'RANDOMKEY' => true, - 'TTL' => true, - 'GET' => true, - 'MGET' => true, - 'SUBSTR' => true, - 'STRLEN' => true, - 'GETRANGE' => true, - 'GETBIT' => true, - 'LLEN' => true, - 'LRANGE' => true, - 'LINDEX' => true, - 'SCARD' => true, - 'SISMEMBER' => true, - 'SINTER' => true, - 'SUNION' => true, - 'SDIFF' => true, - 'SMEMBERS' => true, - 'SSCAN' => true, - 'SRANDMEMBER' => true, - 'ZRANGE' => true, - 'ZREVRANGE' => true, - 'ZRANGEBYSCORE' => true, - 'ZREVRANGEBYSCORE' => true, - 'ZCARD' => true, - 'ZSCORE' => true, - 'ZCOUNT' => true, - 'ZRANK' => true, - 'ZREVRANK' => true, - 'ZSCAN' => true, - 'HGET' => true, - 'HMGET' => true, - 'HEXISTS' => true, - 'HLEN' => true, - 'HKEYS' => true, - 'HVALS' => true, - 'HGETALL' => true, - 'HSCAN' => true, - 'PING' => true, - 'AUTH' => true, - 'SELECT' => true, - 'ECHO' => true, - 'QUIT' => true, - 'OBJECT' => true, - 'BITCOUNT' => true, - 'TIME' => true, - 'SORT' => true, - ); - return array_key_exists(strtoupper($command), $readOnlyCommands); - } + public function isReadOnlyCommand($command) + { + static $readOnlyCommands = array( + 'DBSIZE' => true, + 'INFO' => true, + 'MONITOR' => true, + 'EXISTS' => true, + 'TYPE' => true, + 'KEYS' => true, + 'SCAN' => true, + 'RANDOMKEY' => true, + 'TTL' => true, + 'GET' => true, + 'MGET' => true, + 'SUBSTR' => true, + 'STRLEN' => true, + 'GETRANGE' => true, + 'GETBIT' => true, + 'LLEN' => true, + 'LRANGE' => true, + 'LINDEX' => true, + 'SCARD' => true, + 'SISMEMBER' => true, + 'SINTER' => true, + 'SUNION' => true, + 'SDIFF' => true, + 'SMEMBERS' => true, + 'SSCAN' => true, + 'SRANDMEMBER' => true, + 'ZRANGE' => true, + 'ZREVRANGE' => true, + 'ZRANGEBYSCORE' => true, + 'ZREVRANGEBYSCORE' => true, + 'ZCARD' => true, + 'ZSCORE' => true, + 'ZCOUNT' => true, + 'ZRANK' => true, + 'ZREVRANK' => true, + 'ZSCAN' => true, + 'HGET' => true, + 'HMGET' => true, + 'HEXISTS' => true, + 'HLEN' => true, + 'HKEYS' => true, + 'HVALS' => true, + 'HGETALL' => true, + 'HSCAN' => true, + 'PING' => true, + 'AUTH' => true, + 'SELECT' => true, + 'ECHO' => true, + 'QUIT' => true, + 'OBJECT' => true, + 'BITCOUNT' => true, + 'TIME' => true, + 'SORT' => true, + ); + return array_key_exists(strtoupper($command), $readOnlyCommands); + } } - diff --git a/Module.php b/Module.php index 70b9ba9..f85a699 100644 --- a/Module.php +++ b/Module.php @@ -47,7 +47,7 @@ public function __destruct() */ public function setModule($moduleName) { - $this->moduleName = (string) $moduleName; + $this->moduleName = (string)$moduleName; return $this; } diff --git a/Sentinel.php b/Sentinel.php index b5f4116..995d218 100644 --- a/Sentinel.php +++ b/Sentinel.php @@ -78,22 +78,22 @@ class Credis_Sentinel */ protected $_redisVersion = null; - /** + /** * Connect with a Sentinel node. Sentinel will do the master and slave discovery * * @param Credis_Client $client * @param string $password (deprecated - use setClientPassword) * @throws CredisException */ - public function __construct(Credis_Client $client, $password = NULL, $username = NULL) + public function __construct(Credis_Client $client, $password = null, $username = null) { $client->forceStandalone(); // SENTINEL command not currently supported by phpredis - $this->_client = $client; - $this->_password = $password; - $this->_username = $username; - $this->_timeout = NULL; + $this->_client = $client; + $this->_password = $password; + $this->_username = $username; + $this->_timeout = null; $this->_persistent = ''; - $this->_db = 0; + $this->_db = 0; } /** @@ -150,8 +150,8 @@ public function setClientPassword($password) */ public function setClientUsername($username) { - $this->_username = $username; - return $this; + $this->_username = $username; + return $this; } /** @@ -160,19 +160,19 @@ public function setClientUsername($username) */ public function setReplicaCommand($replicaCmd) { - $this->_replicaCmd = $replicaCmd; - return $this; + $this->_replicaCmd = $replicaCmd; + return $this; } public function detectRedisVersion() { - if ($this->_redisVersion !== null && $this->_replicaCmd !== null) { - return; - } - $serverInfo = $this->info('server'); - $this->_redisVersion = $serverInfo['redis_version']; - // Redis v7+ renames the replica command to 'replicas' instead of 'slaves' - $this->_replicaCmd = version_compare($this->_redisVersion, '7.0.0', '>=') ? 'replicas' : 'slaves'; + if ($this->_redisVersion !== null && $this->_replicaCmd !== null) { + return; + } + $serverInfo = $this->info('server'); + $this->_redisVersion = $serverInfo['redis_version']; + // Redis v7+ renames the replica command to 'replicas' instead of 'slaves' + $this->_replicaCmd = version_compare($this->_redisVersion, '7.0.0', '>=') ? 'replicas' : 'slaves'; } /** @@ -195,7 +195,7 @@ public function forceStandalone() public function createMasterClient($name) { $master = $this->getMasterAddressByName($name); - if(!isset($master[0]) || !isset($master[1])){ + if (!isset($master[0]) || !isset($master[1])) { throw new CredisException('Master not found'); } return new Credis_Client($master[0], $master[1], $this->_timeout, $this->_persistent, $this->_db, $this->_password, $this->_username); @@ -208,7 +208,7 @@ public function createMasterClient($name) */ public function getMasterClient($name) { - if(!isset($this->_master[$name])){ + if (!isset($this->_master[$name])) { $this->_master[$name] = $this->createMasterClient($name); } return $this->_master[$name]; @@ -225,11 +225,11 @@ public function createSlaveClients($name) { $slaves = $this->slaves($name); $workingSlaves = array(); - foreach($slaves as $slave) { - if(!isset($slave[9])){ + foreach ($slaves as $slave) { + if (!isset($slave[9])) { throw new CredisException('Can\' retrieve slave status'); } - if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) { + if (!strstr($slave[9], 's_down') && !strstr($slave[9], 'disconnected')) { $workingSlaves[] = new Credis_Client($slave[3], $slave[5], $this->_timeout, $this->_persistent, $this->_db, $this->_password, $this->_username); } } @@ -243,7 +243,7 @@ public function createSlaveClients($name) */ public function getSlaveClients($name) { - if(!isset($this->_slaves[$name])){ + if (!isset($this->_slaves[$name])) { $this->_slaves[$name] = $this->createSlaveClients($name); } return $this->_slaves[$name]; @@ -265,27 +265,27 @@ public function getSlaveClients($name) * @throws CredisException * @deprecated */ - public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false, $masterOnly=false) + public function createCluster($name, $db = 0, $replicas = 128, $selectRandomSlave = true, $writeOnly = false, $masterOnly = false) { $clients = array(); $workingClients = array(); $master = $this->master($name); - if(strstr($master[9],'s_down') || strstr($master[9],'disconnected')) { + if (strstr($master[9], 's_down') || strstr($master[9], 'disconnected')) { throw new CredisException('The master is down'); } if (!$masterOnly) { $slaves = $this->slaves($name); - foreach($slaves as $slave){ - if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) { - $workingClients[] = array('host'=>$slave[3],'port'=>$slave[5],'master'=>false,'db'=>$db,'password'=>$this->_password); + foreach ($slaves as $slave) { + if (!strstr($slave[9], 's_down') && !strstr($slave[9], 'disconnected')) { + $workingClients[] = array('host' => $slave[3], 'port' => $slave[5], 'master' => false, 'db' => $db, 'password' => $this->_password); } } - if(count($workingClients)>0){ - if($selectRandomSlave){ - if(!$writeOnly){ - $workingClients[] = array('host'=>$master[3],'port'=>$master[5],'master'=>false,'db'=>$db,'password'=>$this->_password); + if (count($workingClients) > 0) { + if ($selectRandomSlave) { + if (!$writeOnly) { + $workingClients[] = array('host' => $master[3], 'port' => $master[5], 'master' => false, 'db' => $db, 'password' => $this->_password); } - $clients[] = $workingClients[rand(0,count($workingClients)-1)]; + $clients[] = $workingClients[rand(0, count($workingClients) - 1)]; } else { $clients = $workingClients; } @@ -293,8 +293,8 @@ public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=tr } else { $writeOnly = false; } - $clients[] = array('host'=>$master[3],'port'=>$master[5], 'db'=>$db ,'master'=>true,'write_only'=>$writeOnly,'password'=>$this->_password); - return new Credis_Cluster($clients,$replicas,$this->_standAlone); + $clients[] = array('host' => $master[3], 'port' => $master[5], 'db' => $db, 'master' => true, 'write_only' => $writeOnly, 'password' => $this->_password); + return new Credis_Cluster($clients, $replicas, $this->_standAlone); } /** @@ -309,9 +309,9 @@ public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=tr * @throws CredisException * @deprecated */ - public function getCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false, $masterOnly=false) + public function getCluster($name, $db = 0, $replicas = 128, $selectRandomSlave = true, $writeOnly = false, $masterOnly = false) { - if(!isset($this->_cluster[$name])){ + if (!isset($this->_cluster[$name])) { $this->_cluster[$name] = $this->createCluster($name, $db, $replicas, $selectRandomSlave, $writeOnly, $masterOnly); } return $this->_cluster[$name]; @@ -325,8 +325,8 @@ public function getCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, */ public function __call($name, $args) { - array_unshift($args,$name); - return call_user_func(array($this->_client,'sentinel'),$args); + array_unshift($args, $name); + return call_user_func(array($this->_client, 'sentinel'), $args); } /** @@ -338,8 +338,7 @@ public function __call($name, $args) */ public function info($section = null) { - if ($section) - { + if ($section) { return $this->_client->info($section); } return $this->_client->info(); @@ -362,9 +361,9 @@ public function masters() public function slaves($name) { if ($this->_replicaCmd === null) { - $this->detectRedisVersion(); + $this->detectRedisVersion(); } - return $this->_client->sentinel($this->_replicaCmd,$name); + return $this->_client->sentinel($this->_replicaCmd, $name); } /** @@ -374,7 +373,7 @@ public function slaves($name) */ public function master($name) { - return $this->_client->sentinel('master',$name); + return $this->_client->sentinel('master', $name); } /** @@ -384,7 +383,7 @@ public function master($name) */ public function getMasterAddressByName($name) { - return $this->_client->sentinel('get-master-addr-by-name',$name); + return $this->_client->sentinel('get-master-addr-by-name', $name); } /** @@ -403,7 +402,7 @@ public function ping() */ public function failover($name) { - return $this->_client->sentinel('failover',$name); + return $this->_client->sentinel('failover', $name); } /** diff --git a/tests/CredisClusterTest.php b/tests/CredisClusterTest.php index eeb1f52..aa76b1e 100644 --- a/tests/CredisClusterTest.php +++ b/tests/CredisClusterTest.php @@ -6,210 +6,207 @@ class CredisClusterTest extends CredisTestCommon { - /** @var Credis_Cluster */ - protected $cluster; + /** @var Credis_Cluster */ + protected $cluster; - protected function setUpInternal() - { - parent::setUpInternal(); + protected function setUpInternal() + { + parent::setUpInternal(); - $clients = array_slice($this->redisConfig,0,4); - $this->cluster = new Credis_Cluster($clients,2,$this->useStandalone); - } + $clients = array_slice($this->redisConfig, 0, 4); + $this->cluster = new Credis_Cluster($clients, 2, $this->useStandalone); + } - protected function tearDownInternal() - { - if($this->cluster) { - $this->cluster->flushAll(); - foreach($this->cluster->clients() as $client){ - if($client->isConnected()) { - $client->close(); + protected function tearDownInternal() + { + if ($this->cluster) { + $this->cluster->flushAll(); + foreach ($this->cluster->clients() as $client) { + if ($client->isConnected()) { + $client->close(); + } + } + $this->cluster = null; } - } - $this->cluster = NULL; } - } - public function testKeyHashing() - { - $this->tearDown(); - $this->cluster = new Credis_Cluster(array_slice($this->redisConfig, 0, 3), 2, $this->useStandalone); - $keys = array(); - $lines = explode("\n", file_get_contents("keys.test")); - foreach ($lines as $line) { - $pair = explode(':', trim($line)); - if (count($pair) >= 2) { - $keys[$pair[0]] = $pair[1]; - } - } - foreach ($keys as $key => $value) { - $this->assertTrue($this->cluster->set($key, $value)); - } - $this->cluster = new Credis_Cluster(array_slice($this->redisConfig, 0, 4), 2, true, $this->useStandalone); - $hits = 0; - foreach ($keys as $key => $value) { - if ($this->cluster->all('get',$key)) { - $hits++; - } - } - $this->assertEquals(count($keys),$hits); - } - public function testAlias() - { - $slicedConfig = array_slice($this->redisConfig, 0, 4); - foreach($slicedConfig as $config) { - $this->assertEquals($config['port'],$this->cluster->client($config['alias'])->getPort()); - } - foreach($slicedConfig as $offset => $config) { - $this->assertEquals($config['port'],$this->cluster->client($offset)->getPort()); - } - $alias = "non-existent-alias"; - $this->setExpectedExceptionShim('CredisException',"Client $alias does not exist."); - $this->cluster->client($alias); - } - public function testMasterSlave() - { - $this->tearDown(); - $this->cluster = new Credis_Cluster(array($this->redisConfig[0],$this->redisConfig[6]), 2, $this->useStandalone); - $this->assertTrue($this->cluster->client('master')->set('key','value')); - $this->waitForSlaveReplication(); - $this->assertEquals('value',$this->cluster->client('slave')->get('key')); - $this->assertEquals('value',$this->cluster->get('key')); - try - { - $this->cluster->client('slave')->set('key2', 'value'); - $this->fail('Writing to readonly slave'); - } - catch(CredisException $e) - { - } + public function testKeyHashing() + { + $this->tearDown(); + $this->cluster = new Credis_Cluster(array_slice($this->redisConfig, 0, 3), 2, $this->useStandalone); + $keys = array(); + $lines = explode("\n", file_get_contents("keys.test")); + foreach ($lines as $line) { + $pair = explode(':', trim($line)); + if (count($pair) >= 2) { + $keys[$pair[0]] = $pair[1]; + } + } + foreach ($keys as $key => $value) { + $this->assertTrue($this->cluster->set($key, $value)); + } + $this->cluster = new Credis_Cluster(array_slice($this->redisConfig, 0, 4), 2, true, $this->useStandalone); + $hits = 0; + foreach ($keys as $key => $value) { + if ($this->cluster->all('get', $key)) { + $hits++; + } + } + $this->assertEquals(count($keys), $hits); + } + public function testAlias() + { + $slicedConfig = array_slice($this->redisConfig, 0, 4); + foreach ($slicedConfig as $config) { + $this->assertEquals($config['port'], $this->cluster->client($config['alias'])->getPort()); + } + foreach ($slicedConfig as $offset => $config) { + $this->assertEquals($config['port'], $this->cluster->client($offset)->getPort()); + } + $alias = "non-existent-alias"; + $this->setExpectedExceptionShim('CredisException', "Client $alias does not exist."); + $this->cluster->client($alias); + } + public function testMasterSlave() + { + $this->tearDown(); + $this->cluster = new Credis_Cluster(array($this->redisConfig[0],$this->redisConfig[6]), 2, $this->useStandalone); + $this->assertTrue($this->cluster->client('master')->set('key', 'value')); + $this->waitForSlaveReplication(); + $this->assertEquals('value', $this->cluster->client('slave')->get('key')); + $this->assertEquals('value', $this->cluster->get('key')); + try { + $this->cluster->client('slave')->set('key2', 'value'); + $this->fail('Writing to readonly slave'); + } catch(CredisException $e) { + } - $this->tearDown(); - $writeOnlyConfig = $this->redisConfig[0]; - $writeOnlyConfig['write_only'] = true; - $this->cluster = new Credis_Cluster(array($writeOnlyConfig,$this->redisConfig[6]), 2, $this->useStandalone); - $this->assertTrue($this->cluster->client('master')->set('key','value')); - $this->waitForSlaveReplication(); - $this->assertEquals('value',$this->cluster->client('slave')->get('key')); - $this->assertEquals('value',$this->cluster->get('key')); - $this->setExpectedExceptionShim('CredisException'); - $this->assertFalse($this->cluster->client('slave')->set('key2','value')); - } - public function testMasterWithoutSlavesAndWriteOnlyFlag() - { - $this->tearDown(); - $writeOnlyConfig = $this->redisConfig[0]; - $writeOnlyConfig['write_only'] = true; - $this->cluster = new Credis_Cluster(array($writeOnlyConfig),2,$this->useStandalone); - $this->assertTrue($this->cluster->set('key','value')); - $this->assertEquals('value',$this->cluster->get('key')); - } - public function testDontHashForCodeCoverage() - { - if (method_exists($this,'assertIsArray')){ - $this->assertIsArray($this->cluster->info()); - } else { - $this->assertInternalType('array',$this->cluster->info()); + $this->tearDown(); + $writeOnlyConfig = $this->redisConfig[0]; + $writeOnlyConfig['write_only'] = true; + $this->cluster = new Credis_Cluster(array($writeOnlyConfig,$this->redisConfig[6]), 2, $this->useStandalone); + $this->assertTrue($this->cluster->client('master')->set('key', 'value')); + $this->waitForSlaveReplication(); + $this->assertEquals('value', $this->cluster->client('slave')->get('key')); + $this->assertEquals('value', $this->cluster->get('key')); + $this->setExpectedExceptionShim('CredisException'); + $this->assertFalse($this->cluster->client('slave')->set('key2', 'value')); + } + public function testMasterWithoutSlavesAndWriteOnlyFlag() + { + $this->tearDown(); + $writeOnlyConfig = $this->redisConfig[0]; + $writeOnlyConfig['write_only'] = true; + $this->cluster = new Credis_Cluster(array($writeOnlyConfig), 2, $this->useStandalone); + $this->assertTrue($this->cluster->set('key', 'value')); + $this->assertEquals('value', $this->cluster->get('key')); + } + public function testDontHashForCodeCoverage() + { + if (method_exists($this, 'assertIsArray')) { + $this->assertIsArray($this->cluster->info()); + } else { + $this->assertInternalType('array', $this->cluster->info()); + } } - } - public function testByHash() - { - $this->cluster->set('key','value'); - $this->assertEquals(6379,$this->cluster->byHash('key')->getPort()); - } - public function testRwsplit() - { - $readOnlyCommands = array( - 'EXISTS', - 'TYPE', - 'KEYS', - 'SCAN', - 'RANDOMKEY', - 'TTL', - 'GET', - 'MGET', - 'SUBSTR', - 'STRLEN', - 'GETRANGE', - 'GETBIT', - 'LLEN', - 'LRANGE', - 'LINDEX', - 'SCARD', - 'SISMEMBER', - 'SINTER', - 'SUNION', - 'SDIFF', - 'SMEMBERS', - 'SSCAN', - 'SRANDMEMBER', - 'ZRANGE', - 'ZREVRANGE', - 'ZRANGEBYSCORE', - 'ZREVRANGEBYSCORE', - 'ZCARD', - 'ZSCORE', - 'ZCOUNT', - 'ZRANK', - 'ZREVRANK', - 'ZSCAN', - 'HGET', - 'HMGET', - 'HEXISTS', - 'HLEN', - 'HKEYS', - 'HVALS', - 'HGETALL', - 'HSCAN', - 'PING', - 'AUTH', - 'SELECT', - 'ECHO', - 'QUIT', - 'OBJECT', - 'BITCOUNT', - 'TIME', - 'SORT' - ); - foreach($readOnlyCommands as $command){ - $this->assertTrue($this->cluster->isReadOnlyCommand($command)); + public function testByHash() + { + $this->cluster->set('key', 'value'); + $this->assertEquals(6379, $this->cluster->byHash('key')->getPort()); } - $this->assertFalse($this->cluster->isReadOnlyCommand("SET")); - $this->assertFalse($this->cluster->isReadOnlyCommand("HDEL")); - $this->assertFalse($this->cluster->isReadOnlyCommand("RPUSH")); - $this->assertFalse($this->cluster->isReadOnlyCommand("SMOVE")); - $this->assertFalse($this->cluster->isReadOnlyCommand("ZADD")); - } - public function testCredisClientInstancesInConstructor() - { - $this->tearDown(); - $two = new Credis_Client($this->redisConfig[1]['host'], $this->redisConfig[1]['port']); - $three = new Credis_Client($this->redisConfig[2]['host'], $this->redisConfig[2]['port']); - $four = new Credis_Client($this->redisConfig[3]['host'], $this->redisConfig[3]['port']); - $this->cluster = new Credis_Cluster(array($two,$three,$four),2,$this->useStandalone); - $this->assertTrue($this->cluster->set('key','value')); - $this->assertEquals('value',$this->cluster->get('key')); - $this->setExpectedExceptionShim('CredisException','Server should either be an array or an instance of Credis_Client'); - new Credis_Cluster(array(new stdClass()),2,$this->useStandalone); - } - public function testSetMasterClient() - { - $this->tearDown(); - $master = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port']); - $slave = new Credis_Client($this->redisConfig[6]['host'], $this->redisConfig[6]['port']); + public function testRwsplit() + { + $readOnlyCommands = array( + 'EXISTS', + 'TYPE', + 'KEYS', + 'SCAN', + 'RANDOMKEY', + 'TTL', + 'GET', + 'MGET', + 'SUBSTR', + 'STRLEN', + 'GETRANGE', + 'GETBIT', + 'LLEN', + 'LRANGE', + 'LINDEX', + 'SCARD', + 'SISMEMBER', + 'SINTER', + 'SUNION', + 'SDIFF', + 'SMEMBERS', + 'SSCAN', + 'SRANDMEMBER', + 'ZRANGE', + 'ZREVRANGE', + 'ZRANGEBYSCORE', + 'ZREVRANGEBYSCORE', + 'ZCARD', + 'ZSCORE', + 'ZCOUNT', + 'ZRANK', + 'ZREVRANK', + 'ZSCAN', + 'HGET', + 'HMGET', + 'HEXISTS', + 'HLEN', + 'HKEYS', + 'HVALS', + 'HGETALL', + 'HSCAN', + 'PING', + 'AUTH', + 'SELECT', + 'ECHO', + 'QUIT', + 'OBJECT', + 'BITCOUNT', + 'TIME', + 'SORT' + ); + foreach ($readOnlyCommands as $command) { + $this->assertTrue($this->cluster->isReadOnlyCommand($command)); + } + $this->assertFalse($this->cluster->isReadOnlyCommand("SET")); + $this->assertFalse($this->cluster->isReadOnlyCommand("HDEL")); + $this->assertFalse($this->cluster->isReadOnlyCommand("RPUSH")); + $this->assertFalse($this->cluster->isReadOnlyCommand("SMOVE")); + $this->assertFalse($this->cluster->isReadOnlyCommand("ZADD")); + } + public function testCredisClientInstancesInConstructor() + { + $this->tearDown(); + $two = new Credis_Client($this->redisConfig[1]['host'], $this->redisConfig[1]['port']); + $three = new Credis_Client($this->redisConfig[2]['host'], $this->redisConfig[2]['port']); + $four = new Credis_Client($this->redisConfig[3]['host'], $this->redisConfig[3]['port']); + $this->cluster = new Credis_Cluster(array($two,$three,$four), 2, $this->useStandalone); + $this->assertTrue($this->cluster->set('key', 'value')); + $this->assertEquals('value', $this->cluster->get('key')); + $this->setExpectedExceptionShim('CredisException', 'Server should either be an array or an instance of Credis_Client'); + new Credis_Cluster(array(new stdClass()), 2, $this->useStandalone); + } + public function testSetMasterClient() + { + $this->tearDown(); + $master = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port']); + $slave = new Credis_Client($this->redisConfig[6]['host'], $this->redisConfig[6]['port']); - $this->cluster = new Credis_Cluster(array($slave),2,$this->useStandalone); - $this->assertInstanceOf('Credis_Cluster',$this->cluster->setMasterClient($master)); - $this->assertCount(2,$this->cluster->clients()); - $this->assertEquals($this->redisConfig[6]['port'], $this->cluster->client(0)->getPort()); - $this->assertEquals($this->redisConfig[0]['port'], $this->cluster->client('master')->getPort()); + $this->cluster = new Credis_Cluster(array($slave), 2, $this->useStandalone); + $this->assertInstanceOf('Credis_Cluster', $this->cluster->setMasterClient($master)); + $this->assertCount(2, $this->cluster->clients()); + $this->assertEquals($this->redisConfig[6]['port'], $this->cluster->client(0)->getPort()); + $this->assertEquals($this->redisConfig[0]['port'], $this->cluster->client('master')->getPort()); - $this->cluster = new Credis_Cluster(array($this->redisConfig[0]), 2, $this->useStandalone); - $this->assertInstanceOf('Credis_Cluster',$this->cluster->setMasterClient(new Credis_Client($this->redisConfig[1]['host'], $this->redisConfig[1]['port']))); - $this->assertEquals($this->redisConfig[0]['port'], $this->cluster->client('master')->getPort()); + $this->cluster = new Credis_Cluster(array($this->redisConfig[0]), 2, $this->useStandalone); + $this->assertInstanceOf('Credis_Cluster', $this->cluster->setMasterClient(new Credis_Client($this->redisConfig[1]['host'], $this->redisConfig[1]['port']))); + $this->assertEquals($this->redisConfig[0]['port'], $this->cluster->client('master')->getPort()); - $this->cluster = new Credis_Cluster(array($slave),2,$this->useStandalone); - $this->assertInstanceOf('Credis_Cluster',$this->cluster->setMasterClient($master,true)); - $this->assertCount(1,$this->cluster->clients()); - } + $this->cluster = new Credis_Cluster(array($slave), 2, $this->useStandalone); + $this->assertInstanceOf('Credis_Cluster', $this->cluster->setMasterClient($master, true)); + $this->assertCount(1, $this->cluster->clients()); + } } diff --git a/tests/CredisSentinelTest.php b/tests/CredisSentinelTest.php index 10503a0..be8878f 100644 --- a/tests/CredisSentinelTest.php +++ b/tests/CredisSentinelTest.php @@ -7,239 +7,230 @@ class CredisSentinelTest extends CredisTestCommon { - /** @var Credis_Sentinel */ - protected $sentinel; - - protected $sentinelConfig; - - protected function setUpInternal() - { - parent::setUpInternal(); - if($this->sentinelConfig === NULL) { - $configFile = dirname(__FILE__).'/sentinel_config.json'; - if( ! file_exists($configFile) || ! ($config = file_get_contents($configFile))) { - $this->markTestSkipped('Could not load '.$configFile); - return; - } - $this->sentinelConfig = json_decode($config); - } - - $sentinelClient = new Credis_Client($this->sentinelConfig->host, $this->sentinelConfig->port); - $this->sentinel = new Credis_Sentinel($sentinelClient); - if($this->useStandalone) { - $this->sentinel->forceStandalone(); - } - $this->waitForSlaveReplication(); - } - - public static function setUpBeforeClassInternal() - { - parent::setUpBeforeClassInternal(); - if(preg_match('/^WIN/',strtoupper(PHP_OS))){ - echo "\tredis-server redis-sentinel.conf --sentinel".PHP_EOL.PHP_EOL; - } else { - sleep(2); - chdir(__DIR__); - copy('redis-sentinel.conf','redis-sentinel.conf.bak'); - exec('redis-server redis-sentinel.conf --sentinel'); - // wait for redis to initialize - sleep(1); - } - } - - public static function tearDownAfterClassInternal() - { - parent::tearDownAfterClassInternal(); - if(preg_match('/^WIN/',strtoupper(PHP_OS))){ - echo "Please kill all Redis instances manually:".PHP_EOL; - } else { - chdir(__DIR__); - @unlink('redis-sentinel.conf'); - @copy('redis-sentinel.conf.bak','redis-sentinel.conf'); - } - } - - protected function tearDownInternal() - { - if($this->sentinel) { - $this->sentinel = NULL; - } - } - public function testMasterClient() - { - $master = $this->sentinel->getMasterClient($this->sentinelConfig->clustername); - $this->assertInstanceOf('Credis_Client',$master); - $this->assertEquals($this->redisConfig[0]['port'],$master->getPort()); - $this->setExpectedExceptionShim('CredisException','Master not found'); - $this->sentinel->getMasterClient('non-existing-cluster'); - } - public function testMasters() - { - $masters = $this->sentinel->masters(); - if (method_exists($this,'assertIsArray')){ - $this->assertIsArray($masters); - } else { - $this->assertInternalType('array',$masters); - } - $this->assertCount(2,$masters); - $this->assertArrayHasKey(0,$masters); - $this->assertArrayHasKey(1,$masters); - $this->assertArrayHasKey(1,$masters[0]); - $this->assertArrayHasKey(1,$masters[1]); - $this->assertArrayHasKey(5,$masters[1]); - if($masters[0][1] == 'masterdown'){ - $this->assertEquals($this->sentinelConfig->clustername,$masters[1][1]); - $this->assertEquals($this->redisConfig[0]['port'],$masters[1][5]); - } else { - $this->assertEquals('masterdown',$masters[1][1]); - $this->assertEquals($this->sentinelConfig->clustername,$masters[0][1]); - $this->assertEquals($this->redisConfig[0]['port'],$masters[0][5]); - } - } - public function testMaster() - { - $master = $this->sentinel->master($this->sentinelConfig->clustername); - if (method_exists($this,'assertIsArray')){ - $this->assertIsArray($master); - } else { - $this->assertInternalType('array',$master); - } - $this->assertArrayHasKey(1,$master); - $this->assertArrayHasKey(5,$master); - $this->assertEquals($this->sentinelConfig->clustername,$master[1]); - $this->assertEquals($this->redisConfig[0]['port'],$master[5]); - - $this->setExpectedExceptionShim('CredisException','No such master with that name'); - $this->sentinel->master('non-existing-cluster'); - } - public function testSlaveClient() - { - $slaves = $this->sentinel->getSlaveClients($this->sentinelConfig->clustername); - if (method_exists($this,'assertIsArray')){ - $this->assertIsArray($slaves); - } else { - $this->assertInternalType('array',$slaves); - } - $this->assertCount(1,$slaves); - foreach($slaves as $slave){ - $this->assertInstanceOf('Credis_Client',$slave); - } - $this->setExpectedExceptionShim('CredisException','No such master with that name'); - $this->sentinel->getSlaveClients('non-existing-cluster'); - } - public function testSlaves() - { - $slaves = $this->sentinel->slaves($this->sentinelConfig->clustername); - if (method_exists($this,'assertIsArray')){ - $this->assertIsArray($slaves); - } else { - $this->assertInternalType('array',$slaves); - } - $this->assertCount(1,$slaves); - $this->assertArrayHasKey(0,$slaves); - $this->assertArrayHasKey(5,$slaves[0]); - $this->assertEquals(6385,$slaves[0][5]); - - $slaves = $this->sentinel->slaves('masterdown'); - if (method_exists($this,'assertIsArray')){ - $this->assertIsArray($slaves); - } else { - $this->assertInternalType('array',$slaves); - } - $this->assertCount(0,$slaves); - - $this->setExpectedExceptionShim('CredisException','No such master with that name'); - $this->sentinel->slaves('non-existing-cluster'); - } - public function testNonExistingClusterNameWhenCreatingSlaves() - { - $this->setExpectedExceptionShim('CredisException','No such master with that name'); - $this->sentinel->createSlaveClients('non-existing-cluster'); - } - public function testCreateCluster() - { - $cluster = $this->sentinel->createCluster($this->sentinelConfig->clustername); - $this->assertInstanceOf('Credis_Cluster',$cluster); - $this->assertCount(2,$cluster->clients()); - $cluster = $this->sentinel->createCluster($this->sentinelConfig->clustername,0,1,false); - $this->assertInstanceOf('Credis_Cluster',$cluster); - $this->assertCount(2,$cluster->clients()); - $this->setExpectedExceptionShim('CredisException','The master is down'); - $this->sentinel->createCluster($this->sentinelConfig->downclustername); - } - public function testGetCluster() - { - $cluster = $this->sentinel->getCluster($this->sentinelConfig->clustername); - $this->assertInstanceOf('Credis_Cluster',$cluster); - $this->assertCount(2,$cluster->clients()); - } - public function testGetClusterOnDbNumber2() - { - $cluster = $this->sentinel->getCluster($this->sentinelConfig->clustername,2); - $this->assertInstanceOf('Credis_Cluster',$cluster); - $this->assertCount(2,$cluster->clients()); - $clients = $cluster->clients(); - $this->assertEquals(2,$clients[0]->getSelectedDb()); - $this->assertEquals(2,$clients[1]->getSelectedDb()); - } - public function testGetMasterAddressByName() - { - $address = $this->sentinel->getMasterAddressByName($this->sentinelConfig->clustername); - if (method_exists($this,'assertIsArray')){ - $this->assertIsArray($address); - } else { - $this->assertInternalType('array',$address); - } - $this->assertCount(2,$address); - $this->assertArrayHasKey(0,$address); - $this->assertArrayHasKey(1,$address); - $this->assertEquals($this->redisConfig[0]['host'],$address[0]); - $this->assertEquals($this->redisConfig[0]['port'],$address[1]); - } - - public function testPing() - { - $pong = $this->sentinel->ping(); - $this->assertEquals("PONG",$pong); - } - - public function testGetHostAndPort() - { - $host = 'localhost'; - $port = '123456'; - - $client = $this->createMock('\Credis_Client'); - $sentinel = new Credis_Sentinel($client); - - $client->expects($this->once())->method('getHost')->willReturn($host); - $client->expects($this->once())->method('getPort')->willReturn($port); - - $this->assertEquals($host, $sentinel->getHost()); - $this->assertEquals($port, $sentinel->getPort()); - } - public function testNonExistingMethod() - { - try - { - $this->sentinel->bla(); - } - catch(CredisException $e) - { - if (strpos($e->getMessage(), 'bla') !== false) - { - if (strpos($e->getMessage(), 'unknown subcommand') !== false) - { - $this->assertStringStartsWith('ERR unknown subcommand \'bla\'', $e->getMessage()); - } - else - { - $this->assertStringStartsWith('ERR Unknown sentinel subcommand \'bla\'', $e->getMessage()); - } + /** @var Credis_Sentinel */ + protected $sentinel; + + protected $sentinelConfig; + + protected function setUpInternal() + { + parent::setUpInternal(); + if ($this->sentinelConfig === null) { + $configFile = dirname(__FILE__).'/sentinel_config.json'; + if (! file_exists($configFile) || ! ($config = file_get_contents($configFile))) { + $this->markTestSkipped('Could not load '.$configFile); + return; + } + $this->sentinelConfig = json_decode($config); } - else - { - $this->assertStringStartsWith('ERR Unknown subcommand or wrong number of arguments', $e->getMessage()); + + $sentinelClient = new Credis_Client($this->sentinelConfig->host, $this->sentinelConfig->port); + $this->sentinel = new Credis_Sentinel($sentinelClient); + if ($this->useStandalone) { + $this->sentinel->forceStandalone(); + } + $this->waitForSlaveReplication(); + } + + public static function setUpBeforeClassInternal() + { + parent::setUpBeforeClassInternal(); + if (preg_match('/^WIN/', strtoupper(PHP_OS))) { + echo "\tredis-server redis-sentinel.conf --sentinel".PHP_EOL.PHP_EOL; + } else { + sleep(2); + chdir(__DIR__); + copy('redis-sentinel.conf', 'redis-sentinel.conf.bak'); + exec('redis-server redis-sentinel.conf --sentinel'); + // wait for redis to initialize + sleep(1); + } + } + + public static function tearDownAfterClassInternal() + { + parent::tearDownAfterClassInternal(); + if (preg_match('/^WIN/', strtoupper(PHP_OS))) { + echo "Please kill all Redis instances manually:".PHP_EOL; + } else { + chdir(__DIR__); + @unlink('redis-sentinel.conf'); + @copy('redis-sentinel.conf.bak', 'redis-sentinel.conf'); + } + } + + protected function tearDownInternal() + { + if ($this->sentinel) { + $this->sentinel = null; + } + } + public function testMasterClient() + { + $master = $this->sentinel->getMasterClient($this->sentinelConfig->clustername); + $this->assertInstanceOf('Credis_Client', $master); + $this->assertEquals($this->redisConfig[0]['port'], $master->getPort()); + $this->setExpectedExceptionShim('CredisException', 'Master not found'); + $this->sentinel->getMasterClient('non-existing-cluster'); + } + public function testMasters() + { + $masters = $this->sentinel->masters(); + if (method_exists($this, 'assertIsArray')) { + $this->assertIsArray($masters); + } else { + $this->assertInternalType('array', $masters); + } + $this->assertCount(2, $masters); + $this->assertArrayHasKey(0, $masters); + $this->assertArrayHasKey(1, $masters); + $this->assertArrayHasKey(1, $masters[0]); + $this->assertArrayHasKey(1, $masters[1]); + $this->assertArrayHasKey(5, $masters[1]); + if ($masters[0][1] == 'masterdown') { + $this->assertEquals($this->sentinelConfig->clustername, $masters[1][1]); + $this->assertEquals($this->redisConfig[0]['port'], $masters[1][5]); + } else { + $this->assertEquals('masterdown', $masters[1][1]); + $this->assertEquals($this->sentinelConfig->clustername, $masters[0][1]); + $this->assertEquals($this->redisConfig[0]['port'], $masters[0][5]); + } + } + public function testMaster() + { + $master = $this->sentinel->master($this->sentinelConfig->clustername); + if (method_exists($this, 'assertIsArray')) { + $this->assertIsArray($master); + } else { + $this->assertInternalType('array', $master); + } + $this->assertArrayHasKey(1, $master); + $this->assertArrayHasKey(5, $master); + $this->assertEquals($this->sentinelConfig->clustername, $master[1]); + $this->assertEquals($this->redisConfig[0]['port'], $master[5]); + + $this->setExpectedExceptionShim('CredisException', 'No such master with that name'); + $this->sentinel->master('non-existing-cluster'); + } + public function testSlaveClient() + { + $slaves = $this->sentinel->getSlaveClients($this->sentinelConfig->clustername); + if (method_exists($this, 'assertIsArray')) { + $this->assertIsArray($slaves); + } else { + $this->assertInternalType('array', $slaves); + } + $this->assertCount(1, $slaves); + foreach ($slaves as $slave) { + $this->assertInstanceOf('Credis_Client', $slave); } - } - } + $this->setExpectedExceptionShim('CredisException', 'No such master with that name'); + $this->sentinel->getSlaveClients('non-existing-cluster'); + } + public function testSlaves() + { + $slaves = $this->sentinel->slaves($this->sentinelConfig->clustername); + if (method_exists($this, 'assertIsArray')) { + $this->assertIsArray($slaves); + } else { + $this->assertInternalType('array', $slaves); + } + $this->assertCount(1, $slaves); + $this->assertArrayHasKey(0, $slaves); + $this->assertArrayHasKey(5, $slaves[0]); + $this->assertEquals(6385, $slaves[0][5]); + + $slaves = $this->sentinel->slaves('masterdown'); + if (method_exists($this, 'assertIsArray')) { + $this->assertIsArray($slaves); + } else { + $this->assertInternalType('array', $slaves); + } + $this->assertCount(0, $slaves); + + $this->setExpectedExceptionShim('CredisException', 'No such master with that name'); + $this->sentinel->slaves('non-existing-cluster'); + } + public function testNonExistingClusterNameWhenCreatingSlaves() + { + $this->setExpectedExceptionShim('CredisException', 'No such master with that name'); + $this->sentinel->createSlaveClients('non-existing-cluster'); + } + public function testCreateCluster() + { + $cluster = $this->sentinel->createCluster($this->sentinelConfig->clustername); + $this->assertInstanceOf('Credis_Cluster', $cluster); + $this->assertCount(2, $cluster->clients()); + $cluster = $this->sentinel->createCluster($this->sentinelConfig->clustername, 0, 1, false); + $this->assertInstanceOf('Credis_Cluster', $cluster); + $this->assertCount(2, $cluster->clients()); + $this->setExpectedExceptionShim('CredisException', 'The master is down'); + $this->sentinel->createCluster($this->sentinelConfig->downclustername); + } + public function testGetCluster() + { + $cluster = $this->sentinel->getCluster($this->sentinelConfig->clustername); + $this->assertInstanceOf('Credis_Cluster', $cluster); + $this->assertCount(2, $cluster->clients()); + } + public function testGetClusterOnDbNumber2() + { + $cluster = $this->sentinel->getCluster($this->sentinelConfig->clustername, 2); + $this->assertInstanceOf('Credis_Cluster', $cluster); + $this->assertCount(2, $cluster->clients()); + $clients = $cluster->clients(); + $this->assertEquals(2, $clients[0]->getSelectedDb()); + $this->assertEquals(2, $clients[1]->getSelectedDb()); + } + public function testGetMasterAddressByName() + { + $address = $this->sentinel->getMasterAddressByName($this->sentinelConfig->clustername); + if (method_exists($this, 'assertIsArray')) { + $this->assertIsArray($address); + } else { + $this->assertInternalType('array', $address); + } + $this->assertCount(2, $address); + $this->assertArrayHasKey(0, $address); + $this->assertArrayHasKey(1, $address); + $this->assertEquals($this->redisConfig[0]['host'], $address[0]); + $this->assertEquals($this->redisConfig[0]['port'], $address[1]); + } + + public function testPing() + { + $pong = $this->sentinel->ping(); + $this->assertEquals("PONG", $pong); + } + + public function testGetHostAndPort() + { + $host = 'localhost'; + $port = '123456'; + + $client = $this->createMock('\Credis_Client'); + $sentinel = new Credis_Sentinel($client); + + $client->expects($this->once())->method('getHost')->willReturn($host); + $client->expects($this->once())->method('getPort')->willReturn($port); + + $this->assertEquals($host, $sentinel->getHost()); + $this->assertEquals($port, $sentinel->getPort()); + } + public function testNonExistingMethod() + { + try { + $this->sentinel->bla(); + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'bla') !== false) { + if (strpos($e->getMessage(), 'unknown subcommand') !== false) { + $this->assertStringStartsWith('ERR unknown subcommand \'bla\'', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR Unknown sentinel subcommand \'bla\'', $e->getMessage()); + } + } else { + $this->assertStringStartsWith('ERR Unknown subcommand or wrong number of arguments', $e->getMessage()); + } + } + } } diff --git a/tests/CredisStandaloneClusterTest.php b/tests/CredisStandaloneClusterTest.php index f3a6915..3371ab5 100644 --- a/tests/CredisStandaloneClusterTest.php +++ b/tests/CredisStandaloneClusterTest.php @@ -4,27 +4,27 @@ class CredisStandaloneClusterTest extends CredisClusterTest { - protected $useStandalone = TRUE; - protected function tearDownInternal() - { - if($this->cluster) { - foreach($this->cluster->clients() as $client){ - if($client->isConnected()) { - $client->close(); + protected $useStandalone = true; + protected function tearDownInternal() + { + if ($this->cluster) { + foreach ($this->cluster->clients() as $client) { + if ($client->isConnected()) { + $client->close(); + } } + $this->cluster = null; } - $this->cluster = NULL; } - } - public function testMasterSlave() - { - $this->tearDown(); - $this->cluster = new Credis_Cluster(array($this->redisConfig[0],$this->redisConfig[6]), 2, $this->useStandalone); - $this->assertTrue($this->cluster->client('master')->set('key','value')); - $this->waitForSlaveReplication(); - $this->assertEquals('value',$this->cluster->client('slave')->get('key')); - $this->assertEquals('value',$this->cluster->get('key')); - $this->setExpectedExceptionShim('CredisException','READONLY You can\'t write against a read only replica.'); - $this->cluster->client('slave')->set('key2','value'); - } + public function testMasterSlave() + { + $this->tearDown(); + $this->cluster = new Credis_Cluster(array($this->redisConfig[0],$this->redisConfig[6]), 2, $this->useStandalone); + $this->assertTrue($this->cluster->client('master')->set('key', 'value')); + $this->waitForSlaveReplication(); + $this->assertEquals('value', $this->cluster->client('slave')->get('key')); + $this->assertEquals('value', $this->cluster->get('key')); + $this->setExpectedExceptionShim('CredisException', 'READONLY You can\'t write against a read only replica.'); + $this->cluster->client('slave')->set('key2', 'value'); + } } diff --git a/tests/CredisStandaloneSentinelTest.php b/tests/CredisStandaloneSentinelTest.php index 1fbfe8e..0e39316 100644 --- a/tests/CredisStandaloneSentinelTest.php +++ b/tests/CredisStandaloneSentinelTest.php @@ -4,5 +4,5 @@ class CredisStandaloneSentinelTest extends CredisSentinelTest { - protected $useStandalone = TRUE; + protected $useStandalone = true; } diff --git a/tests/CredisStandaloneTest.php b/tests/CredisStandaloneTest.php index 46f41ca..7a51ba3 100644 --- a/tests/CredisStandaloneTest.php +++ b/tests/CredisStandaloneTest.php @@ -4,28 +4,31 @@ class CredisStandaloneTest extends CredisTest { - protected $useStandalone = TRUE; + protected $useStandalone = true; - public function testPersistentConnectionsOnStandAloneTcpConnection() - { - $this->credis->close(); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/persistent'); - $this->credis->forceStandalone(); - $this->credis->set('key','value'); - $this->assertEquals('value',$this->credis->get('key')); - } - - public function testPersistentvsNonPersistent() {$this->assertTrue(true);} - - public function testStandAloneArgumentsExtra() + public function testPersistentConnectionsOnStandAloneTcpConnection() { - $this->assertTrue($this->credis->hMSet('hash', array('field1' => 'value1', 'field2' => 'value2'), 'field3', 'value3')); - $this->assertEquals(array('field1' => 'value1', 'field2' => 'value2', 'field3' =>'value3'), $this->credis->hMGet('hash', array('field1','field2','field3'))); + $this->credis->close(); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/persistent'); + $this->credis->forceStandalone(); + $this->credis->set('key', 'value'); + $this->assertEquals('value', $this->credis->get('key')); } - public function testStandAloneMultiPipelineThrowsException() - { - $this->setExpectedExceptionShim('CredisException','A pipeline is already in use and only one pipeline is supported.'); - $this->credis->pipeline()->pipeline(); - } + public function testPersistentvsNonPersistent() + { + $this->assertTrue(true); + } + + public function testStandAloneArgumentsExtra() + { + $this->assertTrue($this->credis->hMSet('hash', array('field1' => 'value1', 'field2' => 'value2'), 'field3', 'value3')); + $this->assertEquals(array('field1' => 'value1', 'field2' => 'value2', 'field3' =>'value3'), $this->credis->hMGet('hash', array('field1','field2','field3'))); + } + + public function testStandAloneMultiPipelineThrowsException() + { + $this->setExpectedExceptionShim('CredisException', 'A pipeline is already in use and only one pipeline is supported.'); + $this->credis->pipeline()->pipeline(); + } } diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 803fa64..2e44fd5 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -12,21 +12,21 @@ protected function setUpInternal() { parent::setUpInternal(); $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout']); - if($this->useStandalone) { + if ($this->useStandalone) { $this->credis->forceStandalone(); } $this->credis->flushDb(); } protected function tearDownInternal() { - if($this->credis) { + if ($this->credis) { $this->credis->close(); - $this->credis = NULL; + $this->credis = null; } } public function testFlush() { - $this->credis->set('foo','FOO'); + $this->credis->set('foo', 'FOO'); $this->assertTrue($this->credis->flushDb()); $this->assertFalse($this->credis->get('foo')); } @@ -61,7 +61,7 @@ public function testPHPRedisReadTimeout() public function testScalars() { // Basic get/set - $this->credis->set('foo','FOO'); + $this->credis->set('foo', 'FOO'); $this->assertEquals('FOO', $this->credis->get('foo')); $this->assertFalse($this->credis->get('nil')); @@ -70,12 +70,12 @@ public function testScalars() $this->assertEquals($this->credis->exists('nil'), 0); // Empty string - $this->credis->set('empty',''); + $this->credis->set('empty', ''); $this->assertEquals('', $this->credis->get('empty')); // UTF-8 characters $utf8str = str_repeat("quarter: ¼, micro: µ, thorn: Þ, ", 500); - $this->credis->set('utf8',$utf8str); + $this->credis->set('utf8', $utf8str); $this->assertEquals($utf8str, $this->credis->get('utf8')); // Array @@ -86,12 +86,12 @@ public function testScalars() $this->assertTrue(in_array('', $mGet)); // Non-array - $mGet = $this->credis->mGet('foo','bar'); + $mGet = $this->credis->mGet('foo', 'bar'); $this->assertTrue(in_array('FOO', $mGet)); $this->assertTrue(in_array('BAR', $mGet)); // Delete strings, null response - $this->assertEquals(2, $this->credis->del('foo','bar')); + $this->assertEquals(2, $this->credis->del('foo', 'bar')); $this->assertFalse($this->credis->get('foo')); $this->assertFalse($this->credis->get('bar')); @@ -214,7 +214,7 @@ public function testSortedSets() $this->assertTrue(array_key_exists('And', $range)); $this->assertEquals(10, $range['And']); - $range = $this->credis->zRevRangeByScore('myset', '+inf',10, array('withscores' => true)); + $range = $this->credis->zRevRangeByScore('myset', '+inf', 10, array('withscores' => true)); $this->assertEquals(2, count($range)); $this->assertTrue(array_key_exists('And', $range)); $this->assertEquals(10, $range['And']); @@ -267,12 +267,12 @@ public function testSortedSets() public function testHashes() { - $this->assertEquals(1, $this->credis->hSet('hash','field1','foo')); - $this->assertEquals(0, $this->credis->hSet('hash','field1','foo')); - $this->assertEquals('foo', $this->credis->hGet('hash','field1')); - $this->assertEquals(NULL, $this->credis->hGet('hash','x')); + $this->assertEquals(1, $this->credis->hSet('hash', 'field1', 'foo')); + $this->assertEquals(0, $this->credis->hSet('hash', 'field1', 'foo')); + $this->assertEquals('foo', $this->credis->hGet('hash', 'field1')); + $this->assertEquals(null, $this->credis->hGet('hash', 'x')); $this->assertTrue($this->credis->hMSet('hash', array('field2' => 'Hello', 'field3' => 'World'))); - $this->assertEquals(array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => FALSE), $this->credis->hMGet('hash', array('field1','field2','nilfield'))); + $this->assertEquals(array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => false), $this->credis->hMGet('hash', array('field1','field2','nilfield'))); $this->assertEquals(array(), $this->credis->hGetAll('nohash')); $this->assertEquals(array('field1' => 'foo', 'field2' => 'Hello', 'field3' => 'World'), $this->credis->hGetAll('hash')); @@ -290,11 +290,11 @@ public function testHashes() $this->credis->pipeline(); $this->assertTrue($this->credis === $this->credis->hMGet('hash', array('field1','field2','nilfield'))); - $this->assertEquals(array(0 => array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => FALSE)), $this->credis->exec()); + $this->assertEquals(array(0 => array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => false)), $this->credis->exec()); $this->credis->pipeline()->multi(); $this->assertTrue($this->credis === $this->credis->hMGet('hash', array('field1','field2','nilfield'))); - $this->assertEquals(array(0 => array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => FALSE)), $this->credis->exec()); + $this->assertEquals(array(0 => array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => false)), $this->credis->exec()); } public function testFalsey() @@ -335,7 +335,8 @@ public function testWatchMultiUnwatch() array( true, true, - ), $reply + ), + $reply ); $this->assertTrue($this->credis->unwatch()); } @@ -425,7 +426,8 @@ protected function pipelineTestInternal() 'member3' => 3.0, 'member2' => 2.0, ), - ), $reply + ), + $reply ); } @@ -454,7 +456,7 @@ public function testTransaction() ->lpop('a') ->exec(); $this->assertEquals(2, count($reply)); - $this->assertEquals(TRUE, $reply[0]); + $this->assertEquals(true, $reply[0]); $this->assertFalse($reply[1]); } @@ -471,7 +473,7 @@ public function testScripts() $this->assertEquals('09d3822de862f46d784e6a36848b4f0736dda47a', $this->credis->script('load', 'return 3')); $this->assertEquals(3, $this->credis->evalSha('09d3822de862f46d784e6a36848b4f0736dda47a')); - $this->credis->set('foo','FOO'); + $this->credis->set('foo', 'FOO'); $this->assertEquals('FOOBAR', $this->credis->eval("return redis.call('get', KEYS[1])..ARGV[1]", 'foo', 'BAR')); $this->assertEquals(array(1,2,'three'), $this->credis->eval("return {1,2,'three'}")); @@ -539,7 +541,7 @@ public function testDb() if ($this->useStandalone) { $this->credis->forceStandalone(); } - $this->assertTrue($this->credis->set('database',1)); + $this->assertTrue($this->credis->set('database', 1)); $this->credis->close(); $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 0); if ($this->useStandalone) { @@ -550,7 +552,7 @@ public function testDb() if ($this->useStandalone) { $this->credis->forceStandalone(); } - $this->assertEquals(1,$this->credis->get('database')); + $this->assertEquals(1, $this->credis->get('database')); } /** @@ -559,32 +561,26 @@ public function testDb() public function testPassword() { $this->tearDown(); - $this->assertArrayHasKey('password',$this->redisConfig[4]); + $this->assertArrayHasKey('password', $this->redisConfig[4]); $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, $this->redisConfig[4]['password']); if ($this->useStandalone) { $this->credis->forceStandalone(); } - $this->assertInstanceOf('Credis_Client',$this->credis->connect()); - $this->assertTrue($this->credis->set('key','value')); + $this->assertInstanceOf('Credis_Client', $this->credis->connect()); + $this->assertTrue($this->credis->set('key', 'value')); $this->credis->close(); $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, 'wrongpassword'); if ($this->useStandalone) { $this->credis->forceStandalone(); } - try - { + try { $this->credis->connect(); $this->fail('connect should fail with wrong password'); - } - catch(CredisException $e) - { - if (strpos($e->getMessage(), 'username') !== false) - { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } - else - { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); } $this->credis->close(); @@ -593,31 +589,22 @@ public function testPassword() if ($this->useStandalone) { $this->credis->forceStandalone(); } - try - { + try { $this->credis->set('key', 'value'); - } - catch(CredisException $e) - { + } catch(CredisException $e) { $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); } - try - { + try { $this->credis->auth('anotherwrongpassword'); - } - catch(CredisException $e) - { - if (strpos($e->getMessage(), 'username') !== false) - { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } - else - { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); - } + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } } $this->assertTrue($this->credis->auth('thepassword')); - $this->assertTrue($this->credis->set('key','value')); + $this->assertTrue($this->credis->set('key', 'value')); } /** @@ -626,32 +613,26 @@ public function testPassword() public function testUsernameAndPassword() { $this->tearDown(); - $this->assertArrayHasKey('password',$this->redisConfig[7]); + $this->assertArrayHasKey('password', $this->redisConfig[7]); $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, $this->redisConfig[7]['password'], $this->redisConfig[7]['username']); if ($this->useStandalone) { $this->credis->forceStandalone(); } - $this->assertInstanceOf('Credis_Client',$this->credis->connect()); - $this->assertTrue($this->credis->set('key','value')); + $this->assertInstanceOf('Credis_Client', $this->credis->connect()); + $this->assertTrue($this->credis->set('key', 'value')); $this->credis->close(); $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, 'wrongpassword', $this->redisConfig[7]['username']); if ($this->useStandalone) { $this->credis->forceStandalone(); } - try - { + try { $this->credis->connect(); $this->fail('connect should fail with wrong password'); - } - catch(CredisException $e) - { - if (strpos($e->getMessage(), 'username') !== false) - { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } - else - { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); } $this->credis->close(); @@ -660,40 +641,31 @@ public function testUsernameAndPassword() if ($this->useStandalone) { $this->credis->forceStandalone(); } - try - { + try { $this->credis->set('key', 'value'); - } - catch(CredisException $e) - { + } catch(CredisException $e) { $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); } - try - { + try { $this->credis->auth('anotherwrongpassword'); - } - catch(CredisException $e) - { - if (strpos($e->getMessage(), 'username') !== false) - { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } - else - { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); - } + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } } $this->assertTrue($this->credis->auth('thepassword')); - $this->assertTrue($this->credis->set('key','value')); + $this->assertTrue($this->credis->set('key', 'value')); } public function testGettersAndSetters() { - $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); - $this->assertEquals($this->credis->getSelectedDb(),0); + $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getSelectedDb(), 0); $this->assertTrue($this->credis->select(2)); - $this->assertEquals($this->credis->getSelectedDb(),2); + $this->assertEquals($this->credis->getSelectedDb(), 2); $this->assertTrue($this->credis->isConnected()); $this->credis->close(); $this->assertFalse($this->credis->isConnected()); @@ -701,16 +673,16 @@ public function testGettersAndSetters() if ($this->useStandalone) { $this->credis->forceStandalone(); } - $this->assertEquals('persistenceId',$this->credis->getPersistence()); + $this->assertEquals('persistenceId', $this->credis->getPersistence()); $this->credis = new Credis_Client('localhost', 12345); if ($this->useStandalone) { $this->credis->forceStandalone(); } $this->credis->setMaxConnectRetries(1); if ($this->useStandalone) { - $this->setExpectedExceptionShim('CredisException','Connection to Redis standalone tcp://localhost:12345 failed after 2 failures.'); + $this->setExpectedExceptionShim('CredisException', 'Connection to Redis standalone tcp://localhost:12345 failed after 2 failures.'); } else { - $this->setExpectedExceptionShim('CredisException','Connection to Redis tcp://localhost:12345 failed after 2 failures.'); + $this->setExpectedExceptionShim('CredisException', 'Connection to Redis tcp://localhost:12345 failed after 2 failures.'); } $this->credis->connect(); } @@ -718,60 +690,60 @@ public function testGettersAndSetters() public function testConnectionStrings() { $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); - $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(),6379); + $this->assertEquals($this->credis->getPort(), 6379); $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); - $this->assertEquals($this->credis->getPersistence(),'abc123'); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'],6380); - $this->assertEquals($this->credis->getPort(),6380); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'],NULL,NULL,"abc123"); - $this->assertEquals($this->credis->getPersistence(),'abc123'); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'], 6380); + $this->assertEquals($this->credis->getPort(), 6380); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'], null, null, "abc123"); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); } public function testConnectionStringsTls() { - foreach(['ssl','tls','tlsv1.0','tlsv1.1','tlsv1.2'] as $prefix){ - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); - $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(),6379); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); - $this->assertEquals($this->credis->getPersistence(),'abc123'); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'],6380); - $this->assertEquals($this->credis->getPort(),6380); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'],NULL,NULL,"abc123"); - $this->assertEquals($this->credis->getPersistence(),'abc123'); - $this->assertTrue($this->credis->isTls()); + foreach (['ssl','tls','tlsv1.0','tlsv1.1','tlsv1.2'] as $prefix) { + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), 6379); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'], 6380); + $this->assertEquals($this->credis->getPort(), 6380); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'], null, null, "abc123"); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); + $this->assertTrue($this->credis->isTls()); } } public function testTLSConnection() { - /** - https://www.php.net/manual/en/context.ssl.php - > Oddly, if "tls://" is set below, then TLSv1 is forced, but using "ssl://" allows TLSv1.2 - - and - >Recommended use "ssl://" transport. - > in php 5.5 ~ 7.1 - > ssl:// transport = ssl_v2|ssl_v3|tls_v1.0|tls_v1.1|tls_v1.2 - > tls:// transport = tls_v1.0 - > after 7.2 ssl:// and tls:// transports is same - > php 7.2 ~ 7.3 = tls_v1.0|tls_v1.1|tls_v1.2 - > php 7.4 ~ 8.1 = tls_v1.0|tls_v1.1|tls_v1.2|tls_v1.3 - */ - - $this->credis = new Credis_Client('ssl://'.$this->redisConfig[8]['host'] . ':' . $this->redisConfig[8]['port']); - $this->credis->setTlsOptions((array)$this->redisConfig[8]['ssl']); - $this->credis->connect(); - $this->assertTrue(true); + /** + https://www.php.net/manual/en/context.ssl.php + > Oddly, if "tls://" is set below, then TLSv1 is forced, but using "ssl://" allows TLSv1.2 + + and + >Recommended use "ssl://" transport. + > in php 5.5 ~ 7.1 + > ssl:// transport = ssl_v2|ssl_v3|tls_v1.0|tls_v1.1|tls_v1.2 + > tls:// transport = tls_v1.0 + > after 7.2 ssl:// and tls:// transports is same + > php 7.2 ~ 7.3 = tls_v1.0|tls_v1.1|tls_v1.2 + > php 7.4 ~ 8.1 = tls_v1.0|tls_v1.1|tls_v1.2|tls_v1.3 + */ + + $this->credis = new Credis_Client('ssl://'.$this->redisConfig[8]['host'] . ':' . $this->redisConfig[8]['port']); + $this->credis->setTlsOptions((array)$this->redisConfig[8]['ssl']); + $this->credis->connect(); + $this->assertTrue(true); } /** @@ -785,14 +757,14 @@ public function testConnectionStringsSocket() } $this->credis->connect(); $this->assertTrue($this->credis->isConnected()); - $this->credis->set('key','value'); - $this->assertEquals('value',$this->credis->get('key')); + $this->credis->set('key', 'value'); + $this->assertEquals('value', $this->credis->get('key')); } public function testInvalidTcpConnectionString() { $this->credis->close(); - $this->setExpectedExceptionShim('CredisException','Invalid host format; expected tcp://host[:port][/persistence_identifier]'); + $this->setExpectedExceptionShim('CredisException', 'Invalid host format; expected tcp://host[:port][/persistence_identifier]'); $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':abc'); if ($this->useStandalone) { $this->credis->forceStandalone(); @@ -802,7 +774,7 @@ public function testInvalidTcpConnectionString() public function testInvalidTlsConnectionString() { $this->credis->close(); - $this->setExpectedExceptionShim('CredisException','Invalid host format; expected tls://host[:port][/persistence_identifier]'); + $this->setExpectedExceptionShim('CredisException', 'Invalid host format; expected tls://host[:port][/persistence_identifier]'); $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':abc'); if ($this->useStandalone) { $this->credis->forceStandalone(); @@ -815,7 +787,7 @@ public function testInvalidTlsConnectionString() public function testInvalidUnixSocketConnectionString() { $this->credis->close(); - $this->setExpectedExceptionShim('CredisException','Invalid unix socket format; expected unix:///path/to/redis.sock'); + $this->setExpectedExceptionShim('CredisException', 'Invalid unix socket format; expected unix:///path/to/redis.sock'); $this->credis = new Credis_Client('unix://path/to/redis.sock'); if ($this->useStandalone) { $this->credis->forceStandalone(); @@ -825,75 +797,67 @@ public function testInvalidUnixSocketConnectionString() public function testForceStandAloneAfterEstablishedConnection() { $this->credis->connect(); - if ( ! $this->useStandalone) { - $this->setExpectedExceptionShim('CredisException','Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.'); + if (! $this->useStandalone) { + $this->setExpectedExceptionShim('CredisException', 'Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.'); } $this->credis->forceStandalone(); $this->assertTrue(true); } public function testHscan() { - $this->credis->hmset('hash',array('name' => 'Jack','age' =>33)); + $this->credis->hmset('hash', array('name' => 'Jack','age' =>33)); $iterator = null; - $result = $this->credis->hscan($iterator,'hash','n*',10); - $this->assertEquals($iterator,0); - $this->assertEquals($result,['name'=>'Jack']); - } + $result = $this->credis->hscan($iterator, 'hash', 'n*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, ['name'=>'Jack']); + } public function testSscan() { - $this->credis->sadd('set','name','Jack'); - $this->credis->sadd('set','age','33'); + $this->credis->sadd('set', 'name', 'Jack'); + $this->credis->sadd('set', 'age', '33'); $iterator = null; - $result = $this->credis->sscan($iterator,'set','n*',10); - $this->assertEquals($iterator,0); - $this->assertEquals($result,[0=>'name']); + $result = $this->credis->sscan($iterator, 'set', 'n*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, [0=>'name']); } public function testZscan() { - $this->credis->zadd('sortedset',0,'name'); - $this->credis->zadd('sortedset',1,'age'); + $this->credis->zadd('sortedset', 0, 'name'); + $this->credis->zadd('sortedset', 1, 'age'); $iterator = null; - $result = $this->credis->zscan($iterator,'sortedset','n*',10); - $this->assertEquals($iterator,0); - $this->assertEquals($result,['name'=>'0']); + $result = $this->credis->zscan($iterator, 'sortedset', 'n*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, ['name'=>'0']); } public function testscan() { $seen = array(); - for($i = 0; $i < 100; $i++) - { + for ($i = 0; $i < 100; $i++) { $this->credis->set('name.' . $i, 'Jack'); $this->credis->set('age.' . $i, '33'); } $iterator = null; - do - { + do { $result = $this->credis->scan($iterator, 'n*', 10); - if ($result === false) - { + if ($result === false) { $this->assertEquals($iterator, 0); break; - } - else - { - foreach($result as $key) - { + } else { + foreach ($result as $key) { $seen[$key] = true; } } - } - while($iterator); + } while ($iterator); $this->assertEquals(count($seen), 100); } public function testPing() { - $pong = $this->credis->ping(); - $this->assertEquals("PONG",$pong); - if (version_compare(phpversion('redis'), '5.0.0', '>=')) - { - $pong = $this->credis->ping("test"); - $this->assertEquals("test", $pong); - } + $pong = $this->credis->ping(); + $this->assertEquals("PONG", $pong); + if (version_compare(phpversion('redis'), '5.0.0', '>=')) { + $pong = $this->credis->ping("test"); + $this->assertEquals("test", $pong); + } } } diff --git a/tests/CredisTestCommon.php b/tests/CredisTestCommon.php index 8fd774a..65c6e7a 100644 --- a/tests/CredisTestCommon.php +++ b/tests/CredisTestCommon.php @@ -1,13 +1,14 @@ =')) { - include 'phpunit-shims/php8.php'; + include 'phpunit-shims/php8.php'; } else { - include 'phpunit-shims/php7.php'; + include 'phpunit-shims/php7.php'; } class CredisTestCommon extends CredisTestCommonShim @@ -18,25 +19,22 @@ class CredisTestCommon extends CredisTestCommonShim protected function setUpInternal() { - if ($this->redisConfig === null) - { + if ($this->redisConfig === null) { $configFile = dirname(__FILE__) . '/redis_config.json'; - if (!file_exists($configFile) || !($config = file_get_contents($configFile))) - { + if (!file_exists($configFile) || !($config = file_get_contents($configFile))) { $this->markTestSkipped('Could not load ' . $configFile); return; } $this->redisConfig = json_decode($config); $arrayConfig = array(); - foreach ($this->redisConfig as $config) - { + foreach ($this->redisConfig as $config) { $arrayConfig[] = (array)$config; } $this->redisConfig = $arrayConfig; } - if(!$this->useStandalone && !extension_loaded('redis')) { + if (!$this->useStandalone && !extension_loaded('redis')) { $this->fail('The Redis extension is not loaded.'); } } @@ -48,18 +46,14 @@ protected function setUpInternal() */ protected function waitForSlaveReplication() { - if ($this->slaveConfig === null) - { - foreach ($this->redisConfig as $config) - { - if ($config['alias'] === 'slave') - { + if ($this->slaveConfig === null) { + foreach ($this->redisConfig as $config) { + if ($config['alias'] === 'slave') { $this->slaveConfig = $config; break; } } - if ($this->slaveConfig === null) - { + if ($this->slaveConfig === null) { $this->markTestSkipped('Could not load slave config'); return false; @@ -73,25 +67,20 @@ protected function waitForSlaveReplication() $start = microtime(true); $timeout = $start + 60; - while (microtime(true) < $timeout) - { + while (microtime(true) < $timeout) { usleep(100); $role = $slaveConfig->role(); - if ($role[0] !== 'slave') - { + if ($role[0] !== 'slave') { $this->markTestSkipped('slave config does not points to a slave'); return false; } - if ($role[3] === 'connected') - { + if ($role[3] === 'connected') { $masterRole = $masterConfig->role(); - if ($masterRole[0] !== 'master') - { + if ($masterRole[0] !== 'master') { $this->markTestSkipped('master config does not points to a master'); return false; } - if ($role[4] >= $masterRole[1]) - { + if ($role[4] >= $masterRole[1]) { return true; } } @@ -103,7 +92,7 @@ protected function waitForSlaveReplication() public static function setUpBeforeClassInternal() { - if(preg_match('/^WIN/',strtoupper(PHP_OS))){ + if (preg_match('/^WIN/', strtoupper(PHP_OS))) { echo "Unit tests will not work automatically on Windows. Please setup all Redis instances manually:".PHP_EOL; echo "\tredis-server redis-master.conf".PHP_EOL; echo "\tredis-server redis-slave.conf".PHP_EOL; @@ -115,19 +104,19 @@ public static function setUpBeforeClassInternal() } else { chdir(__DIR__.'/../'); if (!file_exists('./tests/tls/ca.crt') || !file_exists('./tests/tls/server.crt')) { - // generate SSL keys - system('./tests/gen-test-certs.sh'); + // generate SSL keys + system('./tests/gen-test-certs.sh'); } chdir(__DIR__); $directoryIterator = new DirectoryIterator(__DIR__); - foreach($directoryIterator as $item){ - if(!$item->isfile() || !preg_match('/^redis\-(.+)\.conf$/',$item->getFilename()) || $item->getFilename() == 'redis-sentinel.conf'){ + foreach ($directoryIterator as $item) { + if (!$item->isfile() || !preg_match('/^redis\-(.+)\.conf$/', $item->getFilename()) || $item->getFilename() == 'redis-sentinel.conf') { continue; } exec('redis-server '.$item->getFilename()); } - copy('redis-master.conf','redis-master.conf.bak'); - copy('redis-slave.conf','redis-slave.conf.bak'); + copy('redis-master.conf', 'redis-master.conf.bak'); + copy('redis-slave.conf', 'redis-slave.conf.bak'); // wait for redis instances to initialize sleep(1); } @@ -135,18 +124,18 @@ public static function setUpBeforeClassInternal() public static function tearDownAfterClassInternal() { - if(preg_match('/^WIN/',strtoupper(PHP_OS))){ + if (preg_match('/^WIN/', strtoupper(PHP_OS))) { echo "Please kill all Redis instances manually:".PHP_EOL; } else { chdir(__DIR__); $directoryIterator = new DirectoryIterator(__DIR__); - foreach($directoryIterator as $item){ - if(!$item->isfile() || !preg_match('/^redis\-(.+)\.pid$/',$item->getFilename())){ + foreach ($directoryIterator as $item) { + if (!$item->isfile() || !preg_match('/^redis\-(.+)\.pid$/', $item->getFilename())) { continue; } $pid = trim(file_get_contents($item->getFilename())); - if(function_exists('posix_kill')){ - posix_kill($pid,15); + if (function_exists('posix_kill')) { + posix_kill($pid, 15); } else { exec('kill '.$pid); } @@ -155,8 +144,8 @@ public static function tearDownAfterClassInternal() @unlink('dump.rdb'); @unlink('redis-master.conf'); @unlink('redis-slave.conf'); - @copy('redis-master.conf.bak','redis-master.conf'); - @copy('redis-slave.conf.bak','redis-slave.conf'); + @copy('redis-master.conf.bak', 'redis-master.conf'); + @copy('redis-slave.conf.bak', 'redis-slave.conf'); } } @@ -164,7 +153,7 @@ public static function tearDownAfterClassInternal() * php 7.2 compat fix, as directly polyfilling for older PHPUnit causes a function signature compatibility issue * This is due to the defined return type */ - public function setExpectedExceptionShim($class, $message = NULL, $code = NULL) + public function setExpectedExceptionShim($class, $message = null, $code = null) { if (method_exists($this, 'setExpectedException')) { $this->setExpectedException($class, $message, $code); diff --git a/tests/phpunit-shims/php7.php b/tests/phpunit-shims/php7.php index d364f32..c1f3282 100644 --- a/tests/phpunit-shims/php7.php +++ b/tests/phpunit-shims/php7.php @@ -2,27 +2,33 @@ abstract class CredisTestCommonShim extends \PHPUnit\Framework\TestCase { - abstract protected function setUpInternal(); - protected function tearDownInternal() {} - public static function setUpBeforeClassInternal() {} - public static function tearDownAfterClassInternal() {} + abstract protected function setUpInternal(); + protected function tearDownInternal() + { + } + public static function setUpBeforeClassInternal() + { + } + public static function tearDownAfterClassInternal() + { + } - protected function setUp() - { - $this->setUpInternal(); - } - protected function tearDown() - { - $this->tearDownInternal(); - } + protected function setUp() + { + $this->setUpInternal(); + } + protected function tearDown() + { + $this->tearDownInternal(); + } - public static function setUpBeforeClass() - { - static::setUpBeforeClassInternal(); - } + public static function setUpBeforeClass() + { + static::setUpBeforeClassInternal(); + } - public static function tearDownAfterClass() - { - static::tearDownAfterClassInternal(); - } -} \ No newline at end of file + public static function tearDownAfterClass() + { + static::tearDownAfterClassInternal(); + } +} diff --git a/tests/phpunit-shims/php8.php b/tests/phpunit-shims/php8.php index 2034829..ca15eab 100644 --- a/tests/phpunit-shims/php8.php +++ b/tests/phpunit-shims/php8.php @@ -1,29 +1,36 @@ setUpInternal(); - } - protected function tearDown(): void - { - $this->tearDownInternal(); - } + protected function setUp(): void + { + $this->setUpInternal(); + } + protected function tearDown(): void + { + $this->tearDownInternal(); + } - public static function setUpBeforeClass(): void - { - static::setUpBeforeClassInternal(); - } + public static function setUpBeforeClass(): void + { + static::setUpBeforeClassInternal(); + } - public static function tearDownAfterClass(): void - { - static::tearDownAfterClassInternal(); - } -} \ No newline at end of file + public static function tearDownAfterClass(): void + { + static::tearDownAfterClassInternal(); + } +} From e078ba0b595d3e9d427265b9e7eb10effe74e6f3 Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Fri, 27 Jan 2023 01:23:25 -0500 Subject: [PATCH 36/50] Remove testing for unsupported versions of PHP (except for 7.4) --- .github/workflows/ci.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8046ac5..17d7142 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,16 +7,6 @@ jobs: fail-fast: false matrix: include: - - php-version: '5.6' - phpunit: '^5.7' - - php-version: '7.0' - phpunit: '^6.5' - - php-version: '7.1' - phpunit: '^7.5' - - php-version: '7.2' - phpunit: '^7.5' - - php-version: '7.3' - phpunit: '^7.5' - php-version: '7.4' phpunit: '^7.5' - php-version: '8.0' From 65e59cf17efde7b32ce9f205927380f8c53a9d71 Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Fri, 27 Jan 2023 06:58:23 +0000 Subject: [PATCH 37/50] Remove docker environments for PHP<7.4 --- testenv/docker-compose.yml | 25 ------------------------- testenv/env/php-5.6/Dockerfile | 24 ------------------------ testenv/env/php-7.0/Dockerfile | 25 ------------------------- testenv/env/php-7.1/Dockerfile | 25 ------------------------- testenv/env/php-7.2/Dockerfile | 25 ------------------------- testenv/env/php-7.3/Dockerfile | 25 ------------------------- 6 files changed, 149 deletions(-) delete mode 100644 testenv/env/php-5.6/Dockerfile delete mode 100644 testenv/env/php-7.0/Dockerfile delete mode 100644 testenv/env/php-7.1/Dockerfile delete mode 100644 testenv/env/php-7.2/Dockerfile delete mode 100644 testenv/env/php-7.3/Dockerfile diff --git a/testenv/docker-compose.yml b/testenv/docker-compose.yml index c527d8f..8b2b6bf 100644 --- a/testenv/docker-compose.yml +++ b/testenv/docker-compose.yml @@ -1,30 +1,5 @@ version: '2' services: - - php-56: - build: env/php-5.6/ - volumes: - - ../:/src/ - - php-70: - build: env/php-7.0/ - volumes: - - ../:/src/ - - php-71: - build: env/php-7.1/ - volumes: - - ../:/src/ - - php-72: - build: env/php-7.2/ - volumes: - - ../:/src/ - - php-73: - build: env/php-7.3/ - volumes: - - ../:/src/ php-74: build: env/php-7.4/ diff --git a/testenv/env/php-5.6/Dockerfile b/testenv/env/php-5.6/Dockerfile deleted file mode 100644 index c6378a4..0000000 --- a/testenv/env/php-5.6/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM php:5.6 -ENV phpunit_verison 5.7 -ENV redis_version 4.0.11 - -RUN apt-get update && \ - apt-get install -y wget - -RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ - chmod +x phpunit-${phpunit_verison}.phar && \ - mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit - -# install php extension -RUN yes '' | pecl install -f redis-4.3.0 && \ - docker-php-ext-enable redis - -# install redis server -RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ - tar -xzf redis-${redis_version}.tar.gz && \ - make -s -C redis-${redis_version} -j - -CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ - cp -rp /src /app && \ - cd /app && \ - phpunit diff --git a/testenv/env/php-7.0/Dockerfile b/testenv/env/php-7.0/Dockerfile deleted file mode 100644 index 82dfef9..0000000 --- a/testenv/env/php-7.0/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM php:7.0 -ENV phpunit_verison 6.5 -ENV redis_version 6.0.8 - -RUN apt-get update && \ - apt-get install -y wget libssl-dev - -RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ - chmod +x phpunit-${phpunit_verison}.phar && \ - mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit - -# install php extension -RUN yes '' | pecl install -f redis && \ - docker-php-ext-enable redis - -# install redis server -RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ - tar -xzf redis-${redis_version}.tar.gz && \ - export BUILD_TLS=yes && \ - make -s -C redis-${redis_version} -j - -CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ - cp -rp /src /app && \ - cd /app && \ - phpunit diff --git a/testenv/env/php-7.1/Dockerfile b/testenv/env/php-7.1/Dockerfile deleted file mode 100644 index e75a4c0..0000000 --- a/testenv/env/php-7.1/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM php:7.1 -ENV phpunit_verison 7.5 -ENV redis_version 6.0.8 - -RUN apt-get update && \ - apt-get install -y wget libssl-dev - -RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ - chmod +x phpunit-${phpunit_verison}.phar && \ - mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit - -# install php extension -RUN yes '' | pecl install -f redis && \ - docker-php-ext-enable redis - -# install redis server -RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ - tar -xzf redis-${redis_version}.tar.gz && \ - export BUILD_TLS=yes && \ - make -s -C redis-${redis_version} -j - -CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ - cp -rp /src /app && \ - cd /app && \ - phpunit diff --git a/testenv/env/php-7.2/Dockerfile b/testenv/env/php-7.2/Dockerfile deleted file mode 100644 index 0e34aab..0000000 --- a/testenv/env/php-7.2/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM php:7.2 -ENV phpunit_verison 7.5 -ENV redis_version 6.0.8 - -RUN apt-get update && \ - apt-get install -y wget libssl-dev - -RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ - chmod +x phpunit-${phpunit_verison}.phar && \ - mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit - -# install php extension -RUN yes '' | pecl install -f redis && \ - docker-php-ext-enable redis - -# install redis server -RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ - tar -xzf redis-${redis_version}.tar.gz && \ - export BUILD_TLS=yes && \ - make -s -C redis-${redis_version} -j - -CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ - cp -rp /src /app && \ - cd /app && \ - phpunit diff --git a/testenv/env/php-7.3/Dockerfile b/testenv/env/php-7.3/Dockerfile deleted file mode 100644 index 279ffa9..0000000 --- a/testenv/env/php-7.3/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM php:7.3 -ENV phpunit_verison 7.5 -ENV redis_version 6.0.8 - -RUN apt-get update && \ - apt-get install -y wget libssl-dev - -RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ - chmod +x phpunit-${phpunit_verison}.phar && \ - mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit - -# install php extension -RUN yes '' | pecl install -f redis && \ - docker-php-ext-enable redis - -# install redis server -RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \ - tar -xzf redis-${redis_version}.tar.gz && \ - export BUILD_TLS=yes && \ - make -s -C redis-${redis_version} -j - -CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \ - cp -rp /src /app && \ - cd /app && \ - phpunit From 739183198fd4ff32728d025c12fcd8c62aa9ae6a Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Fri, 27 Jan 2023 06:58:49 +0000 Subject: [PATCH 38/50] Add vardocs for unlink command --- Client.php | 4 ++-- tests/CredisTest.php | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Client.php b/Client.php index d957779..957a375 100755 --- a/Client.php +++ b/Client.php @@ -56,7 +56,7 @@ public function __construct($message, $code = 0, $exception = null) * @method int|Credis_Client dbsize() * * Keys: - * @method int|Credis_Client del(string|array $key) + * @method int|Credis_Client del(string|array ...$keys) * @method int|Credis_Client exists(string $key) * @method int|Credis_Client expire(string $key, int $seconds) * @method int|Credis_Client expireAt(string $key, int $timestamp) @@ -67,6 +67,7 @@ public function __construct($message, $code = 0, $exception = null) * @method array|Credis_Client sort(string $key, string $arg1, string $valueN = null) * @method int|Credis_Client ttl(string $key) * @method string|Credis_Client type(string $key) + * @method string|Credis_Client unlink(string|array ...$keys) * * Scalars: * @method int|Credis_Client append(string $key, string $value) @@ -1153,7 +1154,6 @@ public function __call($name, $args) case 'msetnx': case 'hmset': case 'hmget': - case 'del': case 'zrangebyscore': case 'zrevrangebyscore': break; diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 2e44fd5..7496675 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -90,11 +90,15 @@ public function testScalars() $this->assertTrue(in_array('FOO', $mGet)); $this->assertTrue(in_array('BAR', $mGet)); - // Delete strings, null response + // Delete strings and check they are deleted $this->assertEquals(2, $this->credis->del('foo', 'bar')); $this->assertFalse($this->credis->get('foo')); $this->assertFalse($this->credis->get('bar')); + // Unlink strings using array args + $this->assertTrue($this->credis->mSet(['foo' => 'FOO', 'bar' => 'BAR'])); + $this->assertEquals(2, $this->credis->unlink(['foo', 'bar'])); + // Long string $longString = str_repeat(md5('asd'), 4096); // 128k (redis.h REDIS_INLINE_MAX_SIZE = 64k) $this->assertTrue($this->credis->set('long', $longString)); From b249ca47bc7fc4b6c09d169b0417b538c9e5d392 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 7 Mar 2023 02:51:16 +0800 Subject: [PATCH 39/50] Add php 8.2 to testing matrix (#179) * Add php 8.2 to testing matrix * Workaround for php-cs-fixer not working with php 8.2 --------- Co-authored-by: Xon <635541+Xon@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17d7142..1912d4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,8 @@ jobs: phpunit: '^9' - php-version: '8.1' phpunit: '^9' + - php-version: '8.2' + phpunit: '^9' name: PHP ${{ matrix.php-version }} steps: - name: Checkout @@ -44,6 +46,8 @@ jobs: fi - name: Run PHP CS Fixer + env: + PHP_CS_FIXER_IGNORE_ENV: 1 run: | if [ -f ./vendor/bin/php-cs-fixer ]; then ./vendor/bin/php-cs-fixer fix --diff --dry-run From 53183bcc0122d4f7f39b5dea5396e3f2b6cbed36 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 7 Mar 2023 02:51:49 +0800 Subject: [PATCH 40/50] Fix hKeys signature (#178) Co-authored-by: Xon <635541+Xon@users.noreply.github.com> --- Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client.php b/Client.php index 957a375..6c5b0f1 100755 --- a/Client.php +++ b/Client.php @@ -111,7 +111,7 @@ public function __construct($message, $code = 0, $exception = null) * @method bool|string|Credis_Client hGet(string $key, string $field) * @method bool|int|Credis_Client hLen(string $key) * @method bool|Credis_Client hDel(string $key, string $field) - * @method array|Credis_Client hKeys(string $key, string $field) + * @method array|Credis_Client hKeys(string $key) * @method array|Credis_Client hVals(string $key) * @method array|Credis_Client hGetAll(string $key) * @method bool|Credis_Client hExists(string $key, string $field) From 9956928d1997f4bcaccb89d26ee2b8d0bf161c34 Mon Sep 17 00:00:00 2001 From: Xon Date: Sat, 1 Apr 2023 03:54:47 +0800 Subject: [PATCH 41/50] mget is documented to return false in some cases, smooth over differences between credis and phpredis error handling on an empty array (#180) Co-authored-by: Xon <635541+Xon@users.noreply.github.com> --- Client.php | 3 +++ tests/CredisTest.php | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/Client.php b/Client.php index 6c5b0f1..8415bbe 100755 --- a/Client.php +++ b/Client.php @@ -1027,6 +1027,9 @@ public function __call($name, $args) if (isset($args[0]) && is_array($args[0])) { $args = array_values($args[0]); } + if (is_array($args) && count($args) === 0) { + return false; + } break; case 'hmset': if (isset($args[1]) && is_array($args[1])) { diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 7496675..b8caa4c 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -84,11 +84,17 @@ public function testScalars() $this->assertTrue(in_array('FOO', $mGet)); $this->assertTrue(in_array('BAR', $mGet)); $this->assertTrue(in_array('', $mGet)); + $mGet = $this->credis->mGet(array('foo')); + $this->assertTrue(in_array('FOO', $mGet)); + $mGet = $this->credis->mGet(array()); + $this->assertTrue($mGet === false); // Non-array $mGet = $this->credis->mGet('foo', 'bar'); $this->assertTrue(in_array('FOO', $mGet)); $this->assertTrue(in_array('BAR', $mGet)); + $mGet = $this->credis->mGet('foo'); + $this->assertTrue(in_array('FOO', $mGet)); // Delete strings and check they are deleted $this->assertEquals(2, $this->credis->del('foo', 'bar')); From 28810439de1d9597b7ba11794ed9479fb6f3de7c Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 18 Apr 2023 23:34:23 +0800 Subject: [PATCH 42/50] Fixes for scan/sscan/zscan/hscan & transaction/multi handling (#181) * Fixes type hinting on for scan/sscan/zscan/hscan * Aligns behavior of scan/sscan/zscan/hscan with phpredis of how an empty/falsy iterator is handled. * Fix errors in exec() could cause the redis connection and credis internal state to become out of sync * Prevent scan/sscan/zscan/hscan from being used in pipeline/multi mode, as phpredis would cause trigger a fatal php error instead of throwing an exception in this case * Fix calling mget([]) in standalone mode didn't match phpredis mode * Fix multi mode failing when commands where issued between multi() and pipeline(). Recommended usage is however pipeline()-> multi()->...->exec() --------- Co-authored-by: Xon <635541+Xon@users.noreply.github.com> --- .editorconfig | 6 ++ .gitattributes | 3 + Client.php | 156 +++++++++++++++++++++++++++++++------------ tests/CredisTest.php | 118 ++++++++++++++++++++++++++++---- 4 files changed, 227 insertions(+), 56 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e13e644 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*.php] +charset=utf-8 +end_of_line=lf +insert_final_newline=true +indent_style=space +indent_size=4 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index e5421a9..059fcf5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,6 @@ /.travis.yml export-ignore /composer.lock export-ignore /phpunit.xml export-ignore +*.php text eol=lf +*.js text eol=lf +*.md text eol=lf \ No newline at end of file diff --git a/Client.php b/Client.php index 8415bbe..01d5e1f 100755 --- a/Client.php +++ b/Client.php @@ -758,6 +758,20 @@ public function select($index) return $response; } + /** + * @param string $caller + * @return void + * @throws CredisException + */ + protected function assertNotPipelineOrMulti($caller) + { + if ($this->standalone && ($this->isMulti || $this->usePipeline) || + // phpredis triggers a php fatal error, so do the check before + !$this->standalone && ($this->redis->getMode() === Redis::MULTI || $this->redis->getMode() === Redis::PIPELINE)) { + throw new CredisException('multi()/pipeline() mode can not be used with '.$caller); + } + } + /** * @param string|array $pattern * @return array @@ -770,49 +784,57 @@ public function pUnsubscribe() } /** - * @param int $Iterator + * @param ?int $Iterator * @param string $pattern * @param int $count * @return bool|array + * @throws CredisException */ public function scan(&$Iterator, $pattern = null, $count = null) { + $this->assertNotPipelineOrMulti(__METHOD__); return $this->__call('scan', array(&$Iterator, $pattern, $count)); } /** - * @param int $Iterator + * @param ?int $Iterator * @param string $field * @param string $pattern * @param int $count * @return bool|array + * @throws CredisException */ public function hscan(&$Iterator, $field, $pattern = null, $count = null) { + $this->assertNotPipelineOrMulti(__METHOD__); return $this->__call('hscan', array($field, &$Iterator, $pattern, $count)); } /** - * @param int $Iterator + * @param ?int $Iterator * @param string $field * @param string $pattern * @param int $Iterator * @return bool|array + * @throws CredisException */ public function sscan(&$Iterator, $field, $pattern = null, $count = null) { + $this->assertNotPipelineOrMulti(__METHOD__); return $this->__call('sscan', array($field, &$Iterator, $pattern, $count)); } /** - * @param int $Iterator + * @param ?int $Iterator * @param string $field * @param string $pattern * @param int $Iterator * @return bool|array + * @throws CredisException */ public function zscan(&$Iterator, $field, $pattern = null, $count = null) { + $this->assertNotPipelineOrMulti(__METHOD__); return $this->__call('zscan', array($field, &$Iterator, $pattern, $count)); } @@ -931,6 +953,7 @@ public function __call($name, $args) // Send request via native PHP if ($this->standalone) { + // Early returns should verify how phpredis behaves! $trackedArgs = array(); switch ($name) { case 'eval': @@ -974,8 +997,10 @@ public function __call($name, $args) break; case 'scan': $trackedArgs = array(&$args[0]); - if (empty($trackedArgs[0])) { + if ($trackedArgs[0] === null) { $trackedArgs[0] = 0; + } elseif ($trackedArgs[0] === 0) { + return false; } $eArgs = array($trackedArgs[0]); if (!empty($args[1])) { @@ -992,8 +1017,10 @@ public function __call($name, $args) case 'zscan': case 'hscan': $trackedArgs = array(&$args[1]); - if (empty($trackedArgs[0])) { + if ($trackedArgs[0] === null) { $trackedArgs[0] = 0; + } elseif ($trackedArgs[0] === 0) { + return false; } $eArgs = array($args[0], $trackedArgs[0]); if (!empty($args[2])) { @@ -1028,7 +1055,7 @@ public function __call($name, $args) $args = array_values($args[0]); } if (is_array($args) && count($args) === 0) { - return false; + return ($this->isMulti || $this->usePipeline) ? $this : false; } break; case 'hmset': @@ -1053,6 +1080,12 @@ public function __call($name, $args) $trackedArgs = $args[1]; } break; + case 'multi': + // calling multi() multiple times is a no-op + if ($this->isMulti) { + return $this; + } + break; } // Flatten arguments $args = self::_flattenArguments($args); @@ -1063,40 +1096,52 @@ public function __call($name, $args) throw new CredisException('A pipeline is already in use and only one pipeline is supported.'); } elseif ($name === 'exec') { if ($this->isMulti) { - $this->commandNames[] = array($name, $trackedArgs); + $this->commandNames[] = array($name, $trackedArgs, true); $this->commands .= self::_prepare_command(array($this->getRenamedCommand($name))); } - // Write request - if ($this->commands) { - $this->write_command($this->commands); - } - $this->commands = null; + try { + // Write request + if ($this->commands) { + $this->write_command($this->commands); + } - // Read response - $queuedResponses = array(); - $response = array(); - foreach ($this->commandNames as $command) { - list($name, $arguments) = $command; - $result = $this->read_reply($name, true); - if ($result !== null) { - $result = $this->decode_reply($name, $result, $arguments); - } else { - $queuedResponses[] = $command; + // Read response + $queuedResponses = array(); + $response = array(); + foreach ($this->commandNames as $command) { + list($name, $arguments, $requireDispatch) = $command; + if (!$requireDispatch) { + $queuedResponses[] = $command; + continue; + } + $result = $this->read_reply($name, true); + if ($result !== null) { + if ($name === 'multi') { + continue; + } + $result = $this->decode_reply($name, $result, $arguments); + $response[] = $result; + } else { + $queuedResponses[] = $command; + } } - $response[] = $result; - } - if ($this->isMulti) { - $response = array_pop($response); - foreach ($queuedResponses as $key => $command) { - list($name, $arguments) = $command; - $response[$key] = $this->decode_reply($name, $response[$key], $arguments); + if ($this->isMulti) { + $execResponse = array_pop($response); + foreach ($queuedResponses as $key => $command) { + list($name, $arguments) = $command; + $response[] = $this->decode_reply($name, $execResponse[$key], $arguments); + } } + } catch (CredisException $e) { + // the connection on redis's side is likely in a bad state, force it closed to abort the pipeline/transaction + $this->close(true); + throw $e; + } finally { + $this->commands = $this->commandNames = null; + $this->isMulti = $this->usePipeline = false; } - - $this->commandNames = null; - $this->usePipeline = $this->isMulti = false; return $response; } elseif ($name === 'discard') { $this->commands = null; @@ -1107,7 +1152,7 @@ public function __call($name, $args) $this->isMulti = true; } array_unshift($args, $this->getRenamedCommand($name)); - $this->commandNames[] = array($name, $trackedArgs); + $this->commandNames[] = array($name, $trackedArgs, true); $this->commands .= self::_prepare_command($args); return $this; } @@ -1116,7 +1161,9 @@ public function __call($name, $args) // Start pipeline mode if ($name === 'pipeline') { $this->usePipeline = true; - $this->commandNames = array(); + if (!$this->isMulti) { + $this->commandNames = []; + } $this->commands = ''; return $this; } @@ -1129,18 +1176,41 @@ public function __call($name, $args) // Non-pipeline mode array_unshift($args, $this->getRenamedCommand($name)); $command = self::_prepare_command($args); - $this->write_command($command); - $response = $this->read_reply($name); - $response = $this->decode_reply($name, $response, $trackedArgs); + // transaction mode needs to track commands + if ($this->isMulti) { + try { + if ($name === 'exec' || $name === 'discard') { + try { + $this->write_command($command); + $response = $this->read_reply($name); + $response = $this->decode_reply($name, $response, $trackedArgs); + } finally { + $this->isMulti = false; + $this->commandNames = []; + } + } else { + $this->commandNames[] = array($name, $trackedArgs, false); + $this->write_command($command); + $response = $this->read_reply($name); + } + } catch (CredisException $e) { + // the connection on redis's side is likely in a bad state, force it closed to abort the transaction + $this->isMulti = false; + $this->commandNames = []; + $this->close(true); + throw $e; + } + } else { + $this->write_command($command); + $response = $this->read_reply($name); + $response = $this->decode_reply($name, $response, $trackedArgs); + } // Watch mode disables reconnect so error is thrown - if ($name == 'watch') { + if ($name === 'watch') { $this->isWatching = true; - } // Transaction mode - elseif ($this->isMulti && ($name == 'exec' || $name == 'discard')) { - $this->isMulti = false; } // Started transaction - elseif ($this->isMulti || $name == 'multi') { + elseif ($this->isMulti || $name === 'multi') { $this->isMulti = true; $response = $this; } diff --git a/tests/CredisTest.php b/tests/CredisTest.php index b8caa4c..4260e5c 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -320,6 +320,14 @@ public function testPipeline() $this->credis->pipeline(); $this->pipelineTestInternal(); $this->assertEquals(array(), $this->credis->pipeline()->exec()); + + try { + $iterator = null; + $reply = $this->credis->pipeline()->scan($iterator, '*')->exec(); + $this->assertFalse($reply); + } catch (CredisException $e) { + $this->assertStringStartsWith('multi()/pipeline() mode can not be used with', $e->getMessage()); + } } public function testPipelineMulti() @@ -330,6 +338,14 @@ public function testPipelineMulti() $this->credis->pipeline()->multi(); $this->pipelineTestInternal(); $this->assertEquals(array(), $this->credis->pipeline()->multi()->exec()); + + try { + $iterator = null; + $reply = $this->credis->pipeline()->multi()->scan($iterator, '*')->exec(); + $this->assertFalse($reply); + } catch (CredisException $e) { + $this->assertStringStartsWith('multi()/pipeline() mode can not be used with', $e->getMessage()); + } } public function testWatchMultiUnwatch() @@ -357,6 +373,7 @@ protected function pipelineTestInternal() $reply = $this->credis ->set('a', 123) ->get('a') + ->mGet([]) // will be dropped from return result by phpredis ->sAdd('b', 123) ->sMembers('b') ->set('empty', '') @@ -384,6 +401,7 @@ protected function pipelineTestInternal() array( true, // set('a', 123) '123', // get('a') + //[], // get([]) - phpredis doesn't return this 1, // sAdd('b', 123) array(123), // sMembers('b') true, // set('empty', '') @@ -444,30 +462,65 @@ protected function pipelineTestInternal() public function testTransaction() { $reply = $this->credis->multi() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(1,1), $reply); $reply = $this->credis->pipeline()->multi() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(2,2), $reply); + $reply = $this->credis->pipeline() + ->incr('foo') + ->multi() + ->incr('foo') + ->incr('bar') + ->exec(); + $this->assertEquals(array(3,4,3), $reply); + + $reply = $this->credis->multi() + ->incr('foo') + ->pipeline() + ->incr('foo') + ->incr('bar') + ->exec(); + $this->assertEquals(array(5,6,4), $reply); + $reply = $this->credis->multi()->pipeline() - ->incr('foo') - ->incr('bar') - ->exec(); - $this->assertEquals(array(3,3), $reply); + ->incr('foo') + ->incr('bar') + ->exec(); + $this->assertEquals(array(7,5), $reply); + + $reply = $this->credis->multi() + ->incr('foo') + ->pipeline() + ->incr('foo') + ->incr('bar') + ->exec(); + $this->assertEquals(array(8,9,6), $reply); $reply = $this->credis->multi() - ->set('a', 3) - ->lpop('a') - ->exec(); + ->set('a', 3) + ->lpop('a') // bad operation for "a"'s type + ->exec(); $this->assertEquals(2, count($reply)); $this->assertEquals(true, $reply[0]); $this->assertFalse($reply[1]); + + $reply = $this->credis->multi()->pipeline() + ->set('a', 3) + ->lpop('a') // bad operation for "a"'s type + ->exec(); + $this->assertEquals(2, count($reply)); + $this->assertEquals(true, $reply[0]); + $this->assertFalse($reply[1]); + + $this->assertEquals(array(), $this->credis->multi()->multi()->exec()); + $this->assertEquals(array(), $this->credis->pipeline()->multi()->multi()->exec()); } public function testServer() @@ -821,6 +874,16 @@ public function testHscan() $this->assertEquals($iterator, 0); $this->assertEquals($result, ['name'=>'Jack']); } + + public function testHscanEmptyIterator() + { + $this->credis->hmset('set', ['foo' => 'bar']); + $iterator = 0; + $result = $this->credis->zscan($iterator, 'hash', '*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, false); + } + public function testSscan() { $this->credis->sadd('set', 'name', 'Jack'); @@ -830,6 +893,16 @@ public function testSscan() $this->assertEquals($iterator, 0); $this->assertEquals($result, [0=>'name']); } + + public function testSscanEmptyIterator() + { + $this->credis->sadd('set', 'foo', 'bar'); + $iterator = 0; + $result = $this->credis->zscan($iterator, 'set', '*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, false); + } + public function testZscan() { $this->credis->zadd('sortedset', 0, 'name'); @@ -839,6 +912,16 @@ public function testZscan() $this->assertEquals($iterator, 0); $this->assertEquals($result, ['name'=>'0']); } + + public function testZscanEmptyIterator() + { + $this->credis->zadd('sortedset', 0, 'name'); + $iterator = 0; + $result = $this->credis->zscan($iterator, 'sortedset', '*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, false); + } + public function testscan() { $seen = array(); @@ -861,6 +944,15 @@ public function testscan() $this->assertEquals(count($seen), 100); } + public function testscanEmptyIterator() + { + $this->credis->set('foo', 'bar'); + $iterator = 0; + $result = $this->credis->scan($iterator, '*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, false); + } + public function testPing() { $pong = $this->credis->ping(); From 457215677648940e91b526b379ede86e0f271ecc Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Thu, 26 Oct 2023 12:51:39 -0400 Subject: [PATCH 43/50] Fix strlen deprecation notice, cleanup IDE warnings, remove broken tests for zrangebyscore using an array containing 'withscores' as an argument. --- Client.php | 176 ++++++++++++++++++++++++++----------------- tests/CredisTest.php | 18 ++--- 2 files changed, 115 insertions(+), 79 deletions(-) diff --git a/Client.php b/Client.php index 01d5e1f..463a2b1 100755 --- a/Client.php +++ b/Client.php @@ -195,7 +195,7 @@ class Credis_Client /** * SSL Meta information - * @var string + * @var array|null */ protected $sslMeta; @@ -324,7 +324,7 @@ class Credis_Client /** * Gets Useful Meta debug information about the SSL * - * @return string + * @return array|null */ public function getSslMeta() { @@ -343,6 +343,7 @@ public function getSslMeta() * @param string|null $password The authentication password of the Redis server * @param string|null $username The authentication username of the Redis server * @param array|null $tlsOptions The TLS/SSL context options. See https://www.php.net/manual/en/context.ssl.php for details + * @throws CredisException */ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null, array $tlsOptions = null) { @@ -466,6 +467,9 @@ public function setCloseOnDestruct($flag) return $this; } + /** + * @throws CredisException + */ public function setTlsOptions(array $tlsOptions) { if ($this->connected) { @@ -474,6 +478,9 @@ public function setTlsOptions(array $tlsOptions) $this->tlsOptions = $tlsOptions; } + /** + * @throws CredisException + */ protected function convertHost() { if (preg_match('#^(tcp|tls|ssl|tlsv\d(?:\.\d)?|unix)://(.+)$#', $this->host, $matches)) { @@ -634,9 +641,13 @@ public function setReadTimeout($timeout) stream_set_timeout($this->redis, (int)floor($timeout), ($timeout - floor($timeout)) * 1000000); } elseif (defined('Redis::OPT_READ_TIMEOUT')) { // supported in phpredis 2.2.3 - // a timeout value of -1 means reads will not timeout + // a timeout value of -1 means reads will not time out $timeout = $timeout == 0 ? -1 : $timeout; - $this->redis->setOption(Redis::OPT_READ_TIMEOUT, $timeout); + try { + $this->redis->setOption(Redis::OPT_READ_TIMEOUT, $timeout); + } catch (RedisException $e) { + throw new CredisException($e->getMessage(), $e->getCode(), $e); + } } } return $this; @@ -657,7 +668,8 @@ public function close($force = false) $this->redis = null; } } catch (Exception $e) { - ; // Ignore exceptions on close + // Ignore exceptions on close + $result = false; } $this->connected = $this->usePipeline = $this->isMulti = $this->isWatching = false; } @@ -675,6 +687,7 @@ public function close($force = false) * @param string|callable|array $command * @param string|null $alias * @return $this + * @throws CredisException */ public function renameCommand($command, $alias = null) { @@ -734,6 +747,7 @@ public function getRenamedCommand($command) * @param string $password * @param string|null $username * @return bool + * @throws CredisException */ public function auth($password, $username = null) { @@ -750,6 +764,7 @@ public function auth($password, $username = null) /** * @param int $index * @return bool + * @throws CredisException */ public function select($index) { @@ -773,12 +788,13 @@ protected function assertNotPipelineOrMulti($caller) } /** - * @param string|array $pattern + * @param string|array ...$args * @return array + * @throws CredisException */ - public function pUnsubscribe() + public function pUnsubscribe(...$args) { - list($command, $channel, $subscribedChannels) = $this->__call('punsubscribe', func_get_args()); + list($command, $channel, $subscribedChannels) = $this->__call('punsubscribe', $args); $this->subscribed = $subscribedChannels > 0; return array($command, $channel, $subscribedChannels); } @@ -814,7 +830,7 @@ public function hscan(&$Iterator, $field, $pattern = null, $count = null) * @param ?int $Iterator * @param string $field * @param string $pattern - * @param int $Iterator + * @param ?int $count * @return bool|array * @throws CredisException */ @@ -828,7 +844,7 @@ public function sscan(&$Iterator, $field, $pattern = null, $count = null) * @param ?int $Iterator * @param string $field * @param string $pattern - * @param int $Iterator + * @param ?int $count * @return bool|array * @throws CredisException */ @@ -874,12 +890,13 @@ public function pSubscribe($patterns, $callback) } /** - * @param string|array $pattern + * @param string|array ...$args * @return array + * @throws CredisException */ - public function unsubscribe() + public function unsubscribe(...$args) { - list($command, $channel, $subscribedChannels) = $this->__call('unsubscribe', func_get_args()); + list($command, $channel, $subscribedChannels) = $this->__call('unsubscribe', $args); $this->subscribed = $subscribedChannels > 0; return array($command, $channel, $subscribedChannels); } @@ -922,6 +939,7 @@ public function subscribe($channels, $callback) /** * @param string|null $name * @return string|Credis_Client + * @throws CredisException */ public function ping($name = null) { @@ -933,6 +951,7 @@ public function ping($name = null) * @param array $args * * @return array|Credis_Client + * @throws CredisException */ public function rawCommand($command, array $args) { @@ -944,6 +963,9 @@ public function rawCommand($command, array $args) } } + /** + * @throws CredisException + */ public function __call($name, $args) { // Lazy connection @@ -1301,18 +1323,15 @@ public function __call($name, $args) try { // Proxy pipeline mode to the phpredis library if ($name == 'pipeline' || $name == 'multi') { - if ($this->isMulti) { - return $this; - } else { + if (!$this->isMulti) { $this->isMulti = true; $this->redisMulti = call_user_func_array(array($this->redis, $name), $args); - return $this; } + return $this; } elseif ($name == 'exec' || $name == 'discard') { $this->isMulti = false; $response = $this->redisMulti->$name(); $this->redisMulti = null; - #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n"; return $response; } @@ -1344,9 +1363,13 @@ public function __call($name, $args) } // Wrap exceptions catch (RedisException $e) { $code = 0; - if (!($result = $this->redis->IsConnected())) { - $this->close(true); - $code = CredisException::CODE_DISCONNECTED; + try { + if (!($result = $this->redis->IsConnected())) { + $this->close(true); + $code = CredisException::CODE_DISCONNECTED; + } + } catch (RedisException $e2) { + throw new CredisException($e2->getMessage(), $e2->getCode(), $e2); } throw new CredisException($e->getMessage(), $code, $e); } @@ -1354,62 +1377,69 @@ public function __call($name, $args) #echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n"; // change return values where it is too difficult to minim in standalone mode - switch ($name) { - case 'type': - $typeMap = array( - self::TYPE_NONE, - self::TYPE_STRING, - self::TYPE_SET, - self::TYPE_LIST, - self::TYPE_ZSET, - self::TYPE_HASH, - ); - $response = $typeMap[$response]; - break; + try { + switch ($name) { + case 'type': + $typeMap = array( + self::TYPE_NONE, + self::TYPE_STRING, + self::TYPE_SET, + self::TYPE_LIST, + self::TYPE_ZSET, + self::TYPE_HASH, + ); + $response = $typeMap[$response]; + break; // Handle scripting errors - case 'eval': - case 'evalsha': - case 'script': - $error = $this->redis->getLastError(); - $this->redis->clearLastError(); - if ($error && substr($error, 0, 8) == 'NOSCRIPT') { - $response = null; - } elseif ($error) { - throw new CredisException($error); - } - break; - case 'exists': - // smooth over phpredis-v4 vs earlier difference to match documented credis return results - $response = (int)$response; - break; - case 'ping': - if ($response) { - if ($response === true) { - $response = isset($args[0]) ? $args[0] : "PONG"; - } elseif ($response[0] === '+') { - $response = substr($response, 1); - } - } - break; - case 'auth': - if (is_bool($response) && $response === true) { + case 'eval': + case 'evalsha': + case 'script': + $error = $this->redis->getLastError(); $this->redis->clearLastError(); - } + if ($error && substr($error, 0, 8) == 'NOSCRIPT') { + $response = null; + } elseif ($error) { + throw new CredisException($error); + } + break; + case 'exists': + // smooth over phpredis-v4 vs earlier difference to match documented credis return results + $response = (int)$response; + break; + case 'ping': + if ($response) { + if ($response === true) { + $response = isset($args[0]) ? $args[0] : "PONG"; + } elseif ($response[0] === '+') { + $response = substr($response, 1); + } + } + break; + case 'auth': + if (is_bool($response) && $response === true) { + $this->redis->clearLastError(); + } // no break - default: - $error = $this->redis->getLastError(); - $this->redis->clearLastError(); - if ($error) { - throw new CredisException(rtrim($error)); - } - break; + default: + $error = $this->redis->getLastError(); + $this->redis->clearLastError(); + if ($error) { + throw new CredisException(rtrim($error)); + } + break; + } + } catch (RedisException $e) { + throw new CredisException($e->getMessage(), $e->getCode(), $e); } } return $response; } + /** + * @throws CredisException + */ protected function write_command($command) { // Reconnect on lost connection (Redis server "timeout" exceeded since last command) @@ -1442,6 +1472,9 @@ protected function write_command($command) } } + /** + * @throws CredisException + */ protected function read_reply($name = '', $returnQueued = false) { $reply = fgets($this->redis); @@ -1509,12 +1542,14 @@ protected function read_reply($name = '', $returnQueued = false) break; default: throw new CredisException('Invalid response: ' . print_r($reply, true)); - break; } return $response; } + /** + * @throws CredisException + */ protected function decode_reply($name, $response, array &$arguments = array()) { // Smooth over differences between phpredis and standalone response @@ -1615,7 +1650,7 @@ private static function _prepare_command($args) private static function _map($arg) { - return sprintf('$%d%s%s', strlen($arg), "\r\n", $arg); + return sprintf('$%d%s%s', strlen((string)$arg), "\r\n", $arg); } /** @@ -1626,7 +1661,8 @@ private static function _map($arg) * becomes * array('zrangebyscore', '-inf', 123, 'limit', '0', '1') * - * @param array $in + * @param array $arguments + * @param array $out * @return array */ private static function _flattenArguments(array $arguments, &$out = array()) diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 4260e5c..cc56213 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -151,9 +151,9 @@ public function testSortedSets() $this->assertEquals(10, $range['And']); // withscores-option is off - $range = $this->credis->zRange('myset', 0, 4, array('withscores')); - $this->assertEquals(4, count($range)); - $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores +// $range = $this->credis->zRange('myset', 0, 4, array('withscores')); +// $this->assertEquals(4, count($range)); +// $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores $range = $this->credis->zRange('myset', 0, 4, array('withscores' => false)); $this->assertEquals(4, count($range)); @@ -201,9 +201,9 @@ public function testSortedSets() $this->assertEquals(11, $range['Goodbye']); // withscores-option is off - $range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores')); - $this->assertEquals(4, count($range)); - $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores +// $range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores')); +// $this->assertEquals(4, count($range)); +// $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores $range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores' => false)); $this->assertEquals(4, count($range)); @@ -232,9 +232,9 @@ public function testSortedSets() $this->assertEquals(11, $range['Goodbye']); // withscores-option is off - $range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores')); - $this->assertEquals(4, count($range)); - $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores +// $range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores')); +// $this->assertEquals(4, count($range)); +// $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores $range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores' => false)); $this->assertEquals(4, count($range)); From c985c8d234809e75e3f8b6dc328ea13315871b40 Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Thu, 26 Oct 2023 12:59:03 -0400 Subject: [PATCH 44/50] Fix PHP CS Fixer errors --- Client.php | 3 +- tests/CredisStandaloneTest.php | 28 +- tests/CredisTest.php | 658 ++++++++++++++++----------------- 3 files changed, 344 insertions(+), 345 deletions(-) diff --git a/Client.php b/Client.php index 463a2b1..0107191 100755 --- a/Client.php +++ b/Client.php @@ -1391,7 +1391,6 @@ public function __call($name, $args) $response = $typeMap[$response]; break; - // Handle scripting errors case 'eval': case 'evalsha': case 'script': @@ -1420,7 +1419,7 @@ public function __call($name, $args) if (is_bool($response) && $response === true) { $this->redis->clearLastError(); } - // no break + // no break default: $error = $this->redis->getLastError(); $this->redis->clearLastError(); diff --git a/tests/CredisStandaloneTest.php b/tests/CredisStandaloneTest.php index 7a51ba3..4bad9a9 100644 --- a/tests/CredisStandaloneTest.php +++ b/tests/CredisStandaloneTest.php @@ -15,20 +15,20 @@ public function testPersistentConnectionsOnStandAloneTcpConnection() $this->assertEquals('value', $this->credis->get('key')); } - public function testPersistentvsNonPersistent() - { - $this->assertTrue(true); - } + public function testPersistentvsNonPersistent() + { + $this->assertTrue(true); + } - public function testStandAloneArgumentsExtra() - { - $this->assertTrue($this->credis->hMSet('hash', array('field1' => 'value1', 'field2' => 'value2'), 'field3', 'value3')); - $this->assertEquals(array('field1' => 'value1', 'field2' => 'value2', 'field3' =>'value3'), $this->credis->hMGet('hash', array('field1','field2','field3'))); - } + public function testStandAloneArgumentsExtra() + { + $this->assertTrue($this->credis->hMSet('hash', array('field1' => 'value1', 'field2' => 'value2'), 'field3', 'value3')); + $this->assertEquals(array('field1' => 'value1', 'field2' => 'value2', 'field3' =>'value3'), $this->credis->hMGet('hash', array('field1','field2','field3'))); + } - public function testStandAloneMultiPipelineThrowsException() - { - $this->setExpectedExceptionShim('CredisException', 'A pipeline is already in use and only one pipeline is supported.'); - $this->credis->pipeline()->pipeline(); - } + public function testStandAloneMultiPipelineThrowsException() + { + $this->setExpectedExceptionShim('CredisException', 'A pipeline is already in use and only one pipeline is supported.'); + $this->credis->pipeline()->pipeline(); + } } diff --git a/tests/CredisTest.php b/tests/CredisTest.php index cc56213..7e81dc4 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -151,9 +151,9 @@ public function testSortedSets() $this->assertEquals(10, $range['And']); // withscores-option is off -// $range = $this->credis->zRange('myset', 0, 4, array('withscores')); -// $this->assertEquals(4, count($range)); -// $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores + // $range = $this->credis->zRange('myset', 0, 4, array('withscores')); + // $this->assertEquals(4, count($range)); + // $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores $range = $this->credis->zRange('myset', 0, 4, array('withscores' => false)); $this->assertEquals(4, count($range)); @@ -201,9 +201,9 @@ public function testSortedSets() $this->assertEquals(11, $range['Goodbye']); // withscores-option is off -// $range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores')); -// $this->assertEquals(4, count($range)); -// $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores + // $range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores')); + // $this->assertEquals(4, count($range)); + // $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores $range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores' => false)); $this->assertEquals(4, count($range)); @@ -232,9 +232,9 @@ public function testSortedSets() $this->assertEquals(11, $range['Goodbye']); // withscores-option is off -// $range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores')); -// $this->assertEquals(4, count($range)); -// $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores + // $range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores')); + // $this->assertEquals(4, count($range)); + // $this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores $range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores' => false)); $this->assertEquals(4, count($range)); @@ -353,10 +353,10 @@ public function testWatchMultiUnwatch() $this->assertTrue($this->credis->watch('foo', 'bar')); $reply = $this->credis->pipeline() - ->multi() - ->set('foo', 1) - ->set('bar', 1) - ->exec(); + ->multi() + ->set('foo', 1) + ->set('bar', 1) + ->exec(); $this->assertEquals( array( true, @@ -462,59 +462,59 @@ protected function pipelineTestInternal() public function testTransaction() { $reply = $this->credis->multi() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(1,1), $reply); $reply = $this->credis->pipeline()->multi() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(2,2), $reply); $reply = $this->credis->pipeline() - ->incr('foo') - ->multi() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->multi() + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(3,4,3), $reply); $reply = $this->credis->multi() - ->incr('foo') - ->pipeline() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->pipeline() + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(5,6,4), $reply); $reply = $this->credis->multi()->pipeline() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(7,5), $reply); $reply = $this->credis->multi() - ->incr('foo') - ->pipeline() - ->incr('foo') - ->incr('bar') - ->exec(); + ->incr('foo') + ->pipeline() + ->incr('foo') + ->incr('bar') + ->exec(); $this->assertEquals(array(8,9,6), $reply); $reply = $this->credis->multi() - ->set('a', 3) - ->lpop('a') // bad operation for "a"'s type - ->exec(); + ->set('a', 3) + ->lpop('a') // bad operation for "a"'s type + ->exec(); $this->assertEquals(2, count($reply)); $this->assertEquals(true, $reply[0]); $this->assertFalse($reply[1]); $reply = $this->credis->multi()->pipeline() - ->set('a', 3) - ->lpop('a') // bad operation for "a"'s type - ->exec(); + ->set('a', 3) + ->lpop('a') // bad operation for "a"'s type + ->exec(); $this->assertEquals(2, count($reply)); $this->assertEquals(true, $reply[0]); $this->assertFalse($reply[1]); @@ -597,283 +597,283 @@ public function testPubsub() } } } - public function testDb() - { - $this->tearDown(); - $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 1); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertTrue($this->credis->set('database', 1)); - $this->credis->close(); - $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 0); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertFalse($this->credis->get('database')); - $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 1); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertEquals(1, $this->credis->get('database')); - } - - /** - * @group Auth - */ - public function testPassword() - { - $this->tearDown(); - $this->assertArrayHasKey('password', $this->redisConfig[4]); - $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, $this->redisConfig[4]['password']); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertInstanceOf('Credis_Client', $this->credis->connect()); - $this->assertTrue($this->credis->set('key', 'value')); - $this->credis->close(); - $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, 'wrongpassword'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - try { - $this->credis->connect(); - $this->fail('connect should fail with wrong password'); - } catch(CredisException $e) { - if (strpos($e->getMessage(), 'username') !== false) { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } else { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); - } - - $this->credis->close(); - } - $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - try { - $this->credis->set('key', 'value'); - } catch(CredisException $e) { - $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); - } - try { - $this->credis->auth('anotherwrongpassword'); - } catch(CredisException $e) { - if (strpos($e->getMessage(), 'username') !== false) { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } else { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); - } - } - $this->assertTrue($this->credis->auth('thepassword')); - $this->assertTrue($this->credis->set('key', 'value')); - } - - /** - * @group Auth - */ - public function testUsernameAndPassword() - { - $this->tearDown(); - $this->assertArrayHasKey('password', $this->redisConfig[7]); - $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, $this->redisConfig[7]['password'], $this->redisConfig[7]['username']); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertInstanceOf('Credis_Client', $this->credis->connect()); - $this->assertTrue($this->credis->set('key', 'value')); - $this->credis->close(); - $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, 'wrongpassword', $this->redisConfig[7]['username']); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - try { - $this->credis->connect(); - $this->fail('connect should fail with wrong password'); - } catch(CredisException $e) { - if (strpos($e->getMessage(), 'username') !== false) { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } else { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); - } - - $this->credis->close(); - } - $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - try { - $this->credis->set('key', 'value'); - } catch(CredisException $e) { - $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); - } - try { - $this->credis->auth('anotherwrongpassword'); - } catch(CredisException $e) { - if (strpos($e->getMessage(), 'username') !== false) { - $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); - } else { - $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); - } - } - $this->assertTrue($this->credis->auth('thepassword')); - $this->assertTrue($this->credis->set('key', 'value')); - } - - public function testGettersAndSetters() - { - $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); - $this->assertEquals($this->credis->getSelectedDb(), 0); - $this->assertTrue($this->credis->select(2)); - $this->assertEquals($this->credis->getSelectedDb(), 2); - $this->assertTrue($this->credis->isConnected()); - $this->credis->close(); - $this->assertFalse($this->credis->isConnected()); - $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], null, 'persistenceId'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->assertEquals('persistenceId', $this->credis->getPersistence()); - $this->credis = new Credis_Client('localhost', 12345); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->credis->setMaxConnectRetries(1); - if ($this->useStandalone) { - $this->setExpectedExceptionShim('CredisException', 'Connection to Redis standalone tcp://localhost:12345 failed after 2 failures.'); - } else { - $this->setExpectedExceptionShim('CredisException', 'Connection to Redis tcp://localhost:12345 failed after 2 failures.'); - } - $this->credis->connect(); - } - - public function testConnectionStrings() - { - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); - $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(), 6379); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); - $this->assertEquals($this->credis->getPersistence(), 'abc123'); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'], 6380); - $this->assertEquals($this->credis->getPort(), 6380); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'], null, null, "abc123"); - $this->assertEquals($this->credis->getPersistence(), 'abc123'); - } - - public function testConnectionStringsTls() - { - foreach (['ssl','tls','tlsv1.0','tlsv1.1','tlsv1.2'] as $prefix) { - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); - $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host']); - $this->assertEquals($this->credis->getPort(), 6379); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); - $this->assertEquals($this->credis->getPersistence(), 'abc123'); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'], 6380); - $this->assertEquals($this->credis->getPort(), 6380); - $this->assertTrue($this->credis->isTls()); - $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'], null, null, "abc123"); - $this->assertEquals($this->credis->getPersistence(), 'abc123'); - $this->assertTrue($this->credis->isTls()); - } - } - - public function testTLSConnection() - { - /** - https://www.php.net/manual/en/context.ssl.php - > Oddly, if "tls://" is set below, then TLSv1 is forced, but using "ssl://" allows TLSv1.2 - - and - >Recommended use "ssl://" transport. - > in php 5.5 ~ 7.1 - > ssl:// transport = ssl_v2|ssl_v3|tls_v1.0|tls_v1.1|tls_v1.2 - > tls:// transport = tls_v1.0 - > after 7.2 ssl:// and tls:// transports is same - > php 7.2 ~ 7.3 = tls_v1.0|tls_v1.1|tls_v1.2 - > php 7.4 ~ 8.1 = tls_v1.0|tls_v1.1|tls_v1.2|tls_v1.3 - */ - - $this->credis = new Credis_Client('ssl://'.$this->redisConfig[8]['host'] . ':' . $this->redisConfig[8]['port']); - $this->credis->setTlsOptions((array)$this->redisConfig[8]['ssl']); - $this->credis->connect(); - $this->assertTrue(true); - } - - /** - * @group UnixSocket - */ - public function testConnectionStringsSocket() - { - $this->credis = new Credis_Client(realpath(__DIR__).'/redis.sock'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - $this->credis->connect(); - $this->assertTrue($this->credis->isConnected()); - $this->credis->set('key', 'value'); - $this->assertEquals('value', $this->credis->get('key')); - } - - public function testInvalidTcpConnectionString() - { - $this->credis->close(); - $this->setExpectedExceptionShim('CredisException', 'Invalid host format; expected tcp://host[:port][/persistence_identifier]'); - $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':abc'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - } - - public function testInvalidTlsConnectionString() - { - $this->credis->close(); - $this->setExpectedExceptionShim('CredisException', 'Invalid host format; expected tls://host[:port][/persistence_identifier]'); - $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':abc'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - } - - /** - * @group UnixSocket - */ - public function testInvalidUnixSocketConnectionString() - { - $this->credis->close(); - $this->setExpectedExceptionShim('CredisException', 'Invalid unix socket format; expected unix:///path/to/redis.sock'); - $this->credis = new Credis_Client('unix://path/to/redis.sock'); - if ($this->useStandalone) { - $this->credis->forceStandalone(); - } - } - - public function testForceStandAloneAfterEstablishedConnection() - { - $this->credis->connect(); - if (! $this->useStandalone) { - $this->setExpectedExceptionShim('CredisException', 'Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.'); - } - $this->credis->forceStandalone(); - $this->assertTrue(true); - } - public function testHscan() - { - $this->credis->hmset('hash', array('name' => 'Jack','age' =>33)); - $iterator = null; - $result = $this->credis->hscan($iterator, 'hash', 'n*', 10); - $this->assertEquals($iterator, 0); - $this->assertEquals($result, ['name'=>'Jack']); - } + public function testDb() + { + $this->tearDown(); + $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 1); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertTrue($this->credis->set('database', 1)); + $this->credis->close(); + $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 0); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertFalse($this->credis->get('database')); + $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 1); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertEquals(1, $this->credis->get('database')); + } + + /** + * @group Auth + */ + public function testPassword() + { + $this->tearDown(); + $this->assertArrayHasKey('password', $this->redisConfig[4]); + $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, $this->redisConfig[4]['password']); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertInstanceOf('Credis_Client', $this->credis->connect()); + $this->assertTrue($this->credis->set('key', 'value')); + $this->credis->close(); + $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, 'wrongpassword'); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try { + $this->credis->connect(); + $this->fail('connect should fail with wrong password'); + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + + $this->credis->close(); + } + $this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try { + $this->credis->set('key', 'value'); + } catch(CredisException $e) { + $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); + } + try { + $this->credis->auth('anotherwrongpassword'); + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + } + $this->assertTrue($this->credis->auth('thepassword')); + $this->assertTrue($this->credis->set('key', 'value')); + } + + /** + * @group Auth + */ + public function testUsernameAndPassword() + { + $this->tearDown(); + $this->assertArrayHasKey('password', $this->redisConfig[7]); + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, $this->redisConfig[7]['password'], $this->redisConfig[7]['username']); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertInstanceOf('Credis_Client', $this->credis->connect()); + $this->assertTrue($this->credis->set('key', 'value')); + $this->credis->close(); + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, 'wrongpassword', $this->redisConfig[7]['username']); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try { + $this->credis->connect(); + $this->fail('connect should fail with wrong password'); + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + + $this->credis->close(); + } + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try { + $this->credis->set('key', 'value'); + } catch(CredisException $e) { + $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); + } + try { + $this->credis->auth('anotherwrongpassword'); + } catch(CredisException $e) { + if (strpos($e->getMessage(), 'username') !== false) { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } else { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + } + $this->assertTrue($this->credis->auth('thepassword')); + $this->assertTrue($this->credis->set('key', 'value')); + } + + public function testGettersAndSetters() + { + $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getSelectedDb(), 0); + $this->assertTrue($this->credis->select(2)); + $this->assertEquals($this->credis->getSelectedDb(), 2); + $this->assertTrue($this->credis->isConnected()); + $this->credis->close(); + $this->assertFalse($this->credis->isConnected()); + $this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], null, 'persistenceId'); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertEquals('persistenceId', $this->credis->getPersistence()); + $this->credis = new Credis_Client('localhost', 12345); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->credis->setMaxConnectRetries(1); + if ($this->useStandalone) { + $this->setExpectedExceptionShim('CredisException', 'Connection to Redis standalone tcp://localhost:12345 failed after 2 failures.'); + } else { + $this->setExpectedExceptionShim('CredisException', 'Connection to Redis tcp://localhost:12345 failed after 2 failures.'); + } + $this->credis->connect(); + } + + public function testConnectionStrings() + { + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), 6379); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'], 6380); + $this->assertEquals($this->credis->getPort(), 6380); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'], null, null, "abc123"); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); + } + + public function testConnectionStringsTls() + { + foreach (['ssl','tls','tlsv1.0','tlsv1.1','tlsv1.2'] as $prefix) { + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']); + $this->assertEquals($this->credis->getHost(), $this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), $this->redisConfig[0]['port']); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host']); + $this->assertEquals($this->credis->getPort(), 6379); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123'); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'], 6380); + $this->assertEquals($this->credis->getPort(), 6380); + $this->assertTrue($this->credis->isTls()); + $this->credis = new Credis_Client($prefix.'://'.$this->redisConfig[0]['host'], null, null, "abc123"); + $this->assertEquals($this->credis->getPersistence(), 'abc123'); + $this->assertTrue($this->credis->isTls()); + } + } + + public function testTLSConnection() + { + /** + https://www.php.net/manual/en/context.ssl.php + > Oddly, if "tls://" is set below, then TLSv1 is forced, but using "ssl://" allows TLSv1.2 + + and + >Recommended use "ssl://" transport. + > in php 5.5 ~ 7.1 + > ssl:// transport = ssl_v2|ssl_v3|tls_v1.0|tls_v1.1|tls_v1.2 + > tls:// transport = tls_v1.0 + > after 7.2 ssl:// and tls:// transports is same + > php 7.2 ~ 7.3 = tls_v1.0|tls_v1.1|tls_v1.2 + > php 7.4 ~ 8.1 = tls_v1.0|tls_v1.1|tls_v1.2|tls_v1.3 + */ + + $this->credis = new Credis_Client('ssl://'.$this->redisConfig[8]['host'] . ':' . $this->redisConfig[8]['port']); + $this->credis->setTlsOptions((array)$this->redisConfig[8]['ssl']); + $this->credis->connect(); + $this->assertTrue(true); + } + + /** + * @group UnixSocket + */ + public function testConnectionStringsSocket() + { + $this->credis = new Credis_Client(realpath(__DIR__).'/redis.sock'); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->credis->connect(); + $this->assertTrue($this->credis->isConnected()); + $this->credis->set('key', 'value'); + $this->assertEquals('value', $this->credis->get('key')); + } + + public function testInvalidTcpConnectionString() + { + $this->credis->close(); + $this->setExpectedExceptionShim('CredisException', 'Invalid host format; expected tcp://host[:port][/persistence_identifier]'); + $this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':abc'); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + } + + public function testInvalidTlsConnectionString() + { + $this->credis->close(); + $this->setExpectedExceptionShim('CredisException', 'Invalid host format; expected tls://host[:port][/persistence_identifier]'); + $this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':abc'); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + } + + /** + * @group UnixSocket + */ + public function testInvalidUnixSocketConnectionString() + { + $this->credis->close(); + $this->setExpectedExceptionShim('CredisException', 'Invalid unix socket format; expected unix:///path/to/redis.sock'); + $this->credis = new Credis_Client('unix://path/to/redis.sock'); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + } + + public function testForceStandAloneAfterEstablishedConnection() + { + $this->credis->connect(); + if (! $this->useStandalone) { + $this->setExpectedExceptionShim('CredisException', 'Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.'); + } + $this->credis->forceStandalone(); + $this->assertTrue(true); + } + public function testHscan() + { + $this->credis->hmset('hash', array('name' => 'Jack','age' =>33)); + $iterator = null; + $result = $this->credis->hscan($iterator, 'hash', 'n*', 10); + $this->assertEquals($iterator, 0); + $this->assertEquals($result, ['name'=>'Jack']); + } public function testHscanEmptyIterator() { @@ -953,13 +953,13 @@ public function testscanEmptyIterator() $this->assertEquals($result, false); } - public function testPing() - { - $pong = $this->credis->ping(); - $this->assertEquals("PONG", $pong); - if (version_compare(phpversion('redis'), '5.0.0', '>=')) { - $pong = $this->credis->ping("test"); - $this->assertEquals("test", $pong); - } - } + public function testPing() + { + $pong = $this->credis->ping(); + $this->assertEquals("PONG", $pong); + if (version_compare(phpversion('redis'), '5.0.0', '>=')) { + $pong = $this->credis->ping("test"); + $this->assertEquals("test", $pong); + } + } } From 5641140e14a9679f5a6f66c97268727f9558b881 Mon Sep 17 00:00:00 2001 From: Colin Mollenhour Date: Thu, 26 Oct 2023 13:02:51 -0400 Subject: [PATCH 45/50] Fix PHP CS Fixer errors --- tests/CredisStandaloneTest.php | 2 +- tests/CredisTest.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/CredisStandaloneTest.php b/tests/CredisStandaloneTest.php index 4bad9a9..ea1ea35 100644 --- a/tests/CredisStandaloneTest.php +++ b/tests/CredisStandaloneTest.php @@ -23,7 +23,7 @@ public function testPersistentvsNonPersistent() public function testStandAloneArgumentsExtra() { $this->assertTrue($this->credis->hMSet('hash', array('field1' => 'value1', 'field2' => 'value2'), 'field3', 'value3')); - $this->assertEquals(array('field1' => 'value1', 'field2' => 'value2', 'field3' =>'value3'), $this->credis->hMGet('hash', array('field1','field2','field3'))); + $this->assertEquals(array('field1' => 'value1', 'field2' => 'value2', 'field3' => 'value3'), $this->credis->hMGet('hash', array('field1','field2','field3'))); } public function testStandAloneMultiPipelineThrowsException() diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 7e81dc4..a9f4dac 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -868,11 +868,11 @@ public function testForceStandAloneAfterEstablishedConnection() } public function testHscan() { - $this->credis->hmset('hash', array('name' => 'Jack','age' =>33)); + $this->credis->hmset('hash', array('name' => 'Jack','age' => 33)); $iterator = null; $result = $this->credis->hscan($iterator, 'hash', 'n*', 10); $this->assertEquals($iterator, 0); - $this->assertEquals($result, ['name'=>'Jack']); + $this->assertEquals($result, ['name' => 'Jack']); } public function testHscanEmptyIterator() @@ -891,7 +891,7 @@ public function testSscan() $iterator = null; $result = $this->credis->sscan($iterator, 'set', 'n*', 10); $this->assertEquals($iterator, 0); - $this->assertEquals($result, [0=>'name']); + $this->assertEquals($result, [0 => 'name']); } public function testSscanEmptyIterator() @@ -910,7 +910,7 @@ public function testZscan() $iterator = null; $result = $this->credis->zscan($iterator, 'sortedset', 'n*', 10); $this->assertEquals($iterator, 0); - $this->assertEquals($result, ['name'=>'0']); + $this->assertEquals($result, ['name' => '0']); } public function testZscanEmptyIterator() From f448b209951fadf47bf56e8794e32dc1102aa270 Mon Sep 17 00:00:00 2001 From: Xon Date: Tue, 31 Oct 2023 04:40:03 +0800 Subject: [PATCH 46/50] Add 'discard' function type-hint (uses in multi/exec) & adjust a few return type hints (#183) * Add 'discard' function type-hint (uses in multi/exec) * Adjust function type-hints to be more accurate --------- Co-authored-by: Xon <635541+Xon@users.noreply.github.com> --- Client.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Client.php b/Client.php index 0107191..227eab2 100755 --- a/Client.php +++ b/Client.php @@ -44,11 +44,12 @@ public function __construct($message, $code = 0, $exception = null) * Server/Connection: * @method Credis_Client pipeline() * @method Credis_Client multi() - * @method Credis_Client watch(string ...$keys) - * @method Credis_Client unwatch() + * @method Credis_Client|bool watch(string ...$keys) + * @method Credis_Client|bool unwatch() * @method array exec() - * @method string|Credis_Client flushAll() - * @method string|Credis_Client flushDb() + * @method bool discard() + * @method Credis_Client|bool flushAll() + * @method Credis_Client|bool flushDb() * @method array|Credis_Client info(string $section = null) * @method bool|array|Credis_Client config(string $setGet, string $key, string $value = null) * @method array|Credis_Client role() @@ -162,8 +163,8 @@ public function __construct($message, $code = 0, $exception = null) * * Scripting: * @method string|int|Credis_Client script(string $command, string $arg1 = null) - * @method string|int|array|bool|Credis_Client eval(string $script, array $keys = null, array $args = null) - * @method string|int|array|bool|Credis_Client evalSha(string $script, array $keys = null, array $args = null) + * @method string|int|array|bool|Credis_Client eval(string $script, array|string $keys = null, array|string $args = null) + * @method string|int|array|bool|Credis_Client evalSha(string $script, array|string $keys = null, array|string $args = null) */ class Credis_Client { From 40bf00ead1526b71525aa2a0b6261b42509651e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 2 Mar 2024 22:37:53 +0100 Subject: [PATCH 47/50] Fix a typo (#185) --- testenv/env/php-7.4/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testenv/env/php-7.4/Dockerfile b/testenv/env/php-7.4/Dockerfile index 5de537e..ff29e36 100644 --- a/testenv/env/php-7.4/Dockerfile +++ b/testenv/env/php-7.4/Dockerfile @@ -1,13 +1,13 @@ FROM php:7.4 -ENV phpunit_verison 7.5 +ENV phpunit_version 7.5 ENV redis_version 6.0.8 RUN apt-get update && \ apt-get install -y wget libssl-dev -RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \ - chmod +x phpunit-${phpunit_verison}.phar && \ - mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit +RUN wget https://phar.phpunit.de/phpunit-${phpunit_version}.phar && \ + chmod +x phpunit-${phpunit_version}.phar && \ + mv phpunit-${phpunit_version}.phar /usr/local/bin/phpunit # install php extension RUN yes '' | pecl install -f redis && \ From ba53f7eeeea8d20e336e5dc6f2138105279484f0 Mon Sep 17 00:00:00 2001 From: Xon Date: Wed, 3 Apr 2024 01:33:58 +0800 Subject: [PATCH 48/50] Tests for php 8.3 and php 8.4 (#186) * Tests for php 8.3 and php 8.4 * php 8.4 will no longer supports implicit null ie `array $foo = null`. To maintain compatibility with php 7.1 and earlier, remove the type argument --------- Co-authored-by: Xon <635541+Xon@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++++ Client.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1912d4a..25e51d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,10 @@ jobs: phpunit: '^9' - php-version: '8.2' phpunit: '^9' + - php-version: '8.3' + phpunit: '^9' + - php-version: '8.4' + phpunit: '^9' name: PHP ${{ matrix.php-version }} steps: - name: Checkout diff --git a/Client.php b/Client.php index 227eab2..ffb336b 100755 --- a/Client.php +++ b/Client.php @@ -346,7 +346,7 @@ public function getSslMeta() * @param array|null $tlsOptions The TLS/SSL context options. See https://www.php.net/manual/en/context.ssl.php for details * @throws CredisException */ - public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null, array $tlsOptions = null) + public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null, $tlsOptions = null) { $this->host = (string)$host; if ($port !== null) { @@ -360,7 +360,7 @@ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $this->authUsername = $username; $this->selectedDb = (int)$db; $this->convertHost(); - if ($tlsOptions) { + if (is_array($tlsOptions) && count($tlsOptions) !== 0) { $this->setTlsOptions($tlsOptions); } // PHP Redis extension support TLS/ACL AUTH since 5.3.0 From f11a89fd068d3e5db0c2b5a9ba8663bc36162e95 Mon Sep 17 00:00:00 2001 From: Michael Mussulis Date: Thu, 4 Jul 2024 11:08:03 -0400 Subject: [PATCH 49/50] Fix for warning Trying to access array offset on value of type bool (#188) Fix for warning Trying to access array offset on value of type bool --------- Co-authored-by: Michael Mussulis --- Client.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Client.php b/Client.php index ffb336b..43ae03c 100755 --- a/Client.php +++ b/Client.php @@ -1152,10 +1152,12 @@ public function __call($name, $args) if ($this->isMulti) { $execResponse = array_pop($response); - foreach ($queuedResponses as $key => $command) { - list($name, $arguments) = $command; - $response[] = $this->decode_reply($name, $execResponse[$key], $arguments); - } + if(!empty($execResponse)) { + foreach ($queuedResponses as $key => $command) { + list($name, $arguments) = $command; + $response[] = $this->decode_reply($name, $execResponse[$key], $arguments); + } + } } } catch (CredisException $e) { // the connection on redis's side is likely in a bad state, force it closed to abort the pipeline/transaction From e79bf7a8a3f5540cad7c27e7586c99628a92a071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 3 Aug 2024 00:52:47 +0200 Subject: [PATCH 50/50] Fix CS in Client (#189) --- Client.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client.php b/Client.php index 43ae03c..e61c501 100755 --- a/Client.php +++ b/Client.php @@ -1152,12 +1152,12 @@ public function __call($name, $args) if ($this->isMulti) { $execResponse = array_pop($response); - if(!empty($execResponse)) { + if (!empty($execResponse)) { foreach ($queuedResponses as $key => $command) { list($name, $arguments) = $command; $response[] = $this->decode_reply($name, $execResponse[$key], $arguments); } - } + } } } catch (CredisException $e) { // the connection on redis's side is likely in a bad state, force it closed to abort the pipeline/transaction