Pārlūkot izejas kodu

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.
Daniele Alessandri 9 gadi atpakaļ
vecāks
revīzija
baaf26fe5b

+ 3 - 0
README.md

@@ -95,6 +95,9 @@ $client = new Predis\Client([
 $client = new Predis\Client('tcp://10.0.0.1:6379');
 $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
 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
 client-side sharding. Both named arrays and URI strings can be mixed when providing configurations
 for each node:
 for each node:

+ 8 - 8
src/Connection/AbstractConnection.php

@@ -58,14 +58,14 @@ abstract class AbstractConnection implements NodeConnectionInterface
      */
      */
     protected function assertParameters(ParametersInterface $parameters)
     protected function assertParameters(ParametersInterface $parameters)
     {
     {
-        $scheme = $parameters->scheme;
-
-        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.');
+        switch ($parameters->scheme) {
+            case 'tcp':
+            case 'redis':
+            case 'unix':
+                break;
+
+            default:
+                throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
         }
         }
 
 
         return $parameters;
         return $parameters;

+ 4 - 3
src/Connection/Factory.php

@@ -24,9 +24,10 @@ use Predis\Command\RawCommand;
 class Factory implements FactoryInterface
 class Factory implements FactoryInterface
 {
 {
     protected $schemes = array(
     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
src/Connection/Parameters.php

@@ -77,12 +77,49 @@ class Parameters implements ParametersInterface
      */
      */
     public static function parse($uri)
     public static function parse($uri)
     {
     {
+        if (stripos($uri, 'redis') === 0) {
+            return static::parseIANA($uri);
+        }
+
         if (stripos($uri, 'unix') === 0) {
         if (stripos($uri, 'unix') === 0) {
             // Hack to support URIs for UNIX sockets with minimal effort.
             // Hack to support URIs for UNIX sockets with minimal effort.
             $uri = str_ireplace('unix:///', 'unix://localhost/', $uri);
             $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");
+        }
+
+        if (isset($parsed['query'])) {
+            parse_str($parsed['query'], $queryarray);
+            unset($parsed['query']);
+
+            $parsed = array_merge($parsed, $queryarray);
+        }
+
+        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");
             throw new InvalidArgumentException("Invalid parameters URI: $uri");
         }
         }
 
 
@@ -93,6 +130,16 @@ class Parameters implements ParametersInterface
             $parsed = array_merge($parsed, $queryarray);
             $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;
         return $parsed;
     }
     }
 
 

+ 4 - 2
src/Connection/PhpiredisSocketConnection.php

@@ -32,7 +32,7 @@ use Predis\Response\Status as StatusResponse;
  *
  *
  * The connection parameters supported by this class are:
  * 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.
  *  - host: hostname or IP address of the server.
  *  - port: TCP port of the server.
  *  - port: TCP port of the server.
  *  - path: path of a UNIX domain socket when scheme is 'unix'.
  *  - path: path of a UNIX domain socket when scheme is 'unix'.
@@ -92,13 +92,15 @@ class PhpiredisSocketConnection extends AbstractConnection
      */
      */
     protected function assertParameters(ParametersInterface $parameters)
     protected function assertParameters(ParametersInterface $parameters)
     {
     {
+        parent::assertParameters($parameters);
+
         if (isset($parameters->persistent)) {
         if (isset($parameters->persistent)) {
             throw new NotSupportedException(
             throw new NotSupportedException(
                 "Persistent connections are not supported by this connection backend."
                 "Persistent connections are not supported by this connection backend."
             );
             );
         }
         }
 
 
-        return parent::assertParameters($parameters);
+        return $parameters;
     }
     }
 
 
     /**
     /**

+ 1 - 1
src/Connection/PhpiredisStreamConnection.php

@@ -32,7 +32,7 @@ use Predis\Response\Status as StatusResponse;
  *
  *
  * The connection parameters supported by this class are:
  * 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.
  *  - host: hostname or IP address of the server.
  *  - port: TCP port of the server.
  *  - port: TCP port of the server.
  *  - path: path of a UNIX domain socket when scheme is 'unix'.
  *  - path: path of a UNIX domain socket when scheme is 'unix'.

+ 15 - 4
src/Connection/StreamConnection.php

@@ -19,7 +19,7 @@ use Predis\Response\Status as StatusResponse;
  * Standard connection to Redis servers implemented on top of PHP's streams.
  * Standard connection to Redis servers implemented on top of PHP's streams.
  * The connection parameters supported by this class are:
  * 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.
  *  - host: hostname or IP address of the server.
  *  - port: TCP port of the server.
  *  - port: TCP port of the server.
  *  - path: path of a UNIX domain socket when scheme is 'unix'.
  *  - path: path of a UNIX domain socket when scheme is 'unix'.
@@ -52,10 +52,17 @@ class StreamConnection extends AbstractConnection
      */
      */
     protected function createResource()
     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)
     protected function unixStreamInitializer(ParametersInterface $parameters)
     {
     {
+        if ($scheme === 'unix' && !isset($parameters->path)) {
+            throw new InvalidArgumentException('Missing UNIX domain socket path.');
+        }
+
         $uri = "unix://{$parameters->path}";
         $uri = "unix://{$parameters->path}";
         $flags = STREAM_CLIENT_CONNECT;
         $flags = STREAM_CLIENT_CONNECT;
 
 

+ 37 - 4
tests/Predis/Connection/CompositeStreamConnectionTest.php

@@ -29,12 +29,34 @@ class CompositeStreamConnectionTest extends PredisConnectionTestCase
     /**
     /**
      * @group disconnected
      * @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);
         new CompositeStreamConnection($parameters);
     }
     }
 
 
+    /**
+     * @group disconnected
+     */
+    public function testExposesParameters()
+    {
+        $parameters = $this->getParameters();
+        $connection = new CompositeStreamConnection($parameters);
+
+        $this->assertSame($parameters, $connection->getParameters());
+    }
+
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */

+ 17 - 5
tests/Predis/Connection/FactoryTest.php

@@ -47,6 +47,18 @@ class FactoryTest extends PredisTestCase
         $this->assertEquals($tcp->host, $parameters->host);
         $this->assertEquals($tcp->host, $parameters->host);
         $this->assertEquals($tcp->database, $parameters->database);
         $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(
         $unix = new Parameters(array(
             'scheme' => 'unix',
             'scheme' => 'unix',
             'path' => '/tmp/redis.sock',
             'path' => '/tmp/redis.sock',
@@ -246,7 +258,7 @@ class FactoryTest extends PredisTestCase
     /**
     /**
      * @group disconnected
      * @group disconnected
      * @expectedException \InvalidArgumentException
      * @expectedException \InvalidArgumentException
-     * @expecteExceptionMessage Unknown connection scheme: 'redis'.
+     * @expecteExceptionMessage Unknown connection scheme: 'test'.
      */
      */
     public function testDefineAndUndefineConnection()
     public function testDefineAndUndefineConnection()
     {
     {
@@ -254,11 +266,11 @@ class FactoryTest extends PredisTestCase
 
 
         $factory = new Factory();
         $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
tests/Predis/Connection/ParametersTest.php

@@ -137,7 +137,7 @@ class ParametersTest extends PredisTestCase
      */
      */
     public function testParsingURI()
     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(
         $expected = array(
             'scheme' => 'tcp',
             'scheme' => 'tcp',
@@ -145,6 +145,8 @@ class ParametersTest extends PredisTestCase
             'port' => 6400,
             'port' => 6400,
             'timeout' => '0.5',
             'timeout' => '0.5',
             'persistent' => '1',
             'persistent' => '1',
+            'database' => '5',
+            'password' => 'secret',
         );
         );
 
 
         $this->assertSame($expected, Parameters::parse($uri));
         $this->assertSame($expected, Parameters::parse($uri));
@@ -153,7 +155,45 @@ class ParametersTest extends PredisTestCase
     /**
     /**
      * @group disconnected
      * @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';
         $uri = 'unix:///tmp/redis.sock?timeout=0.5&persistent=1';
 
 

+ 36 - 3
tests/Predis/Connection/PhpiredisSocketConnectionTest.php

@@ -29,12 +29,34 @@ class PhpiredisSocketConnectionTest extends PredisConnectionTestCase
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */
-    public function testExposesParameters()
+    public function testSupportsSchemeTCP()
     {
     {
-        $parameters = $this->getParameters();
+        $parameters = $this->getParameters(array('scheme' => 'tcp'));
         $connection = new PhpiredisSocketConnection($parameters);
         $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);
         new PhpiredisSocketConnection($parameters);
     }
     }
 
 
+    /**
+     * @group disconnected
+     */
+    public function testExposesParameters()
+    {
+        $parameters = $this->getParameters();
+        $connection = new PhpiredisSocketConnection($parameters);
+
+        $this->assertSame($parameters, $connection->getParameters());
+    }
+
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */

+ 36 - 3
tests/Predis/Connection/PhpiredisStreamConnectionTest.php

@@ -29,12 +29,34 @@ class PhpiredisStreamConnectionTest extends PredisConnectionTestCase
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */
-    public function testExposesParameters()
+    public function testSupportsSchemeTCP()
     {
     {
-        $parameters = $this->getParameters();
+        $parameters = $this->getParameters(array('scheme' => 'tcp'));
         $connection = new PhpiredisStreamConnection($parameters);
         $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);
         new PhpiredisStreamConnection($parameters);
     }
     }
 
 
+    /**
+     * @group disconnected
+     */
+    public function testExposesParameters()
+    {
+        $parameters = $this->getParameters();
+        $connection = new PhpiredisStreamConnection($parameters);
+
+        $this->assertSame($parameters, $connection->getParameters());
+    }
+
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */

+ 36 - 3
tests/Predis/Connection/StreamConnectionTest.php

@@ -29,12 +29,34 @@ class StreamConnectionTest extends PredisConnectionTestCase
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */
-    public function testExposesParameters()
+    public function testSupportsSchemeTCP()
     {
     {
-        $parameters = $this->getParameters();
+        $parameters = $this->getParameters(array('scheme' => 'tcp'));
         $connection = new StreamConnection($parameters);
         $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);
         new StreamConnection($parameters);
     }
     }
 
 
+    /**
+     * @group disconnected
+     */
+    public function testExposesParameters()
+    {
+        $parameters = $this->getParameters();
+        $connection = new StreamConnection($parameters);
+
+        $this->assertSame($parameters, $connection->getParameters());
+    }
+
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */

+ 22 - 0
tests/Predis/Connection/WebdisConnectionTest.php

@@ -32,6 +32,28 @@ class WebdisConnectionTest extends PredisTestCase
         $this->assertTrue($connection->isConnected());
         $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
      * @group disconnected
      * @expectedException \Predis\NotSupportedException
      * @expectedException \Predis\NotSupportedException