mirror of
https://github.com/predis/predis.git
synced 2026-08-19 14:23:13 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ca99f91e4 | |||
| c778843b0d | |||
| dca201291c | |||
| 2e76410e65 | |||
| ca468b785c | |||
| 4a7fb55058 | |||
| fdf663ebba | |||
| 880ad09cad | |||
| 5eccc65d42 | |||
| f225c55a01 |
@@ -1,3 +1,32 @@
|
||||
v1.1.5 (2020-09-10)
|
||||
================================================================================
|
||||
|
||||
- __FIX__: authentication for sentinels is now supported, previously it was not
|
||||
possible to specify a `password` for sentinels as its value was stripped during
|
||||
initialization because sentinels did not support authentication until Redis 5.
|
||||
**Please note** that with the current implementation each sentinel must have
|
||||
its own `password` parameter set in the parameters list despite this password is
|
||||
the same for all sentinels (read how `requirepass` works on the Redis docs). In
|
||||
this case you should avoid using the global `parameters` client option used to
|
||||
set default parameters for every connection created by Predis as this would end
|
||||
up using the same password even when connecting to actual Redis nodes.
|
||||
|
||||
- __FIX__: the username is now correctly retrieved from the userinfo fragment of
|
||||
the URI when using the "redis" scheme and a "username:password" pair is present.
|
||||
Values retrieved from the userinfo fragment always override the ones specified
|
||||
in `username` and `password` if those fields are present in the query string.
|
||||
|
||||
- __FIX__: `Predis\Connection\WebdisConnection` was unable to connect to Webdis
|
||||
when using an IPv4 address in the URL and this is probably due to some change in
|
||||
cURL internals since the last time we tested it.
|
||||
|
||||
- __FIX__: an exception is thrown whe passing `FALSE` or any value evaluating to
|
||||
`FALSE` to the `replication` client option. This was supposed to be unsupported,
|
||||
in fact it actually breaks client initialization and raises a PHP warning. Now
|
||||
the user is alerted with an `InvalidArgumentException` and a proper message.
|
||||
(PR #381).
|
||||
|
||||
|
||||
v1.1.4 (2020-08-31)
|
||||
================================================================================
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ name = "Predis"
|
||||
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
|
||||
homepage = "http://github.com/nrk/predis"
|
||||
license = "MIT"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
stability = "stable"
|
||||
channel = "pear.nrk.io"
|
||||
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
|
||||
*/
|
||||
class Client implements ClientInterface, \IteratorAggregate
|
||||
{
|
||||
const VERSION = '1.1.4';
|
||||
const VERSION = '1.1.5';
|
||||
|
||||
protected $connection;
|
||||
protected $options;
|
||||
|
||||
@@ -118,7 +118,7 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this zremrangebyrank($key, $start, $stop)
|
||||
* @method $this zremrangebyscore($key, $min, $max)
|
||||
* @method $this zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrevrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
|
||||
* @method $this zrevrank($key, $member)
|
||||
* @method $this zunionstore($destination, array|string $keys, array $options = null)
|
||||
* @method $this zscore($key, $member)
|
||||
|
||||
@@ -36,10 +36,6 @@ class ReplicationOption implements OptionInterface
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_bool($value) || $value === null) {
|
||||
return $value ? $this->getDefault($options) : null;
|
||||
}
|
||||
|
||||
if ($value === 'sentinel') {
|
||||
return function ($sentinels, $options) {
|
||||
return new SentinelReplication($options->service, $sentinels, $options->connections);
|
||||
@@ -50,7 +46,13 @@ class ReplicationOption implements OptionInterface
|
||||
!is_object($value) &&
|
||||
null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
|
||||
) {
|
||||
return $asbool ? $this->getDefault($options) : null;
|
||||
if (true === $asbool) {
|
||||
return $this->getDefault($options);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(
|
||||
"Values evaluating to FALSE are not accepted for `replication`"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(
|
||||
|
||||
@@ -239,10 +239,12 @@ class SentinelReplication implements ReplicationInterface
|
||||
}
|
||||
|
||||
if (is_array($parameters)) {
|
||||
// We explicitly set "database" and "password" to null,
|
||||
// so that no AUTH and SELECT command is send to the sentinels.
|
||||
// Password authentication is fine now that Redis Sentinel supports
|
||||
// password-protected sentinel instances, but we must explicitly set
|
||||
// "database" and "username" to NULL so that no augmented AUTH (ACL)
|
||||
// and SELECT command are sent by accident to the sentinels.
|
||||
$parameters['database'] = null;
|
||||
$parameters['password'] = null;
|
||||
$parameters['username'] = null;
|
||||
|
||||
if (!isset($parameters['timeout'])) {
|
||||
$parameters['timeout'] = $this->sentinelTimeout;
|
||||
|
||||
@@ -109,8 +109,17 @@ class Parameters implements ParametersInterface
|
||||
}
|
||||
|
||||
if (stripos($uri, 'redis') === 0) {
|
||||
if (isset($parsed['user'])) {
|
||||
if (strlen($parsed['user'])) {
|
||||
$parsed['username'] = $parsed['user'];
|
||||
}
|
||||
unset($parsed['user']);
|
||||
}
|
||||
|
||||
if (isset($parsed['pass'])) {
|
||||
$parsed['password'] = $parsed['pass'];
|
||||
if (strlen($parsed['pass'])) {
|
||||
$parsed['password'] = $parsed['pass'];
|
||||
}
|
||||
unset($parsed['pass']);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,9 +87,23 @@ class PhpiredisStreamConnection extends StreamConnection
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertSslSupport(ParametersInterface $parameters)
|
||||
protected function assertParameters(ParametersInterface $parameters)
|
||||
{
|
||||
throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.');
|
||||
switch ($parameters->scheme) {
|
||||
case 'tcp':
|
||||
case 'redis':
|
||||
case 'unix':
|
||||
break;
|
||||
|
||||
case 'tls':
|
||||
case 'rediss':
|
||||
throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.');
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -119,7 +119,7 @@ class WebdisConnection implements NodeConnectionInterface
|
||||
$parameters = $this->getParameters();
|
||||
$timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000;
|
||||
|
||||
if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) {
|
||||
if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$host = "[$host]";
|
||||
}
|
||||
|
||||
|
||||
@@ -337,4 +337,16 @@ abstract class PredisTestCase extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->checkRequiredRedisServerVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks current test skipped when test suite is running on CI environments.
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
protected function markTestSkippedOnCIEnvironment($message = 'Test skipped on CI environment')
|
||||
{
|
||||
if (getenv('GITHUB_ACTIONS') || getenv('TRAVIS')) {
|
||||
$this->markTestSkipped($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,26 +31,45 @@ class ReplicationOptionTest extends PredisTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @return array
|
||||
*/
|
||||
public function testAcceptsValuesThatCanBeInterpretedAsBooleans()
|
||||
public function provideValuesEvaluatingTrue()
|
||||
{
|
||||
return array(array(true), array(1), array('true'), array('on'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider provideValuesEvaluatingTrue
|
||||
*/
|
||||
public function testAcceptsValuesThatCanBeInterpretedAsBooleanTrue($value)
|
||||
{
|
||||
$option = new ReplicationOption();
|
||||
$options = $this->getMock('Predis\Configuration\OptionsInterface');
|
||||
|
||||
$this->assertNull($option->filter($options, null));
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\MasterSlaveReplication', $option->filter($options, $value));
|
||||
}
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, true));
|
||||
$this->assertNull($option->filter($options, false));
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function provideValuesEvaluatingFalse()
|
||||
{
|
||||
return array(array(false), array(0), array('false'), array('off'));
|
||||
}
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, 1));
|
||||
$this->assertNull($option->filter($options, 0));
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider provideValuesEvaluatingFalse
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Values evaluating to FALSE are not accepted for `replication`
|
||||
*/
|
||||
public function testDoesNotAcceptValuesThatCanBeInterpretedAsBooleanFalse($value)
|
||||
{
|
||||
$option = new ReplicationOption();
|
||||
$options = $this->getMock('Predis\Configuration\OptionsInterface');
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, 'true'));
|
||||
$this->assertNull($option->filter($options, 'false'));
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, 'on'));
|
||||
$this->assertNull($option->filter($options, 'off'));
|
||||
$option->filter($options, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,15 +36,29 @@ class SentinelReplicationTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParametersForSentinelConnectionShouldNotUseDatabaseAndPassword()
|
||||
public function testParametersForSentinelConnectionShouldUsePasswordForAuthentication()
|
||||
{
|
||||
$replication = $this->getReplicationConnection('svc', array(
|
||||
'tcp://127.0.0.1:5381?alias=sentinel1&database=1&password=secret',
|
||||
'tcp://127.0.0.1:5381?alias=sentinel1&password=secret',
|
||||
));
|
||||
|
||||
$parameters = $replication->getSentinelConnection()->getParameters()->toArray();
|
||||
|
||||
$this->assertArraySubset(array('database' => null, 'password' => null), $parameters);
|
||||
$this->assertArraySubset(array('password' => 'secret'), $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParametersForSentinelConnectionShouldNotUseDatabaseAndUsername()
|
||||
{
|
||||
$replication = $this->getReplicationConnection('svc', array(
|
||||
'tcp://127.0.0.1:5381?alias=sentinel1&database=1&username=myusername',
|
||||
));
|
||||
|
||||
$parameters = $replication->getSentinelConnection()->getParameters()->toArray();
|
||||
|
||||
$this->assertArraySubset(array('database' => null, 'username' => null), $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -156,7 +156,7 @@ class ParametersTest extends PredisTestCase
|
||||
*/
|
||||
public function testParsingURIWithRedisScheme()
|
||||
{
|
||||
$uri = 'redis://:secret@10.10.10.10:6400/5?timeout=0.5&persistent=1';
|
||||
$uri = 'redis://predis:secret@10.10.10.10:6400/5?timeout=0.5&persistent=1';
|
||||
|
||||
$expected = array(
|
||||
'scheme' => 'redis',
|
||||
@@ -164,20 +164,48 @@ class ParametersTest extends PredisTestCase
|
||||
'port' => 6400,
|
||||
'timeout' => '0.5',
|
||||
'persistent' => '1',
|
||||
'username' => 'predis',
|
||||
'password' => 'secret',
|
||||
'database' => '5',
|
||||
);
|
||||
|
||||
$parameters = Parameters::parse($uri);
|
||||
|
||||
// TODO: parse_url() in PHP >= 5.6 returns an empty "user" entry in the
|
||||
// dictionary when no username has been provided in the URI string. This
|
||||
// actually makes sense, but let's keep the test ugly & simple for now.
|
||||
unset($parameters['user']);
|
||||
|
||||
$this->assertSame($expected, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeOverridesUsernameAndPasswordInQueryString()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://predis:secret@10.10.10.10/5?username=ignored&password=ignored');
|
||||
|
||||
$this->assertSame('predis', $parameters['username']);
|
||||
$this->assertSame('secret', $parameters['password']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeDoesNotOverridesUsernameAndPasswordInQueryStringOnEmptyAuthFragment()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://:@10.10.10.10/5?username=predis&password=secret');
|
||||
|
||||
$this->assertSame('predis', $parameters['username']);
|
||||
$this->assertSame('secret', $parameters['password']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeOverridesDatabaseInQueryString()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://10.10.10.10/5?database=10');
|
||||
|
||||
$this->assertSame('5', $parameters['database']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
@@ -197,17 +225,6 @@ class ParametersTest extends PredisTestCase
|
||||
$this->assertSame($expected, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeOverridesPasswordAndDatabaseInQueryString()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://:secret@10.10.10.10/5?password=ignored&database=4');
|
||||
|
||||
$this->assertSame('secret', $parameters['password']);
|
||||
$this->assertSame('5', $parameters['database']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
|
||||
@@ -312,11 +312,9 @@ class ConsumerTest extends PredisTestCase
|
||||
*/
|
||||
public function testPubSubAgainstRedisServer()
|
||||
{
|
||||
if (getenv('GITHUB_ACTIONS') || getenv('TRAVIS')) {
|
||||
$this->markTestSkipped(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test'
|
||||
);
|
||||
}
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
@@ -363,11 +361,9 @@ class ConsumerTest extends PredisTestCase
|
||||
*/
|
||||
public function testPubSubAgainstRedisServerBlocking()
|
||||
{
|
||||
if (getenv('GITHUB_ACTIONS') || getenv('TRAVIS')) {
|
||||
$this->markTestSkipped(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test'
|
||||
);
|
||||
}
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
|
||||
@@ -23,11 +23,25 @@ class DispatcherLoopTest extends PredisTestCase
|
||||
// ---- INTEGRATION TESTS --------------------------------------------- //
|
||||
// ******************************************************************** //
|
||||
|
||||
// NOTE: the following 2 tests fail at random without any apparent reason
|
||||
// when executed on our CI environments and these failures are not tied
|
||||
// to a particular version of PHP or Redis. It is most likely some weird
|
||||
// timing issue on busy systems as it is really rare to get it triggered
|
||||
// locally. The chances it is a bug in the library are pretty low so for
|
||||
// now we just mark this test skipped on our CI environments (but still
|
||||
// enabled for local test runs) and "debug" this issue using a separate
|
||||
// branch to avoid having spurious failures on main development branches
|
||||
// which is utterly annoying.
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
*/
|
||||
public function testDispatcherLoopAgainstRedisServer()
|
||||
{
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
'port' => REDIS_SERVER_PORT,
|
||||
@@ -87,6 +101,10 @@ class DispatcherLoopTest extends PredisTestCase
|
||||
*/
|
||||
public function testDispatcherLoopAgainstRedisServerWithPrefix()
|
||||
{
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
'port' => REDIS_SERVER_PORT,
|
||||
|
||||
Reference in New Issue
Block a user