Skip to content

Commit

Permalink
support for Heroku style REDIS_URL env variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Klaus Großmann committed Jun 12, 2017
1 parent abfb9da commit 29290d9
Show file tree
Hide file tree
Showing 10 changed files with 483 additions and 21 deletions.
8 changes: 7 additions & 1 deletion DependencyInjection/Configuration/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ private function addClientsSection(ArrayNodeDefinition $rootNode)
return array_map(function($dsn) {
$parsed = new RedisDsn($dsn);

return $parsed->isValid() ? $parsed : $dsn;
if ($parsed->isValid()) {
return $parsed;
}

$parsedEnv = new RedisEnvDsn($dsn);

return $parsedEnv->isValid() ? $parsedEnv : $dsn;
}, $v);
})
->end()
Expand Down
2 changes: 1 addition & 1 deletion DependencyInjection/Configuration/RedisDsn.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* RedisDsn
*/
class RedisDsn
class RedisDsn implements RedisDsnInterface
{
/**
* @var string
Expand Down
16 changes: 16 additions & 0 deletions DependencyInjection/Configuration/RedisDsnInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Snc\RedisBundle\DependencyInjection\Configuration;

interface RedisDsnInterface
{
/**
* @return bool
*/
public function isValid();

/**
* @return string
*/
public function getAlias();
}
43 changes: 43 additions & 0 deletions DependencyInjection/Configuration/RedisEnvDsn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Snc\RedisBundle\DependencyInjection\Configuration;

class RedisEnvDsn implements RedisDsnInterface
{
/**
* @var string
*/
private $dsn;

/**
* @param string $dsn
*/
public function __construct($dsn)
{
$this->dsn = $dsn;
}

/**
* @return bool
*/
public function isValid()
{
return (bool)preg_match('#^env_\w+_[0-9a-fA-F]{32}$#', $this->dsn);
}

/**
* @return null
*/
public function getAlias()
{
return null;
}

/**
* @return string
*/
public function getDsn()
{
return $this->dsn;
}
}
69 changes: 50 additions & 19 deletions DependencyInjection/SncRedisExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
namespace Snc\RedisBundle\DependencyInjection;

use Snc\RedisBundle\DependencyInjection\Configuration\Configuration;
use Snc\RedisBundle\DependencyInjection\Configuration\RedisDsn;
use Snc\RedisBundle\DependencyInjection\Configuration\RedisDsnInterface;
use Snc\RedisBundle\DependencyInjection\Configuration\RedisEnvDsn;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

Expand Down Expand Up @@ -132,32 +134,40 @@ protected function loadPredisClient(array $client, ContainerBuilder $container)

$connectionAliases = array();
$connectionCount = count($client['dsns']);
/** @var RedisDsnInterface $dsn */
foreach ($client['dsns'] as $i => $dsn) {
/** @var \Snc\RedisBundle\DependencyInjection\Configuration\RedisDsn $dsn */
if (!$connectionAlias = $dsn->getAlias()) {
$connectionAlias = 1 === $connectionCount ? $client['alias'] : $client['alias'] . ($i + 1);
}
$connectionAliases[] = $connectionAlias;

$connection = $client['options'];
$connection['logging'] = $client['logging'];
$connection['alias'] = $connectionAlias;
if (null !== $dsn->getSocket()) {
$connection['scheme'] = 'unix';
$connection['path'] = $dsn->getSocket();
} else {
$connection['scheme'] = 'tcp';
$connection['host'] = $dsn->getHost();
$connection['port'] = $dsn->getPort();

if ($dsn instanceof RedisDsn) {
if (null !== $dsn->getSocket()) {
$connection['scheme'] = 'unix';
$connection['path'] = $dsn->getSocket();
} else {
$connection['scheme'] = 'tcp';
$connection['host'] = $dsn->getHost();
$connection['port'] = $dsn->getPort();
if (null !== $dsn->getDatabase()) {
$connection['path'] = $dsn->getDatabase();
}
}
if (null !== $dsn->getDatabase()) {
$connection['path'] = $dsn->getDatabase();
$connection['database'] = $dsn->getDatabase();
}
$connection['password'] = $dsn->getPassword();
$connection['weight'] = $dsn->getWeight();

$this->loadPredisConnectionParameters($client['alias'], $connection, $container);
}
if (null !== $dsn->getDatabase()) {
$connection['database'] = $dsn->getDatabase();
if ($dsn instanceof RedisEnvDsn) {
$this->loadEnvPredisConnectionParameters($client['alias'], $connection, $dsn->getDsn(), $container);
}
$connection['password'] = $dsn->getPassword();
$connection['weight'] = $dsn->getWeight();
$this->loadPredisConnectionParameters($client['alias'], $connection, $container);
}

// TODO can be shared between clients?!
Expand Down Expand Up @@ -212,6 +222,27 @@ protected function loadPredisConnectionParameters($clientAlias, array $connectio
$container->setDefinition($parameterId, $parameterDef);
}

/**
* @param string $clientAlias The client alias
* @param array $connection A connection configuration
* @param string $dsn The DSN placeholder
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function loadEnvPredisConnectionParameters($clientAlias, array $connection, $dsn, ContainerBuilder $container)
{
$parametersClass = $container->getParameter('snc_redis.connection_parameters.class');

$parameterId = sprintf('snc_redis.connection.%s_parameters.%s', $connection['alias'], $clientAlias);
$parameterDef = new Definition($parametersClass);
$parameterDef->setPublic(false);
$parameterDef->setFactory(array('Snc\RedisBundle\Factory\EnvParametersFactory', 'create'));
$parameterDef->addArgument($connection);
$parameterDef->addArgument($parametersClass);
$parameterDef->addArgument($dsn);
$parameterDef->addTag('snc_redis.connection_parameters', array('clientAlias' => $clientAlias));
$container->setDefinition($parameterId, $parameterDef);
}

/**
* Loads a redis client using phpredis.
*
Expand All @@ -228,7 +259,8 @@ protected function loadPhpredisClient(array $client, ContainerBuilder $container
throw new \RuntimeException('Support for RedisArray is not yet implemented.');
}

$dsn = $client['dsns'][0]; /** @var \Snc\RedisBundle\DependencyInjection\Configuration\RedisDsn $dsn */
$dsn = $client['dsns'][0];
/** @var \Snc\RedisBundle\DependencyInjection\Configuration\RedisDsn $dsn */
$phpredisId = sprintf('snc_redis.phpredis.%s', $client['alias']);

$phpredisDef = new Definition($container->getParameter('snc_redis.phpredis_client.class'));
Expand All @@ -251,11 +283,10 @@ protected function loadPhpredisClient(array $client, ContainerBuilder $container
}
if ($client['options']['connection_timeout']) {
$connectParameters[] = $client['options']['connection_timeout'];
}
else {
} else {
$connectParameters[] = null;
}
if($client['options']['connection_persistent']) {
if ($client['options']['connection_persistent']) {
$connectParameters[] = $dsn->getPersistentId();
}

Expand Down
30 changes: 30 additions & 0 deletions Factory/EnvParametersFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Snc\RedisBundle\Factory;

use Predis\Connection\ParametersInterface;

class EnvParametersFactory
{
/**
* @param array $options
* @param string $class
* @param string $dsn
*
* @return ParametersInterface
*/
public static function create($options, $class, $dsn)
{
$callable = [$class, 'parse'];

if(!is_callable($callable)) {
$alias = isset($options['alias']) ? $options['alias'] : 'the client';

throw new \InvalidArgumentException(sprintf('The parameters class you defined for %s does not support parsing url like DSNs.', $alias));
}

$dsnOptions = call_user_func($callable, $dsn);

return new $class(array_merge($options, $dsnOptions));
}
}
1 change: 1 addition & 0 deletions Tests/DependencyInjection/Configuration/RedisDsnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ public static function isValidValues()
array('localhost', false),
array('localhost/1', false),
array('pw@localhost:63790/10', false),
array('env_REDIS_URL_e07910a06a086c83ba41827aa00b26ed', false),
);
}

Expand Down
78 changes: 78 additions & 0 deletions Tests/DependencyInjection/Configuration/RedisEnvDsnTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Snc\RedisBundle\Tests\DependencyInjection\Configuration;

use Snc\RedisBundle\DependencyInjection\Configuration\RedisEnvDsn;

class RedisEnvDsnTest extends \PHPUnit_Framework_TestCase
{
/**
* @static
*
* @return array
*/
public static function isValidValues()
{
return array(
array('redis://localhost', false),
array('redis://localhost/1', false),
array('redis://pw@localhost:63790/10', false),
array('redis://127.0.0.1', false),
array('redis://127.0.0.1/1', false),
array('redis://pw@127.0.0.1:63790/10', false),
array('redis:///redis.sock', false),
array('redis:///redis.sock/1', false),
array('redis://pw@/redis.sock/10', false),
array('redis://pw@/redis.sock/10', false),
array('redis://%redis_host%', false),
array('redis://%redis_host%/%redis_db%', false),
array('redis://%redis_host%:%redis_port%', false),
array('redis://%redis_host%:%redis_port%/%redis_db%', false),
array('redis://%redis_pass%@%redis_host%:%redis_port%/%redis_db%', false),
array('redis://env_REDIS_HOST_1ef60d9ef7a55747f99d0a42206e58ed', false),
array('redis://env_REDIS_HOST_1ef60d9ef7a55747f99d0a42206e58ed/env_REDIS_DB_0d1da5bfb707f91e21a1f78cd11fcd0a', false),
array('redis://env_REDIS_HOST_1ef60d9ef7a55747f99d0a42206e58ed:env_REDIS_PORT_0458150d4bf631c8ac63b0fa4d257a21', false),
array('redis://env_REDIS_HOST_1ef60d9ef7a55747f99d0a42206e58ed:env_REDIS_PORT_0458150d4bf631c8ac63b0fa4d257a21/env_REDIS_DB_0d1da5bfb707f91e21a1f78cd11fcd0a', false),
array('redis://env_REDIS_PW_e7406513a853fd4692343d101baecb7c@env_REDIS_HOST_1ef60d9ef7a55747f99d0a42206e58ed:env_REDIS_PORT_0458150d4bf631c8ac63b0fa4d257a21/env_REDIS_DB_0d1da5bfb707f91e21a1f78cd11fcd0a', false),
array('localhost', false),
array('localhost/1', false),
array('pw@localhost:63790/10', false),
array('env_REDIS_URL_z07910a06a086c83ba41827aa00b26ed', false),
array('env_REDIS_URL_e07910a06a086c83ba41827aa00b26ed', true),
);
}

/**
* @param string $dsn DSN
* @param bool $valid Valid
*
* @dataProvider isValidValues
*/
public function testIsValid($dsn, $valid)
{
$dsn = new RedisEnvDsn($dsn);
$this->assertSame($valid, $dsn->isValid());
}

/**
* @param string $dsn DSN
*
* @dataProvider isValidValues
*/
public function testAliasIsNull($dsn)
{
$dsn = new RedisEnvDsn($dsn);
$this->assertNull($dsn->getAlias());
}

/**
* @param string $dsn DSN
*
* @dataProvider isValidValues
*/
public function testDsnIsUnmodified($providedDsn)
{
$dsn = new RedisEnvDsn($providedDsn);
$this->assertEquals($providedDsn, $dsn->getDsn());
}
}
Loading

0 comments on commit 29290d9

Please sign in to comment.