[Cache] Add DSN based Redis connection factory
This commit is contained in:
parent
731d7a6bf0
commit
3073efb6d1
@ -18,6 +18,13 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
|||||||
*/
|
*/
|
||||||
class RedisAdapter extends AbstractAdapter
|
class RedisAdapter extends AbstractAdapter
|
||||||
{
|
{
|
||||||
|
private static $defaultConnectionOptions = array(
|
||||||
|
'class' => \Redis::class,
|
||||||
|
'persistent' => 0,
|
||||||
|
'timeout' => 0,
|
||||||
|
'read_timeout' => 0,
|
||||||
|
'retry_interval' => 0,
|
||||||
|
);
|
||||||
private $redis;
|
private $redis;
|
||||||
|
|
||||||
public function __construct(\Redis $redisConnection, $namespace = '', $defaultLifetime = 0)
|
public function __construct(\Redis $redisConnection, $namespace = '', $defaultLifetime = 0)
|
||||||
@ -30,6 +37,80 @@ class RedisAdapter extends AbstractAdapter
|
|||||||
parent::__construct($namespace, $defaultLifetime);
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Redis connection using a DSN configuration.
|
||||||
|
*
|
||||||
|
* Example DSN:
|
||||||
|
* - redis://localhost
|
||||||
|
* - redis://example.com:1234
|
||||||
|
* - redis://secret@example.com/13
|
||||||
|
* - redis:///var/run/redis.sock
|
||||||
|
* - redis://secret@/var/run/redis.sock/13
|
||||||
|
*
|
||||||
|
* @param string $dsn
|
||||||
|
* @param array $options See self::$defaultConnectionOptions
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When the DSN is invalid.
|
||||||
|
*
|
||||||
|
* @return \Redis
|
||||||
|
*/
|
||||||
|
public static function createConnection($dsn, array $options = array())
|
||||||
|
{
|
||||||
|
if (0 !== strpos($dsn, 'redis://')) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s does not start with "redis://"', $dsn));
|
||||||
|
}
|
||||||
|
$params = preg_replace_callback('#^redis://(?:([^@]*)@)?#', function ($m) use (&$auth) {
|
||||||
|
if (isset($m[1])) {
|
||||||
|
$auth = $m[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'file://';
|
||||||
|
}, $dsn);
|
||||||
|
if (false === $params = parse_url($params)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn));
|
||||||
|
}
|
||||||
|
if (!isset($params['host']) && !isset($params['path'])) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn));
|
||||||
|
}
|
||||||
|
if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) {
|
||||||
|
$params['dbindex'] = $m[1];
|
||||||
|
$params['path'] = substr($params['path'], 0, -strlen($m[0]));
|
||||||
|
}
|
||||||
|
$params += array(
|
||||||
|
'host' => isset($params['host']) ? $params['host'] : $params['path'],
|
||||||
|
'port' => isset($params['host']) ? 6379 : null,
|
||||||
|
'dbindex' => 0,
|
||||||
|
);
|
||||||
|
if (isset($params['query'])) {
|
||||||
|
parse_str($params['query'], $query);
|
||||||
|
$params += $query;
|
||||||
|
}
|
||||||
|
$params += $options + self::$defaultConnectionOptions;
|
||||||
|
|
||||||
|
if (\Redis::class !== $params['class'] && !is_subclass_of($params['class'], \Redis::class)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis"', $params['class']));
|
||||||
|
}
|
||||||
|
$connect = empty($params['persistent']) ? 'connect' : 'pconnect';
|
||||||
|
$redis = new $params['class']();
|
||||||
|
@$redis->{$connect}($params['host'], $params['port'], $params['timeout'], null, $params['retry_interval']);
|
||||||
|
|
||||||
|
if (@!$redis->isConnected()) {
|
||||||
|
$e = ($e = error_get_last()) && preg_match('/^Redis::p?connect\(\): (.*)/', $e['message'], $e) ? sprintf(' (%s)', $e[1]) : '';
|
||||||
|
throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $e, $dsn));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((null !== $auth && !$redis->auth($auth))
|
||||||
|
|| ($params['dbindex'] && !$redis->select($params['dbindex']))
|
||||||
|
|| ($params['read_timeout'] && !$redis->setOption(\Redis::OPT_READ_TIMEOUT, $params['read_timeout']))
|
||||||
|
) {
|
||||||
|
$e = preg_replace('/^ERR /', '', $redis->getLastError());
|
||||||
|
$redis->close();
|
||||||
|
throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e, $dsn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $redis;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -44,4 +44,60 @@ class RedisAdapterTest extends CachePoolTest
|
|||||||
self::$redis->flushDB();
|
self::$redis->flushDB();
|
||||||
self::$redis->close();
|
self::$redis->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCreateConnection()
|
||||||
|
{
|
||||||
|
$redis = RedisAdapter::createConnection('redis://localhost');
|
||||||
|
$this->assertTrue($redis->isConnected());
|
||||||
|
$this->assertSame(0, $redis->getDbNum());
|
||||||
|
|
||||||
|
$redis = RedisAdapter::createConnection('redis://localhost/2');
|
||||||
|
$this->assertSame(2, $redis->getDbNum());
|
||||||
|
|
||||||
|
$redis = RedisAdapter::createConnection('redis://localhost', array('timeout' => 3));
|
||||||
|
$this->assertEquals(3, $redis->getTimeout());
|
||||||
|
|
||||||
|
$redis = RedisAdapter::createConnection('redis://localhost?timeout=4');
|
||||||
|
$this->assertEquals(4, $redis->getTimeout());
|
||||||
|
|
||||||
|
$redis = RedisAdapter::createConnection('redis://localhost', array('read_timeout' => 5));
|
||||||
|
$this->assertEquals(5, $redis->getReadTimeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideFailedCreateConnection
|
||||||
|
* @expectedException Symfony\Component\Cache\Exception\InvalidArgumentException
|
||||||
|
* @expectedExceptionMessage Redis connection failed
|
||||||
|
*/
|
||||||
|
public function testFailedCreateConnection($dsn)
|
||||||
|
{
|
||||||
|
RedisAdapter::createConnection($dsn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideFailedCreateConnection()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('redis://localhost:1234'),
|
||||||
|
array('redis://foo@localhost'),
|
||||||
|
array('redis://localhost/123'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideInvalidCreateConnection
|
||||||
|
* @expectedException Symfony\Component\Cache\Exception\InvalidArgumentException
|
||||||
|
* @expectedExceptionMessage Invalid Redis DSN
|
||||||
|
*/
|
||||||
|
public function testInvalidCreateConnection($dsn)
|
||||||
|
{
|
||||||
|
RedisAdapter::createConnection($dsn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideInvalidCreateConnection()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('foo://localhost'),
|
||||||
|
array('redis://'),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user