Skip to content

[Redis] Add ability to pass Redis instance to connection factory #372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions bin/dev
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ set -e
while getopts "bustefcdp" OPTION; do
case $OPTION in
b)
COMPOSE_PROJECT_NAME=mqdev docker-compose pull && COMPOSE_PROJECT_NAME=mqdev docker-compose build
docker-compose pull && docker-compose build
;;
u)
COMPOSE_PROJECT_NAME=mqdev docker-compose up
docker-compose up
;;
s)
COMPOSE_PROJECT_NAME=mqdev docker-compose stop
docker-compose stop
;;
e)
docker exec -it mqdev_dev_1 /bin/bash
Expand All @@ -24,10 +24,10 @@ while getopts "bustefcdp" OPTION; do
./bin/run-fun-test.sh "$2"
;;
c)
COMPOSE_PROJECT_NAME=mqdev docker-compose run -e CHANGELOG_GITHUB_TOKEN=${CHANGELOG_GITHUB_TOKEN:-""} --workdir="/mqdev" --rm generate-changelog github_changelog_generator --future-release "$2" --simple-list
docker-compose run -e CHANGELOG_GITHUB_TOKEN=${CHANGELOG_GITHUB_TOKEN:-""} --workdir="/mqdev" --rm generate-changelog github_changelog_generator --future-release "$2" --simple-list
;;

d) COMPOSE_PROJECT_NAME=mqdev docker-compose run --workdir="/mqdev" --rm dev php pkg/enqueue-bundle/Tests/Functional/app/console.php config:dump-reference enqueue -vvv
d) docker-compose run --workdir="/mqdev" --rm dev php pkg/enqueue-bundle/Tests/Functional/app/console.php config:dump-reference enqueue -vvv
;;
\?)
echo "Invalid option: -$OPTARG" >&2
Expand Down
17 changes: 17 additions & 0 deletions docs/transport/redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ $connectionFactory = new RedisConnectionFactory([
$psrContext = $connectionFactory->createContext();
```

* With custom redis instance:

It gives you more control over vendor specific features.

```php
<?php
use Enqueue\Redis\RedisConnectionFactory;
use Enqueue\Redis\PRedis;

$config = [];
$options = [];

$redis = new PRedis(new \PRedis\Client($config, $options));

$factory = new RedisConnectionFactory(['vendor' => 'custom', 'redis' => $redis]);
```

## Send message to topic

```php
Expand Down
16 changes: 15 additions & 1 deletion pkg/redis/RedisConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class RedisConnectionFactory implements PsrConnectionFactory
* 'reserved' => should be null if $retry_interval is specified
* 'retry_interval' => retry interval in milliseconds.
* 'vendor' => 'The library used internally to interact with Redis server
* 'redis' => 'Used only if vendor is custom, should contain an instance of \Enqueue\Redis\Redis interface.
* 'persisted' => bool, Whether it use single persisted connection or open a new one for every context
* 'lazy' => the connection will be performed as later as possible, if the option set to true
* 'database' => Database index to select when connected (default value: 0)
Expand All @@ -50,7 +51,7 @@ public function __construct($config = 'redis:')

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

$supportedVendors = ['predis', 'phpredis'];
$supportedVendors = ['predis', 'phpredis', 'custom'];
if (false == in_array($this->config['vendor'], $supportedVendors, true)) {
throw new \LogicException(sprintf(
'Unsupported redis vendor given. It must be either "%s". Got "%s"',
Expand Down Expand Up @@ -90,6 +91,18 @@ private function createRedis()
$this->redis = new PRedis(new Client($this->config, ['exceptions' => true]));
}

if ('custom' == $this->config['vendor'] && false == $this->redis) {
if (empty($this->config['redis'])) {
throw new \LogicException('The redis option should be set if vendor is custom.');
}

if (false == $this->config['redis'] instanceof Redis) {
throw new \LogicException(sprintf('The redis option should be instance of "%s".', Redis::class));
}

$this->redis = $this->config['redis'];
}

$this->redis->connect();
}

Expand Down Expand Up @@ -138,6 +151,7 @@ private function defaultConfig()
'reserved' => null,
'retry_interval' => null,
'vendor' => 'phpredis',
'redis' => null,
'persisted' => false,
'lazy' => true,
'database' => 0,
Expand Down
10 changes: 9 additions & 1 deletion pkg/redis/Symfony/RedisTransportFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,14 @@ public function addConfiguration(ArrayNodeDefinition $builder)
->end()
->integerNode('port')->end()
->enumNode('vendor')
->values(['phpredis', 'predis'])
->values(['phpredis', 'predis', 'custom'])
->cannotBeEmpty()
->info('The library used internally to interact with Redis server')
->end()
->scalarNode('redis')
->cannotBeEmpty()
->info('A custom redis service id, used with vendor true only')
->end()
->booleanNode('persisted')
->defaultFalse()
->info('bool, Whether it use single persisted connection or open a new one for every context')
Expand All @@ -73,6 +77,10 @@ public function addConfiguration(ArrayNodeDefinition $builder)
*/
public function createConnectionFactory(ContainerBuilder $container, array $config)
{
if (false == empty($config['redis'])) {
$config['redis'] = new Reference($config['redis']);
}

$factory = new Definition(RedisConnectionFactory::class);
$factory->setArguments([isset($config['dsn']) ? $config['dsn'] : $config]);

Expand Down
7 changes: 6 additions & 1 deletion pkg/redis/Tests/RedisConnectionFactoryConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function testThrowIfDsnCouldNotBeParsed()
public function testThrowIfVendorIsInvalid()
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Unsupported redis vendor given. It must be either "predis", "phpredis". Got "invalidVendor"');
$this->expectExceptionMessage('Unsupported redis vendor given. It must be either "predis", "phpredis", "custom". Got "invalidVendor"');

new RedisConnectionFactory(['vendor' => 'invalidVendor']);
}
Expand Down Expand Up @@ -72,6 +72,7 @@ public static function provideConfigs()
'persisted' => false,
'lazy' => true,
'database' => 0,
'redis' => null,
],
];

Expand All @@ -87,6 +88,7 @@ public static function provideConfigs()
'persisted' => false,
'lazy' => true,
'database' => 0,
'redis' => null,
],
];

Expand All @@ -102,6 +104,7 @@ public static function provideConfigs()
'persisted' => false,
'lazy' => true,
'database' => 0,
'redis' => null,
],
];

Expand All @@ -118,6 +121,7 @@ public static function provideConfigs()
'lazy' => false,
'foo' => 'bar',
'database' => 5,
'redis' => null,
],
];

Expand All @@ -134,6 +138,7 @@ public static function provideConfigs()
'lazy' => true,
'foo' => 'bar',
'database' => 0,
'redis' => null,
],
];
}
Expand Down
42 changes: 42 additions & 0 deletions pkg/redis/Tests/RedisConnectionFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Enqueue\Redis\Tests;

use Enqueue\Redis\Redis;
use Enqueue\Redis\RedisConnectionFactory;
use Enqueue\Redis\RedisContext;
use Enqueue\Test\ClassExtensionTrait;
Expand All @@ -28,4 +29,45 @@ public function testShouldCreateLazyContext()
$this->assertAttributeEquals(null, 'redis', $context);
$this->assertInternalType('callable', $this->readAttribute($context, 'redisFactory'));
}

public function testShouldThrowIfVendorIsCustomButRedisInstanceNotSet()
{
$factory = new RedisConnectionFactory([
'vendor' => 'custom',
'redis' => null,
'lazy' => false,
]);

$this->expectException(\LogicException::class);
$this->expectExceptionMessage('The redis option should be set if vendor is custom.');
$factory->createContext();
}

public function testShouldThrowIfVendorIsCustomButRedisIsNotInstanceOfRedis()
{
$factory = new RedisConnectionFactory([
'vendor' => 'custom',
'redis' => new \stdClass(),
'lazy' => false,
]);

$this->expectException(\LogicException::class);
$this->expectExceptionMessage('The redis option should be instance of "Enqueue\Redis\Redis".');
$factory->createContext();
}

public function testShouldUseCustomRedisInstance()
{
$redisMock = $this->createMock(Redis::class);

$factory = new RedisConnectionFactory([
'vendor' => 'custom',
'redis' => $redisMock,
'lazy' => false,
]);

$context = $factory->createContext();

$this->assertAttributeSame($redisMock, 'redis', $context);
}
}
29 changes: 29 additions & 0 deletions pkg/redis/Tests/Symfony/RedisTransportFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,35 @@ public function testShouldCreateConnectionFactory()
]], $factory->getArguments());
}

public function testShouldCreateConnectionFactoryWithCustomRedisInstance()
{
$container = new ContainerBuilder();

$transport = new RedisTransportFactory();

$serviceId = $transport->createConnectionFactory($container, [
'host' => 'localhost',
'port' => 123,
'vendor' => 'custom',
'redis' => 'a.redis.service',
]);

$this->assertTrue($container->hasDefinition($serviceId));
$factory = $container->getDefinition($serviceId);
$this->assertEquals(RedisConnectionFactory::class, $factory->getClass());

$config = $factory->getArgument(0);

$this->assertInternalType('array', $config);

$this->assertArrayHasKey('vendor', $config);
$this->assertSame('custom', $config['vendor']);

$this->assertArrayHasKey('redis', $config);
$this->assertInstanceOf(Reference::class, $config['redis']);
$this->assertSame('a.redis.service', (string) $config['redis']);
}

public function testShouldCreateContext()
{
$container = new ContainerBuilder();
Expand Down
24 changes: 16 additions & 8 deletions pkg/simple-client/SimpleClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@
use Interop\Queue\PsrContext;
use Interop\Queue\PsrProcessor;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;

final class SimpleClient
{
/**
* @var ContainerBuilder
* @var ContainerInterface
*/
private $container;

/**
* @var array|string
*/
private $config;

/**
* The config could be a transport DSN (string) or an array, here's an example of a few DSNs:.
*
Expand Down Expand Up @@ -73,11 +79,13 @@ final class SimpleClient
* ]
*
*
* @param string|array $config
* @param string|array $config
* @param ContainerBuilder|null $container
*/
public function __construct($config)
public function __construct($config, ContainerBuilder $container = null)
{
$this->container = $this->buildContainer($config);
$this->container = $this->buildContainer($config, $container ?: new ContainerBuilder());
$this->config = $config;
}

/**
Expand Down Expand Up @@ -252,16 +260,16 @@ public function getRouterProcessor()
}

/**
* @param array|string $config
* @param array|string $config
* @param ContainerBuilder $container
*
* @return ContainerBuilder
* @return ContainerInterface
*/
private function buildContainer($config)
private function buildContainer($config, ContainerBuilder $container)
{
$config = $this->buildConfig($config);
$extension = $this->buildContainerExtension();

$container = new ContainerBuilder();
$container->registerExtension($extension);
$container->loadFromExtension($extension->getAlias(), $config);

Expand Down