From 728afe7fcbe5612c868f4cd8ff066f0c8b97a2e0 Mon Sep 17 00:00:00 2001 From: Daniele Alessandri Date: Tue, 29 Nov 2011 12:01:28 +0100 Subject: [PATCH] Redesign how Predis\ConnectionFactory works and make more sense out of it. --- FAQ.PERFORMANCES.md | 4 +- README.md | 7 +- lib/Predis/Client.php | 51 +------ lib/Predis/ConnectionFactory.php | 142 ++++++++++-------- lib/Predis/IConnectionFactory.php | 29 +++- .../Options/ClientConnectionFactory.php | 6 +- 6 files changed, 130 insertions(+), 109 deletions(-) diff --git a/FAQ.PERFORMANCES.md b/FAQ.PERFORMANCES.md index 8c2904e3..f33828f1 100644 --- a/FAQ.PERFORMANCES.md +++ b/FAQ.PERFORMANCES.md @@ -55,7 +55,9 @@ name) and let Predis using it. __phpiredis__ is a C-based extension that wraps _ official Redis C client library) with a thin layer that exposes its features to PHP. You will now get the benefits of a faster protocol parser just by adding a single line of code in your application: - Predis\ConnectionFactory::define('tcp', '\Predis\Network\PhpiredisConnection'); + $client = new Predis\Client('tcp://127.0.0.1', array( + 'connections' => array('tcp' => 'Predis\Network\PhpiredisConnection') + )); As simple as it is, nothing will really change in the way you use the library in your application. So, how fast is it now? There are not much improvements for inline or short bulk replies (e.g. _SET_ or diff --git a/README.md b/README.md index 1668ad0a..687b2ee4 100644 --- a/README.md +++ b/README.md @@ -113,11 +113,12 @@ class MyConnectionClass implements Predis\Network\IConnectionSingle // implementation goes here } -// Let Predis automatically use your own class to handle the default TCP connection -Predis\ConnectionFactory::define('tcp', 'MyConnectionClass'); +// Let Predis automatically use your own class to handle connections identified by the tcp prefix. +$client = new Predis\Client('tcp://127.0.0.1', array( + 'connections' => array('tcp' => 'MyConnectionClass') +)); ``` - You can have a look at the `Predis\Network` namespace for some actual code that gives a better insight about how to create new connection classes. diff --git a/lib/Predis/Client.php b/lib/Predis/Client.php index c8b695c5..2d24f84a 100644 --- a/lib/Predis/Client.php +++ b/lib/Predis/Client.php @@ -32,7 +32,7 @@ class Client private $options; private $profile; private $connection; - private $connectionFactory; + private $connections; /** * Initializes a new client with optional connection parameters and client options. @@ -43,15 +43,16 @@ class Client public function __construct($parameters = null, $options = null) { $options = $this->filterOptions($options); - $profile = $options->profile; + $profile = $options->profile; if (isset($options->prefix)) { $profile->setProcessor($options->prefix); } $this->options = $options; $this->profile = $profile; - $this->connectionFactory = $options->connections; + $this->connections = $options->connections; + $this->connection = $this->initializeConnection($parameters); } @@ -94,51 +95,15 @@ class Client */ private function initializeConnection($parameters) { - if ($parameters === null) { - return $this->createConnection(new ConnectionParameters()); - } - - if (is_array($parameters)) { - if (isset($parameters[0])) { - $cluster = $this->options->cluster; - foreach ($parameters as $node) { - $connection = $node instanceof IConnectionSingle ? $node : $this->createConnection($node); - $cluster->add($connection); - } - return $cluster; - } - return $this->createConnection($parameters); - } - if ($parameters instanceof IConnection) { return $parameters; } - return $this->createConnection($parameters); - } - - /** - * Creates a new connection to a single server with the provided parameters. - * - * @param mixed $parameters Connection parameters. - * @return IConnectionSingle - */ - protected function createConnection($parameters) - { - $connection = $this->connectionFactory->create($parameters); - $parameters = $connection->getParameters(); - - if (isset($parameters->password)) { - $command = $this->createCommand('auth', array($parameters->password)); - $connection->pushInitCommand($command); + if (is_array($parameters) && isset($parameters[0])) { + return $this->connections->createCluster($this->options->cluster, $parameters, $this->profile); } - if (isset($parameters->database)) { - $command = $this->createCommand('select', array($parameters->database)); - $connection->pushInitCommand($command); - } - - return $connection; + return $this->connections->create($parameters, $this->profile); } /** @@ -168,7 +133,7 @@ class Client */ public function getConnectionFactory() { - return $this->connectionFactory; + return $this->connections; } /** diff --git a/lib/Predis/ConnectionFactory.php b/lib/Predis/ConnectionFactory.php index 49812392..e7e98243 100644 --- a/lib/Predis/ConnectionFactory.php +++ b/lib/Predis/ConnectionFactory.php @@ -11,7 +11,10 @@ namespace Predis; +use Predis\Profiles\IServerProfile; use Predis\Network\IConnectionSingle; +use Predis\Network\IConnectionCluster; +use Predis\Profiles\ServerProfile; /** * Provides a default factory for Redis connections that maps URI schemes @@ -22,22 +25,28 @@ use Predis\Network\IConnectionSingle; */ class ConnectionFactory implements IConnectionFactory { - private static $globalSchemes; - - private $instanceSchemes = array(); + private $schemes; /** - * @param array $schemesMap Map of URI schemes to connection classes. + * Initializes a new instance of the default connection factory class used by Predis. */ - public function __construct(Array $schemesMap = null) + public function __construct() { - $this->instanceSchemes = self::ensureDefaultSchemes(); + $this->schemes = $this->getDefaultSchemes(); + } - if (isset($schemesMap)) { - foreach ($schemesMap as $scheme => $initializer) { - $this->defineConnection($scheme, $initializer); - } - } + /** + * Returns a named array that maps URI schemes to connection classes. + * + * @return array Map of URI schemes and connection classes. + */ + protected function getDefaultSchemes() + { + return array( + 'tcp' => 'Predis\Network\StreamConnection', + 'unix' => 'Predis\Network\StreamConnection', + 'http' => 'Predis\Network\WebdisConnection', + ); } /** @@ -46,81 +55,61 @@ class ConnectionFactory implements IConnectionFactory * callable objects are used for lazy initialization of connection objects. * * @param mixed $initializer FQN of a connection class or a callable for lazy initialization. + * @return mixed */ - private static function checkConnectionInitializer($initializer) + protected function checkInitializer($initializer) { if (is_callable($initializer)) { - return; + return $initializer; } $initializerReflection = new \ReflectionClass($initializer); - if (!$initializerReflection->isSubclassOf('\Predis\Network\IConnectionSingle')) { + if (!$initializerReflection->isSubclassOf('Predis\Network\IConnectionSingle')) { throw new \InvalidArgumentException( 'A connection initializer must be a valid connection class or a callable object' ); } - } - /** - * Ensures that the default global URI schemes map is initialized. - * - * @return array - */ - private static function ensureDefaultSchemes() - { - if (!isset(self::$globalSchemes)) { - self::$globalSchemes = array( - 'tcp' => '\Predis\Network\StreamConnection', - 'unix' => '\Predis\Network\StreamConnection', - ); - } - - return self::$globalSchemes; - } - - /** - * Defines a new URI scheme => connection class relation at class level. - * - * @param string $scheme URI scheme - * @param mixed $connectionInitializer FQN of a connection class or a callable for lazy initialization. - */ - public static function define($scheme, $connectionInitializer) - { - self::ensureDefaultSchemes(); - self::checkConnectionInitializer($connectionInitializer); - self::$globalSchemes[$scheme] = $connectionInitializer; - } - - /** - * Defines a new URI scheme => connection class relation at instance level. - * - * @param string $scheme URI scheme - * @param mixed $connectionInitializer FQN of a connection class or a callable for lazy initialization. - */ - public function defineConnection($scheme, $connectionInitializer) - { - self::checkConnectionInitializer($connectionInitializer); - $this->instanceSchemes[$scheme] = $connectionInitializer; + return $initializer; } /** * {@inheritdoc} */ - public function create($parameters) + public function define($scheme, $initializer) + { + $this->schemes[$scheme] = $this->checkInitializer($initializer); + } + + /** + * {@inheritdoc} + */ + public function undefine($scheme) + { + unset($this->schemes[$scheme]); + } + + /** + * {@inheritdoc} + */ + public function create($parameters, IServerProfile $profile = null) { if (!$parameters instanceof IConnectionParameters) { - $parameters = new ConnectionParameters($parameters); + $parameters = new ConnectionParameters($parameters ?: array()); } $scheme = $parameters->scheme; - if (!isset($this->instanceSchemes[$scheme])) { + if (!isset($this->schemes[$scheme])) { throw new \InvalidArgumentException("Unknown connection scheme: $scheme"); } - $initializer = $this->instanceSchemes[$scheme]; + $initializer = $this->schemes[$scheme]; if (!is_callable($initializer)) { - return new $initializer($parameters); + $connection = new $initializer($parameters); + $this->prepareConnection($connection, $profile ?: ServerProfile::getDefault()); + + return $connection; } $connection = call_user_func($initializer, $parameters); @@ -133,4 +122,37 @@ class ConnectionFactory implements IConnectionFactory return $connection; } + + /** + * {@inheritdoc} + */ + public function createCluster(IConnectionCluster $cluster, $parameters, IServerProfile $profile = null) + { + foreach ($parameters as $node) { + $cluster->add($node instanceof IConnectionSingle ? $node : $this->create($node, $profile)); + } + + return $cluster; + } + + /** + * Prepares a connection object after its initialization. + * + * @param IConnectionSingle $connection Instance of a connection object. + * @param IServerProfile $profile $connection Instance of a connection object. + */ + protected function prepareConnection(IConnectionSingle $connection, IServerProfile $profile) + { + $parameters = $connection->getParameters(); + + if (isset($parameters->password)) { + $command = $profile->createCommand('auth', array($parameters->password)); + $connection->pushInitCommand($command); + } + + if (isset($parameters->database)) { + $command = $profile->createCommand('select', array($parameters->database)); + $connection->pushInitCommand($command); + } + } } diff --git a/lib/Predis/IConnectionFactory.php b/lib/Predis/IConnectionFactory.php index 884a019d..290c1e16 100644 --- a/lib/Predis/IConnectionFactory.php +++ b/lib/Predis/IConnectionFactory.php @@ -11,6 +11,9 @@ namespace Predis; +use Predis\Profiles\IServerProfile; +use Predis\Network\IConnectionCluster; + /** * Interface that must be implemented by classes that provide their own mechanism * to create and initialize new instances of Predis\Network\IConnectionSingle. @@ -19,11 +22,35 @@ namespace Predis; */ interface IConnectionFactory { + /** + * Defines or overrides the connection class identified by a scheme prefix. + * + * @param string $scheme URI scheme identifying the connection class. + * @param mixed $initializer FQN of a connection class or a callable object for lazy initialization. + */ + public function define($scheme, $initializer); + + /** + * Undefines the connection identified by a scheme prefix. + * + * @param string $scheme Parameters for the connection. + */ + public function undefine($scheme); + /** * Creates a new connection object. * * @param mixed $parameters Parameters for the connection. * @return Predis\Network\IConnectionSingle */ - public function create($parameters); + public function create($parameters, IServerProfile $profile = null); + + /** + * Prepares a cluster of connection objects. + * + * @param IConnectionCluster Instance of a connection cluster class. + * @param array $parameters List of parameters for each connection object. + * @return Predis\Network\IConnectionCluster + */ + public function createCluster(IConnectionCluster $cluster, $parameters, IServerProfile $profile = null); } diff --git a/lib/Predis/Options/ClientConnectionFactory.php b/lib/Predis/Options/ClientConnectionFactory.php index 33429d74..26f6b3ab 100644 --- a/lib/Predis/Options/ClientConnectionFactory.php +++ b/lib/Predis/Options/ClientConnectionFactory.php @@ -30,7 +30,11 @@ class ClientConnectionFactory extends Option return $value; } if (is_array($value)) { - return new ConnectionFactory($value); + $factory = $this->getDefault(); + foreach ($value as $scheme => $initializer) { + $factory->define($scheme, $initializer); + } + return $factory; } }