From b8778375607a95e823edfcd943a1487828b7a540 Mon Sep 17 00:00:00 2001 From: Konstantin Chigakov Date: Sun, 7 Jul 2024 06:41:48 +0400 Subject: [PATCH] [Cache] Improve `dbindex` DSN parameter parsing --- .../Cache/Tests/Traits/RedisTraitTest.php | 75 +++++++++++++++++++ .../Component/Cache/Traits/RedisTrait.php | 8 +- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php b/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php index 650a0c71c1c2e..9373ff6c9217f 100644 --- a/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php +++ b/src/Symfony/Component/Cache/Tests/Traits/RedisTraitTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\SkippedTestSuiteError; use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Traits\RedisTrait; /** @@ -133,4 +134,78 @@ public function testPconnectSelectsCorrectDatabase() ini_set('redis.pconnect.connection_limit', $prevPoolSize); } } + + /** + * @dataProvider provideDbIndexDsnParameter + */ + public function testDbIndexDsnParameter(string $dsn, int $expectedDb) + { + if (!getenv('REDIS_AUTHENTICATED_HOST')) { + self::markTestSkipped('REDIS_AUTHENTICATED_HOST env var is not defined.'); + } + + $mock = new class () { + use RedisTrait; + }; + $connection = $mock::createConnection($dsn); + self::assertSame($expectedDb, $connection->getDbNum()); + } + + public static function provideDbIndexDsnParameter(): array + { + return [ + [ + 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST'), + 0, + ], + [ + 'redis:?host['.getenv('REDIS_HOST').']', + 0, + ], + [ + 'redis:?host['.getenv('REDIS_HOST').']&dbindex=1', + 1, + ], + [ + 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'?dbindex=2', + 2, + ], + [ + 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/4', + 4, + ], + [ + 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/?dbindex=5', + 5, + ], + ]; + } + + /** + * @dataProvider provideInvalidDbIndexDsnParameter + */ + public function testInvalidDbIndexDsnParameter(string $dsn) + { + if (!getenv('REDIS_AUTHENTICATED_HOST')) { + self::markTestSkipped('REDIS_AUTHENTICATED_HOST env var is not defined.'); + } + $this->expectException(InvalidArgumentException::class); + + $mock = new class () { + use RedisTrait; + }; + $mock::createConnection($dsn); + } + + public static function provideInvalidDbIndexDsnParameter(): array + { + return [ + [ + 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/abc' + ], + [ + 'redis://:p%40ssword@'.getenv('REDIS_AUTHENTICATED_HOST').'/3?dbindex=6' + ] + ]; + } } diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 518a563060240..129254bd68c7a 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -152,10 +152,10 @@ public static function createConnection(string $dsn, array $options = []) if (isset($params['host']) || isset($params['path'])) { if (!isset($params['dbindex']) && isset($params['path'])) { if (preg_match('#/(\d+)?$#', $params['path'], $m)) { - $params['dbindex'] = $m[1] ?? '0'; + $params['dbindex'] = $m[1] ?? $query['dbindex'] ?? '0'; $params['path'] = substr($params['path'], 0, -\strlen($m[0])); } elseif (isset($params['host'])) { - throw new InvalidArgumentException('Invalid Redis DSN: query parameter "dbindex" must be a number.'); + throw new InvalidArgumentException('Invalid Redis DSN: parameter "dbindex" must be a number.'); } } @@ -170,6 +170,10 @@ public static function createConnection(string $dsn, array $options = []) throw new InvalidArgumentException('Invalid Redis DSN: missing host.'); } + if (isset($params['dbindex'], $query['dbindex']) && $params['dbindex'] !== $query['dbindex']) { + throw new InvalidArgumentException('Invalid Redis DSN: path and query "dbindex" parameters mismatch.'); + } + $params += $query + $options + self::$defaultConnectionOptions; if (isset($params['redis_sentinel']) && !class_exists(\Predis\Client::class) && !class_exists(\RedisSentinel::class)) {