Skip to content

Commit

Permalink
Redesign how Predis\ConnectionFactory works and make more sense out o…
Browse files Browse the repository at this point in the history
…f it.
  • Loading branch information
nrk committed Nov 29, 2011
1 parent 058e112 commit 728afe7
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 99 deletions.
4 changes: 3 additions & 1 deletion FAQ.PERFORMANCES.md
Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions README.md
Expand Up @@ -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.

Expand Down
51 changes: 8 additions & 43 deletions lib/Predis/Client.php
Expand Up @@ -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.
Expand All @@ -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);
}

Expand Down Expand Up @@ -94,51 +95,15 @@ private function filterOptions($options)
*/
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 (isset($parameters->database)) {
$command = $this->createCommand('select', array($parameters->database));
$connection->pushInitCommand($command);
if (is_array($parameters) && isset($parameters[0])) {
return $this->connections->createCluster($this->options->cluster, $parameters, $this->profile);
}

return $connection;
return $this->connections->create($parameters, $this->profile);
}

/**
Expand Down Expand Up @@ -168,7 +133,7 @@ public function getOptions()
*/
public function getConnectionFactory()
{
return $this->connectionFactory;
return $this->connections;
}

/**
Expand Down
122 changes: 72 additions & 50 deletions lib/Predis/ConnectionFactory.php
Expand Up @@ -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
Expand All @@ -22,22 +25,28 @@
*/
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',
);
}

/**
Expand All @@ -46,81 +55,61 @@ public function __construct(Array $schemesMap = null)
* 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;
return $initializer;
}

/**
* 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.
* {@inheritdoc}
*/
public static function define($scheme, $connectionInitializer)
public function define($scheme, $initializer)
{
self::ensureDefaultSchemes();
self::checkConnectionInitializer($connectionInitializer);
self::$globalSchemes[$scheme] = $connectionInitializer;
$this->schemes[$scheme] = $this->checkInitializer($initializer);
}

/**
* 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.
* {@inheritdoc}
*/
public function defineConnection($scheme, $connectionInitializer)
public function undefine($scheme)
{
self::checkConnectionInitializer($connectionInitializer);
$this->instanceSchemes[$scheme] = $connectionInitializer;
unset($this->schemes[$scheme]);
}

/**
* {@inheritdoc}
*/
public function create($parameters)
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);
Expand All @@ -133,4 +122,37 @@ public function create($parameters)

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);
}
}
}
29 changes: 28 additions & 1 deletion lib/Predis/IConnectionFactory.php
Expand Up @@ -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.
Expand All @@ -19,11 +22,35 @@
*/
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);
}
6 changes: 5 additions & 1 deletion lib/Predis/Options/ClientConnectionFactory.php
Expand Up @@ -30,7 +30,11 @@ public function validate($value)
return $value;
}
if (is_array($value)) {
return new ConnectionFactory($value);
$factory = $this->getDefault();
foreach ($value as $scheme => $initializer) {
$factory->define($scheme, $initializer);
}
return $factory;
}
}

Expand Down

0 comments on commit 728afe7

Please sign in to comment.