diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 752d7180..f4057634 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -18,11 +18,18 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
- extensions: "relay"
+ extensions: "redis, relay"
php-version: "8.4"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
+ - name: "Compile Symfony container"
+ run: |
+ php -r "
+ require 'vendor/autoload.php';
+ (new Snc\RedisBundle\Tests\Functional\App\Kernel('test', true))->boot();
+ "
+
- name: "Run a static analysis with vimeo/psalm"
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
diff --git a/composer.json b/composer.json
index 4c4d41c0..2f3e74d3 100644
--- a/composer.json
+++ b/composer.json
@@ -37,6 +37,7 @@
"phpunit/phpunit": "^9.6.34 || ^10.5.63",
"predis/predis": "^3.1",
"psalm/plugin-phpunit": "^0.19.3",
+ "psalm/plugin-symfony": "^5.0",
"psr/http-message": "^2",
"seec/phpunit-consecutive-params": "^1.1.4",
"symfony/browser-kit": "^6.4 || ^7.3 || ^8.0",
@@ -49,7 +50,7 @@
"symfony/twig-bundle": "^6.4 || ^7.3 || ^8.0",
"symfony/web-profiler-bundle": "^6.4 || ^7.3 || ^8.0",
"symfony/yaml": "^6.4 || ^7.3 || ^8.0",
- "vimeo/psalm": "^5 || ^6 || ^7"
+ "vimeo/psalm": "^6 || ^7"
},
"conflict": {
"ext-redis": "<6.1.0",
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index d13aefab..fcb84265 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -7,6 +7,7 @@
+
diff --git a/psalm.xml.dist b/psalm.xml.dist
index c7f96af7..7803297a 100644
--- a/psalm.xml.dist
+++ b/psalm.xml.dist
@@ -1,6 +1,7 @@
+
+
+
+
+
+
+ tests/Functional/App/var/cache/test/Snc_RedisBundle_Tests_Functional_App_KernelTestDebugContainer.xml
+
diff --git a/src/DependencyInjection/Compiler/LoggingPass.php b/src/DependencyInjection/Compiler/LoggingPass.php
index 6b8cf7b7..65711ef1 100644
--- a/src/DependencyInjection/Compiler/LoggingPass.php
+++ b/src/DependencyInjection/Compiler/LoggingPass.php
@@ -45,7 +45,7 @@ public function process(ContainerBuilder $container): void
$arguments = $option->getArgument(0);
$connectionFactoryId = sprintf('snc_redis.%s_connectionfactory', $clientAlias);
- $connectionFactoryDef = new Definition((string) $container->getParameter('snc_redis.connection_factory.class'));
+ $connectionFactoryDef = new Definition($container->getParameter('snc_redis.connection_factory.class'));
if ($container->getParameter('kernel.debug')) {
$connectionFactoryDef->addMethodCall('setStopwatch', [new Reference('debug.stopwatch', ContainerInterface::NULL_ON_INVALID_REFERENCE)]);
}
diff --git a/src/DependencyInjection/SncRedisExtension.php b/src/DependencyInjection/SncRedisExtension.php
index be4d6cdb..b7ec4d66 100644
--- a/src/DependencyInjection/SncRedisExtension.php
+++ b/src/DependencyInjection/SncRedisExtension.php
@@ -180,12 +180,13 @@ private function loadPredisClient(array $client, ContainerBuilder $container): v
}
$optionId = sprintf('snc_redis.client.%s_options', $client['alias']);
- $optionDef = new Definition((string) $container->getParameter('snc_redis.client_options.class'));
+ $optionDef = new Definition($container->getParameter('snc_redis.client_options.class'));
$optionDef->addArgument($client['options']);
$container->setDefinition($optionId, $optionDef);
- $clientDef = new Definition($client['class'] ?? (string) $container->getParameter('snc_redis.client.class'));
+ $clientDef = new Definition($client['class'] ?? $container->getParameter('snc_redis.client.class'));
$clientDef->addTag('snc_redis.client', ['alias' => $client['alias']]);
- if ($connectionCount === 1 && !isset($client['options']['cluster']) && !isset($client['options']['replication'])) {
+
+ if ($connectionCount === 1 && !isset($client['options']['replication'])) {
$clientDef->addArgument(new Reference(sprintf('snc_redis.connection.%s_parameters.%s', $connectionAliases[0], $client['alias'])));
} else {
$connections = [];
@@ -206,7 +207,7 @@ private function loadPredisClient(array $client, ContainerBuilder $container): v
*/
private function loadPredisConnectionParameters(string $clientAlias, array $options, ContainerBuilder $container, object $dsn): void
{
- $parametersClass = (string) $container->getParameter('snc_redis.connection_parameters.class');
+ $parametersClass = $container->getParameter('snc_redis.connection_parameters.class');
$parameterId = sprintf('snc_redis.connection.%s_parameters.%s', $options['alias'], $clientAlias);
$parameterDef = new Definition($parametersClass);
@@ -261,7 +262,7 @@ private function loadMonolog(array $config, ContainerBuilder $container): void
{
$ref = new Reference(sprintf('snc_redis.%s', $config['monolog']['client']));
- $def = new Definition((string) $container->getParameter('snc_redis.monolog_handler.class'), [
+ $def = new Definition($container->getParameter('snc_redis.monolog_handler.class'), [
$ref,
$config['monolog']['key'],
]);
@@ -277,6 +278,6 @@ private function loadMonolog(array $config, ContainerBuilder $container): void
#[Override]
public function getConfiguration(array $config, ContainerBuilder $container): ConfigurationInterface
{
- return new Configuration((bool) $container->getParameter('kernel.debug'));
+ return new Configuration($container->getParameter('kernel.debug'));
}
}
diff --git a/src/Factory/PredisParametersFactory.php b/src/Factory/PredisParametersFactory.php
index 637fcee0..7e10614a 100644
--- a/src/Factory/PredisParametersFactory.php
+++ b/src/Factory/PredisParametersFactory.php
@@ -9,21 +9,51 @@
use Snc\RedisBundle\DependencyInjection\Configuration\RedisDsn;
use function array_filter;
+use function array_map;
use function array_merge;
use function constant;
+use function count;
use function defined;
use function is_a;
+use function is_array;
+use function is_string;
use function sprintf;
use function str_replace;
/** @internal */
class PredisParametersFactory
{
+ /**
+ * @param class-string $class
+ * @param array $options
+ * @param string|list|list> $dsn
+ *
+ * @return ParametersInterface|list
+ */
+ public static function create(array $options, string $class, string|array $dsn): ParametersInterface|array
+ {
+ if (is_string($dsn)) {
+ $dsn = [$dsn];
+ }
+
+ // json:/csv: env processors can produce a single-element array wrapping the actual list
+ if (count($dsn) === 1 && is_array($dsn[0])) {
+ $dsn = $dsn[0];
+ }
+
+ $parameters = array_map(
+ static fn (string $d) => static::createFromSingleDsn($options, $class, $d),
+ $dsn,
+ );
+
+ return count($parameters) === 1 ? $parameters[0] : $parameters;
+ }
+
/**
* @param class-string $class
* @param array $options
*/
- public static function create(array $options, string $class, string $dsn): ParametersInterface
+ private static function createFromSingleDsn(array $options, string $class, string $dsn): ParametersInterface
{
if (!is_a($class, ParametersInterface::class, true)) {
throw new InvalidArgumentException(sprintf('%s::%s requires $class argument to implement %s', self::class, __METHOD__, ParametersInterface::class));
diff --git a/tests/Factory/PredisParametersFactoryTest.php b/tests/Factory/PredisParametersFactoryTest.php
index f9420f8c..c354cea0 100644
--- a/tests/Factory/PredisParametersFactoryTest.php
+++ b/tests/Factory/PredisParametersFactoryTest.php
@@ -185,4 +185,28 @@ public function testCreateMergesSslWithTlsVersion(): void
$this->assertSame(STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, $ssl['crypto_type']);
$this->assertFalse($ssl['verify_peer']);
}
+
+ /**
+ * @testWith ["redis://localhost"]
+ * [["redis://localhost"]]
+ * [[["redis://localhost"]]]
+ */
+ public function testCreateReturnsSingleParameters(string|array $dsn): void
+ {
+ $result = PredisParametersFactory::create([], Parameters::class, $dsn);
+ $this->assertInstanceOf(Parameters::class, $result);
+ }
+
+ /**
+ * @param array> $dsn
+ *
+ * @testWith [["redis://host1", "redis://host2"]]
+ * [[["redis://host1", "redis://host2"]]]
+ */
+ public function testCreateReturnsMultipleParameters(array $dsn): void
+ {
+ $result = PredisParametersFactory::create([], Parameters::class, $dsn);
+ $this->assertIsArray($result);
+ $this->assertCount(2, $result);
+ }
}
diff --git a/tests/Functional/App/Controller/Controller.php b/tests/Functional/App/Controller/Controller.php
index d0193daa..3136e9cb 100644
--- a/tests/Functional/App/Controller/Controller.php
+++ b/tests/Functional/App/Controller/Controller.php
@@ -13,18 +13,28 @@
namespace Snc\RedisBundle\Tests\Functional\App\Controller;
+use Predis\ClientInterface;
use Redis;
+use RedisArray;
+use Relay\Relay;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
class Controller extends AbstractController
{
- public function __invoke(Redis $redis): JsonResponse
+ /** @param iterable $clients */
+ public function __construct(private iterable $clients)
{
- $redis->set('foo', 'bar');
+ }
+
+ public function __invoke(): JsonResponse
+ {
+ $result = null;
+ foreach ($this->clients as $client) {
+ $client->set('foo', 'bar');
+ $result = $client->get('foo');
+ }
- return new JsonResponse([
- 'result' => $redis->get('foo'),
- ]);
+ return new JsonResponse(['result' => $result]);
}
}
diff --git a/tests/Functional/App/config.yaml b/tests/Functional/App/config.yaml
index 64e08d7a..38a3bfc4 100644
--- a/tests/Functional/App/config.yaml
+++ b/tests/Functional/App/config.yaml
@@ -35,11 +35,9 @@ snc_redis:
cluster:
type: predis
alias: cluster
- dsn:
- - redis://sncredis@127.0.0.1/3
- - redis://sncredis@127.0.0.1/4
- - redis://sncredis@127.0.0.1/5
+ dsn: "%env(json:REDIS_DSNS)%"
options:
+ cluster: predis
prefix: foo
connection_timeout: 10
connection_persistent: true
@@ -59,20 +57,14 @@ snc_redis:
with_acl:
type: predis
alias: with_acl
- dsn: redis://localhost/1
+ dsn: redis://snc_redis:snc_password@localhost:7099/1
logging: false
- options:
- parameters:
- username: my_user
- password: sncredis
services:
_defaults:
autowire: true
autoconfigure: true
public: false
- bind:
- Predis\Client $predisReplication: '@snc_redis.predis_replication'
Redis: '@snc_redis.default'
@@ -87,6 +79,14 @@ services:
resource: './Controller'
tags: ['controller.service_arguments']
+ Snc\RedisBundle\Tests\Functional\App\Controller\PredisReplication:
+ bind:
+ Predis\Client $predisReplication: '@snc_redis.predis_replication'
+
+ Snc\RedisBundle\Tests\Functional\App\Controller\Controller:
+ arguments:
+ $clients: !tagged_iterator snc_redis.client
+
var_dumper.cli_dumper:
class: Symfony\Component\VarDumper\Dumper\CliDumper
arguments: ['/dev/null']
\ No newline at end of file
diff --git a/tests/Functional/IntegrationTest.php b/tests/Functional/IntegrationTest.php
index a806c8d3..06080b55 100644
--- a/tests/Functional/IntegrationTest.php
+++ b/tests/Functional/IntegrationTest.php
@@ -69,8 +69,8 @@ public function testIntegration(): void
$this->assertInstanceOf(RedisDataCollector::class, $collector);
$this->assertInstanceOf(ResetInterface::class, $container = $this->client->getKernel()->getContainer());
$this->assertInstanceOf(RedisLogger::class, $redisLogger = $container->get('test.snc_redis.logger'));
- $this->assertSame(5, $redisLogger->getNbCommands());
- $this->assertCount(5, $collector->getCommands());
+ $this->assertSame(13, $redisLogger->getNbCommands());
+ $this->assertCount(13, $collector->getCommands());
$container->reset();
$this->assertSame(0, $redisLogger->getNbCommands());
}