Add support for the 'redis://' scheme in URI strings.

The URI string will be handled following the rules as described by the
the provisional IANA registration document that can be found on IANA's
website: http://www.iana.org/assignments/uri-schemes/prov/redis.
This commit is contained in:
Daniele Alessandri
2015-07-23 17:27:58 +02:00
parent 474f3ddbe7
commit 35f135be30
14 changed files with 308 additions and 38 deletions
+3
View File
@@ -95,6 +95,9 @@ $client = new Predis\Client([
$client = new Predis\Client('tcp://10.0.0.1:6379');
```
Starting with Predis v1.0.2 the client also understands the `redis` scheme in URI strings as defined
by the [provisional IANA registration](http://www.iana.org/assignments/uri-schemes/prov/redis).
When an array of connection parameters is provided, Predis automatically works in cluster mode using
client-side sharding. Both named arrays and URI strings can be mixed when providing configurations
for each node:
+7 -7
View File
@@ -58,14 +58,14 @@ abstract class AbstractConnection implements NodeConnectionInterface
*/
protected function assertParameters(ParametersInterface $parameters)
{
$scheme = $parameters->scheme;
switch ($parameters->scheme) {
case 'tcp':
case 'redis':
case 'unix':
break;
if ($scheme !== 'tcp' && $scheme !== 'unix') {
throw new InvalidArgumentException("Invalid scheme: '$scheme'.");
}
if ($scheme === 'unix' && !isset($parameters->path)) {
throw new InvalidArgumentException('Missing UNIX domain socket path.');
default:
throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
}
return $parameters;
+4 -3
View File
@@ -24,9 +24,10 @@ use Predis\Command\RawCommand;
class Factory implements FactoryInterface
{
protected $schemes = array(
'tcp' => 'Predis\Connection\StreamConnection',
'unix' => 'Predis\Connection\StreamConnection',
'http' => 'Predis\Connection\WebdisConnection',
'tcp' => 'Predis\Connection\StreamConnection',
'unix' => 'Predis\Connection\StreamConnection',
'redis' => 'Predis\Connection\StreamConnection',
'http' => 'Predis\Connection\WebdisConnection',
);
/**
+48 -1
View File
@@ -77,12 +77,16 @@ class Parameters implements ParametersInterface
*/
public static function parse($uri)
{
if (stripos($uri, 'redis') === 0) {
return static::parseIANA($uri);
}
if (stripos($uri, 'unix') === 0) {
// Hack to support URIs for UNIX sockets with minimal effort.
$uri = str_ireplace('unix:///', 'unix://localhost/', $uri);
}
if (!($parsed = parse_url($uri)) || !isset($parsed['host'])) {
if (!$parsed = parse_url($uri)) {
throw new InvalidArgumentException("Invalid parameters URI: $uri");
}
@@ -96,6 +100,49 @@ class Parameters implements ParametersInterface
return $parsed;
}
/**
* Parses an URI string as defined by the "redis" and "rediss" URL schemes
* registered with the IANA.
*
* When the URI has a password in the "user-information" part or a database
* number the "path" part, their values override the values of "password"
* and "database" in the "query" part of the same URI.
*
* @link http://www.iana.org/assignments/uri-schemes/prov/redis
* @link http://www.iana.org/assignments/uri-schemes/prov/redis
*
* @param string $uri URI string.
*
* @return array
*
* @throws \InvalidArgumentException
*/
public static function parseIANA($uri)
{
if (!$parsed = parse_url($uri)) {
throw new InvalidArgumentException("Invalid parameters URI: $uri");
}
if (isset($parsed['query'])) {
parse_str($parsed['query'], $queryarray);
unset($parsed['query']);
$parsed = array_merge($parsed, $queryarray);
}
if (isset($parsed['pass'])) {
$parsed['password'] = $parsed['pass'];
unset($parsed['pass']);
}
if (isset($parsed['path']) && preg_match('/^\/(\d+)\/?/', $parsed['path'], $path)) {
$parsed['database'] = $path[1];
unset($parsed['path']);
}
return $parsed;
}
/**
* Validates and converts each value of the connection parameters array.
*
+4 -2
View File
@@ -32,7 +32,7 @@ use Predis\Response\Status as StatusResponse;
*
* The connection parameters supported by this class are:
*
* - scheme: it can be either 'tcp' or 'unix'.
* - scheme: it can be either 'redis', 'tcp' or 'unix'.
* - host: hostname or IP address of the server.
* - port: TCP port of the server.
* - path: path of a UNIX domain socket when scheme is 'unix'.
@@ -92,13 +92,15 @@ class PhpiredisSocketConnection extends AbstractConnection
*/
protected function assertParameters(ParametersInterface $parameters)
{
parent::assertParameters($parameters);
if (isset($parameters->persistent)) {
throw new NotSupportedException(
"Persistent connections are not supported by this connection backend."
);
}
return parent::assertParameters($parameters);
return $parameters;
}
/**
+1 -1
View File
@@ -32,7 +32,7 @@ use Predis\Response\Status as StatusResponse;
*
* The connection parameters supported by this class are:
*
* - scheme: it can be either 'tcp' or 'unix'.
* - scheme: it can be either 'redis', 'tcp' or 'unix'.
* - host: hostname or IP address of the server.
* - port: TCP port of the server.
* - path: path of a UNIX domain socket when scheme is 'unix'.
+15 -4
View File
@@ -19,7 +19,7 @@ use Predis\Response\Status as StatusResponse;
* Standard connection to Redis servers implemented on top of PHP's streams.
* The connection parameters supported by this class are:
*
* - scheme: it can be either 'tcp' or 'unix'.
* - scheme: it can be either 'redis', 'tcp' or 'unix'.
* - host: hostname or IP address of the server.
* - port: TCP port of the server.
* - path: path of a UNIX domain socket when scheme is 'unix'.
@@ -52,10 +52,17 @@ class StreamConnection extends AbstractConnection
*/
protected function createResource()
{
$initializer = "{$this->parameters->scheme}StreamInitializer";
$resource = $this->$initializer($this->parameters);
switch ($this->parameters->scheme) {
case 'tcp':
case 'redis':
return $this->tcpStreamInitializer($this->parameters);
return $resource;
case 'unix':
return $this->unixStreamInitializer($this->parameters);
default:
throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'.");
}
}
/**
@@ -110,6 +117,10 @@ class StreamConnection extends AbstractConnection
*/
protected function unixStreamInitializer(ParametersInterface $parameters)
{
if ($scheme === 'unix' && !isset($parameters->path)) {
throw new InvalidArgumentException('Missing UNIX domain socket path.');
}
$uri = "unix://{$parameters->path}";
$flags = STREAM_CLIENT_CONNECT;
@@ -29,12 +29,34 @@ class CompositeStreamConnectionTest extends PredisConnectionTestCase
/**
* @group disconnected
*/
public function testExposesParameters()
public function testSupportsSchemeTCP()
{
$parameters = $this->getParameters();
$connection = new CompositeStreamConnection($parameters);
$parameters = $this->getParameters(array('scheme' => 'tcp'));
$connection = new StreamConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeRedis()
{
$parameters = $this->getParameters(array('scheme' => 'redis'));
$connection = new StreamConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeUnix()
{
$parameters = $this->getParameters(array('scheme' => 'unix'));
$connection = new StreamConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
@@ -48,6 +70,17 @@ class CompositeStreamConnectionTest extends PredisConnectionTestCase
new CompositeStreamConnection($parameters);
}
/**
* @group disconnected
*/
public function testExposesParameters()
{
$parameters = $this->getParameters();
$connection = new CompositeStreamConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
}
/**
* @group disconnected
*/
+17 -5
View File
@@ -47,6 +47,18 @@ class FactoryTest extends PredisTestCase
$this->assertEquals($tcp->host, $parameters->host);
$this->assertEquals($tcp->database, $parameters->database);
$tcp = new Parameters(array(
'scheme' => 'redis',
'host' => 'locahost',
));
$connection = $factory->create($tcp);
$parameters = $connection->getParameters();
$this->assertInstanceOf('Predis\Connection\StreamConnection', $connection);
$this->assertEquals($tcp->scheme, $parameters->scheme);
$this->assertEquals($tcp->host, $parameters->host);
$this->assertEquals($tcp->database, $parameters->database);
$unix = new Parameters(array(
'scheme' => 'unix',
'path' => '/tmp/redis.sock',
@@ -246,7 +258,7 @@ class FactoryTest extends PredisTestCase
/**
* @group disconnected
* @expectedException \InvalidArgumentException
* @expecteExceptionMessage Unknown connection scheme: 'redis'.
* @expecteExceptionMessage Unknown connection scheme: 'test'.
*/
public function testDefineAndUndefineConnection()
{
@@ -254,11 +266,11 @@ class FactoryTest extends PredisTestCase
$factory = new Factory();
$factory->define('redis', $connectionClass);
$this->assertInstanceOf($connectionClass, $factory->create('redis://127.0.0.1'));
$factory->define('test', $connectionClass);
$this->assertInstanceOf($connectionClass, $factory->create('test://127.0.0.1'));
$factory->undefine('redis');
$factory->create('redis://127.0.0.1');
$factory->undefine('test');
$factory->create('test://127.0.0.1');
}
/**
+42 -2
View File
@@ -137,7 +137,7 @@ class ParametersTest extends PredisTestCase
*/
public function testParsingURI()
{
$uri = 'tcp://10.10.10.10:6400?timeout=0.5&persistent=1';
$uri = 'tcp://10.10.10.10:6400?timeout=0.5&persistent=1&database=5&password=secret';
$expected = array(
'scheme' => 'tcp',
@@ -145,6 +145,8 @@ class ParametersTest extends PredisTestCase
'port' => 6400,
'timeout' => '0.5',
'persistent' => '1',
'database' => '5',
'password' => 'secret',
);
$this->assertSame($expected, Parameters::parse($uri));
@@ -153,7 +155,45 @@ class ParametersTest extends PredisTestCase
/**
* @group disconnected
*/
public function testParsingUnixDomainURI()
public function testParsingURIWithRedisScheme()
{
$uri = 'redis://:secret@10.10.10.10:6400/5?timeout=0.5&persistent=1';
$expected = array(
'scheme' => 'redis',
'host' => '10.10.10.10',
'port' => 6400,
'timeout' => '0.5',
'persistent' => '1',
'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 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
*/
public function testParsingURIWithUnixDomain()
{
$uri = 'unix:///tmp/redis.sock?timeout=0.5&persistent=1';
@@ -29,12 +29,34 @@ class PhpiredisSocketConnectionTest extends PredisConnectionTestCase
/**
* @group disconnected
*/
public function testExposesParameters()
public function testSupportsSchemeTCP()
{
$parameters = $this->getParameters();
$parameters = $this->getParameters(array('scheme' => 'tcp'));
$connection = new PhpiredisSocketConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeRedis()
{
$parameters = $this->getParameters(array('scheme' => 'redis'));
$connection = new PhpiredisSocketConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeUnix()
{
$parameters = $this->getParameters(array('scheme' => 'unix'));
$connection = new PhpiredisSocketConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
@@ -48,6 +70,17 @@ class PhpiredisSocketConnectionTest extends PredisConnectionTestCase
new PhpiredisSocketConnection($parameters);
}
/**
* @group disconnected
*/
public function testExposesParameters()
{
$parameters = $this->getParameters();
$connection = new PhpiredisSocketConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
}
/**
* @group disconnected
*/
@@ -29,12 +29,34 @@ class PhpiredisStreamConnectionTest extends PredisConnectionTestCase
/**
* @group disconnected
*/
public function testExposesParameters()
public function testSupportsSchemeTCP()
{
$parameters = $this->getParameters();
$parameters = $this->getParameters(array('scheme' => 'tcp'));
$connection = new PhpiredisStreamConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeRedis()
{
$parameters = $this->getParameters(array('scheme' => 'redis'));
$connection = new PhpiredisStreamConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeUnix()
{
$parameters = $this->getParameters(array('scheme' => 'unix'));
$connection = new PhpiredisStreamConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
@@ -48,6 +70,17 @@ class PhpiredisStreamConnectionTest extends PredisConnectionTestCase
new PhpiredisStreamConnection($parameters);
}
/**
* @group disconnected
*/
public function testExposesParameters()
{
$parameters = $this->getParameters();
$connection = new PhpiredisStreamConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
}
/**
* @group disconnected
*/
@@ -29,12 +29,34 @@ class StreamConnectionTest extends PredisConnectionTestCase
/**
* @group disconnected
*/
public function testExposesParameters()
public function testSupportsSchemeTCP()
{
$parameters = $this->getParameters();
$parameters = $this->getParameters(array('scheme' => 'tcp'));
$connection = new StreamConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeRedis()
{
$parameters = $this->getParameters(array('scheme' => 'redis'));
$connection = new StreamConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
*/
public function testSupportsSchemeUnix()
{
$parameters = $this->getParameters(array('scheme' => 'unix'));
$connection = new StreamConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
@@ -48,6 +70,17 @@ class StreamConnectionTest extends PredisConnectionTestCase
new StreamConnection($parameters);
}
/**
* @group disconnected
*/
public function testExposesParameters()
{
$parameters = $this->getParameters();
$connection = new StreamConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
}
/**
* @group disconnected
*/
@@ -32,6 +32,28 @@ class WebdisConnectionTest extends PredisTestCase
$this->assertTrue($connection->isConnected());
}
/**
* @group disconnected
*/
public function testSupportsSchemeUnix()
{
$parameters = $this->getParameters(array('scheme' => 'http'));
$connection = new WebdisConnection($parameters);
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
}
/**
* @group disconnected
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Invalid scheme: 'tcp'.
*/
public function testThrowsExceptionOnInvalidScheme()
{
$parameters = $this->getParameters(array('scheme' => 'tcp'));
new WebdisConnection($parameters);
}
/**
* @group disconnected
* @expectedException \Predis\NotSupportedException