Skip to content

Commit

Permalink
[redis] add dsn support for redis transport.
Browse files Browse the repository at this point in the history
  • Loading branch information
makasim committed Sep 22, 2017
1 parent 9667cac commit 40c9612
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 18 deletions.
21 changes: 17 additions & 4 deletions docs/transport/redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,26 @@ $ composer require enqueue/redis predis/predis:^1
<?php
use Enqueue\Redis\RedisConnectionFactory;

$connectionFactory = new RedisConnectionFactory([
'host' => 'localhost',
'port' => 6379,
// connects to localhost
$factory = new RedisConnectionFactory();

// same as above
$factory = new RedisConnectionFactory('redis:');

// same as above
$factory = new RedisConnectionFactory([]);

// connect to Redis at example.com port 1000 using phpredis extension
$factory = new RedisConnectionFactory([
'host' => 'example.com',
'port' => 1000,
'vendor' => 'phpredis',
]);

$psrContext = $connectionFactory->createContext();
// same as above but given as DSN string
$factory = new RedisConnectionFactory('redis://example.com:1000?vendor=phpredis');

$psrContext = $factory->createContext();
```

* With predis library:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Enqueue\Null\NullConnectionFactory;
use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
use Enqueue\RdKafka\RdKafkaConnectionFactory;
use Enqueue\Redis\RedisConnectionFactory;
use PHPUnit\Framework\TestCase;

class DsnToConnectionFactoryFunctionTest extends TestCase
Expand Down Expand Up @@ -71,5 +72,7 @@ public static function provideDSNs()
// yield ['gearman://', GearmanConnectionFactory::class];

yield ['rdkafka://', RdKafkaConnectionFactory::class];

yield ['redis:', RedisConnectionFactory::class];
}
}
9 changes: 7 additions & 2 deletions pkg/enqueue/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Enqueue\Null\NullConnectionFactory;
use Enqueue\Pheanstalk\PheanstalkConnectionFactory;
use Enqueue\RdKafka\RdKafkaConnectionFactory;
use Enqueue\Redis\RedisConnectionFactory;
use Interop\Queue\PsrConnectionFactory;
use Interop\Queue\PsrContext;

Expand Down Expand Up @@ -80,8 +81,12 @@ function dsn_to_connection_factory($dsn)
$map['rdkafka'] = RdKafkaConnectionFactory::class;
}

list($scheme) = explode('://', $dsn);
if (false == $scheme || false === strpos($dsn, '://')) {
if (class_exists(RedisConnectionFactory::class)) {
$map['redis'] = RedisConnectionFactory::class;
}

list($scheme) = explode(':', $dsn, 2);
if (false == $scheme || false === strpos($dsn, ':')) {
throw new \LogicException(sprintf('The scheme could not be parsed from DSN "%s"', $dsn));
}

Expand Down
76 changes: 64 additions & 12 deletions pkg/redis/RedisConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,25 @@ class RedisConnectionFactory implements PsrConnectionFactory
* 'lazy' => the connection will be performed as later as possible, if the option set to true
* ].
*
* @param $config
* or
*
* redis:
* redis:?vendor=predis
*
* @param array|string|null $config
*/
public function __construct(array $config)
public function __construct($config = 'redis:')
{
$this->config = array_replace([
'host' => null,
'port' => null,
'timeout' => null,
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'persisted' => false,
'lazy' => true,
], $config);
if (empty($config) || 'redis:' === $config) {
$config = [];
} elseif (is_string($config)) {
$config = $this->parseDsn($config);
} elseif (is_array($config)) {
} else {
throw new \LogicException('The config must be either an array of options, a DSN string or null');
}

$this->config = array_replace($this->defaultConfig(), $config);

$supportedVendors = ['predis', 'phpredis'];
if (false == in_array($this->config['vendor'], $supportedVendors, true)) {
Expand Down Expand Up @@ -89,4 +94,51 @@ private function createRedis()

return $this->redis;
}

/**
* @param string $dsn
*
* @return array
*/
private function parseDsn($dsn)
{
if (false === strpos($dsn, 'redis:')) {
throw new \LogicException(sprintf('The given DSN "%s" is not supported. Must start with "redis:".', $dsn));
}

if (false === $config = parse_url($dsn)) {
throw new \LogicException(sprintf('Failed to parse DSN "%s"', $dsn));
}

if ($query = parse_url($dsn, PHP_URL_QUERY)) {
$queryConfig = [];
parse_str($query, $queryConfig);

$config = array_replace($queryConfig, $config);
}

unset($config['query'], $config['scheme']);

$config['lazy'] = empty($config['lazy']) ? false : true;
$config['persisted'] = empty($config['persisted']) ? false : true;

return $config;
}

/**
* @return array
*/
private function defaultConfig()
{
return [
'host' => 'localhost',
'port' => 6379,
'timeout' => null,
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'persisted' => false,
'lazy' => true,
];
}
}
135 changes: 135 additions & 0 deletions pkg/redis/Tests/RedisConnectionFactoryConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

namespace Enqueue\Redis\Tests;

use Enqueue\Redis\RedisConnectionFactory;
use Enqueue\Test\ClassExtensionTrait;
use PHPUnit\Framework\TestCase;

/**
* The class contains the factory tests dedicated to configuration.
*/
class RedisConnectionFactoryConfigTest extends TestCase
{
use ClassExtensionTrait;

public function testThrowNeitherArrayStringNorNullGivenAsConfig()
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('The config must be either an array of options, a DSN string or null');

new RedisConnectionFactory(new \stdClass());
}

public function testThrowIfSchemeIsNotAmqp()
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('The given DSN "http://example.com" is not supported. Must start with "redis:".');

new RedisConnectionFactory('http://example.com');
}

public function testThrowIfDsnCouldNotBeParsed()
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Failed to parse DSN "redis://:@/"');

new RedisConnectionFactory('redis://:@/');
}

public function testThrowIfVendorIsInvalid()
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Unsupported redis vendor given. It must be either "predis", "phpredis". Got "invalidVendor"');

new RedisConnectionFactory(['vendor' => 'invalidVendor']);
}

/**
* @dataProvider provideConfigs
*
* @param mixed $config
* @param mixed $expectedConfig
*/
public function testShouldParseConfigurationAsExpected($config, $expectedConfig)
{
$factory = new RedisConnectionFactory($config);

$this->assertAttributeEquals($expectedConfig, 'config', $factory);
}

public static function provideConfigs()
{
yield [
null,
[
'host' => 'localhost',
'port' => 6379,
'timeout' => null,
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'persisted' => false,
'lazy' => true,
],
];

yield [
'redis:',
[
'host' => 'localhost',
'port' => 6379,
'timeout' => null,
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'persisted' => false,
'lazy' => true,
],
];

yield [
[],
[
'host' => 'localhost',
'port' => 6379,
'timeout' => null,
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'persisted' => false,
'lazy' => true,
],
];

yield [
'redis://localhost:1234?foo=bar&lazy=0&persisted=true',
[
'host' => 'localhost',
'port' => 1234,
'timeout' => null,
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'persisted' => true,
'lazy' => false,
'foo' => 'bar',
],
];

yield [
['host' => 'localhost', 'port' => 1234, 'foo' => 'bar'],
[
'host' => 'localhost',
'port' => 1234,
'timeout' => null,
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'persisted' => false,
'lazy' => true,
'foo' => 'bar',
],
];
}
}

0 comments on commit 40c9612

Please sign in to comment.