|
18 | 18 | */
|
19 | 19 | class RedisAdapter extends AbstractAdapter
|
20 | 20 | {
|
| 21 | + private static $defaultConnectionOptions = array( |
| 22 | + 'class' => \Redis::class, |
| 23 | + 'persistent' => 0, |
| 24 | + 'timeout' => 0, |
| 25 | + 'read_timeout' => 0, |
| 26 | + 'retry_interval' => 0, |
| 27 | + ); |
21 | 28 | private $redis;
|
22 | 29 |
|
23 | 30 | public function __construct(\Redis $redisConnection, $namespace = '', $defaultLifetime = 0)
|
24 | 31 | {
|
25 | 32 | if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
|
26 | 33 | throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
27 | 34 | }
|
| 35 | + if (!$redisConnection->isConnected()) { |
| 36 | + throw new InvalidArgumentException('Redis connection is not connected.'); |
| 37 | + } |
28 | 38 | $this->redis = $redisConnection;
|
29 | 39 |
|
30 | 40 | parent::__construct($namespace, $defaultLifetime);
|
31 | 41 | }
|
32 | 42 |
|
| 43 | + /** |
| 44 | + * Creates a Redis connection using a DSN configuration. |
| 45 | + * |
| 46 | + * Example DNS: |
| 47 | + * - redis://localhost |
| 48 | + * - redis://example.com:1234 |
| 49 | + |
| 50 | + * - redis:///var/run/redis.sock |
| 51 | + * - redis:///var/run/redis.sock?dbindex=13&user=secret |
| 52 | + * |
| 53 | + * @param string $dsn |
| 54 | + * |
| 55 | + * @throws InvalidArgumentException When the DSN is invalid. |
| 56 | + * |
| 57 | + * @return \Redis |
| 58 | + */ |
| 59 | + public static function createConnection($dsn, array $options = array()) |
| 60 | + { |
| 61 | + if (0 !== strpos($dsn, 'redis://')) { |
| 62 | + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s does not start with "redis://"', $dsn)); |
| 63 | + } |
| 64 | + if (false === $params = parse_url(substr_replace($dsn, 'file', 0, 5))) { |
| 65 | + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); |
| 66 | + } |
| 67 | + if (!isset($params['host']) && !isset($params['path'])) { |
| 68 | + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); |
| 69 | + } |
| 70 | + if (isset($params['query'])) { |
| 71 | + parse_str($params['query'], $query); |
| 72 | + $params += $query; |
| 73 | + } |
| 74 | + $params += $options + self::$defaultConnectionOptions + array( |
| 75 | + 'host' => isset($params['host']) ? $params['host'] : $params['path'], |
| 76 | + 'port' => isset($params['host']) ? 6379 : null, |
| 77 | + 'dbindex' => isset($params['host'], $params['path']) ? substr($params['path'], 1) : 0, |
| 78 | + 'user' => '', |
| 79 | + ); |
| 80 | + if (\Redis::class !== $params['class'] && !is_subclass_of($params['class'], \Redis::class)) { |
| 81 | + throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis"', $params['class'])); |
| 82 | + } |
| 83 | + $connect = empty($params['persistent']) ? 'connect' : 'pconnect'; |
| 84 | + $redis = new $params['class'](); |
| 85 | + @$redis->{$connect}($params['host'], $params['port'], $params['timeout'], null, $params['retry_interval']); |
| 86 | + |
| 87 | + if (@!$redis->isConnected()) { |
| 88 | + $e = ($e = error_get_last()) && preg_match('/^Redis::p?connect\(\): (.*)/', $e['message'], $e) ? sprintf(' (%s)', $e[1]) : ''; |
| 89 | + throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $e, $dsn)); |
| 90 | + } |
| 91 | + |
| 92 | + if (($params['user'] && !$redis->auth($params['user'])) |
| 93 | + || ($params['dbindex'] && !$redis->select($params['dbindex'])) |
| 94 | + || ($params['read_timeout'] && !$redis->setOption(\Redis::OPT_READ_TIMEOUT, $params['read_timeout'])) |
| 95 | + ) { |
| 96 | + $e = preg_replace('/^ERR /', '', $redis->getLastError()); |
| 97 | + $redis->close(); |
| 98 | + throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e, $dsn)); |
| 99 | + } |
| 100 | + |
| 101 | + return $redis; |
| 102 | + } |
| 103 | + |
33 | 104 | /**
|
34 | 105 | * {@inheritdoc}
|
35 | 106 | */
|
|
0 commit comments