From 8ca27e19d0a7147b2ef64fd60849db6655518942 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 9 Nov 2024 11:52:09 +0100 Subject: [PATCH] silence PHP warnings issued by Redis::connect() --- .../Redis/Tests/Transport/ConnectionTest.php | 84 ++++++++++++------- .../Bridge/Redis/Transport/Connection.php | 16 +++- 2 files changed, 68 insertions(+), 32 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php index b1bff95fe4b67..74a675d866bf1 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php @@ -37,8 +37,8 @@ public function testFromDsn() new Connection(['stream' => 'queue', 'delete_after_ack' => true], [ 'host' => 'localhost', 'port' => 6379, - ], [], $this->createMock(\Redis::class)), - Connection::fromDsn('redis://localhost/queue?delete_after_ack=1', [], $this->createMock(\Redis::class)) + ], [], $this->createRedisMock()), + Connection::fromDsn('redis://localhost/queue?delete_after_ack=1', [], $this->createRedisMock()) ); } @@ -48,24 +48,24 @@ public function testFromDsnOnUnixSocket() new Connection(['stream' => 'queue', 'delete_after_ack' => true], [ 'host' => '/var/run/redis/redis.sock', 'port' => 0, - ], [], $redis = $this->createMock(\Redis::class)), - Connection::fromDsn('redis:///var/run/redis/redis.sock', ['stream' => 'queue', 'delete_after_ack' => true], $redis) + ], [], $this->createRedisMock()), + Connection::fromDsn('redis:///var/run/redis/redis.sock', ['stream' => 'queue', 'delete_after_ack' => true], $this->createRedisMock()) ); } public function testFromDsnWithOptions() { $this->assertEquals( - Connection::fromDsn('redis://localhost', ['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false, 'serializer' => 2, 'delete_after_ack' => true], $this->createMock(\Redis::class)), - Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&auto_setup=0&delete_after_ack=1', [], $this->createMock(\Redis::class)) + Connection::fromDsn('redis://localhost', ['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false, 'serializer' => 2, 'delete_after_ack' => true], $this->createRedisMock()), + Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&auto_setup=0&delete_after_ack=1', [], $this->createRedisMock()) ); } public function testFromDsnWithOptionsAndTrailingSlash() { $this->assertEquals( - Connection::fromDsn('redis://localhost/', ['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false, 'serializer' => 2, 'delete_after_ack' => true], $this->createMock(\Redis::class)), - Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&auto_setup=0&delete_after_ack=1', [], $this->createMock(\Redis::class)) + Connection::fromDsn('redis://localhost/', ['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false, 'serializer' => 2, 'delete_after_ack' => true], $this->createRedisMock()), + Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&auto_setup=0&delete_after_ack=1', [], $this->createRedisMock()) ); } @@ -79,6 +79,9 @@ public function testFromDsnWithTls() ->method('connect') ->with('tls://127.0.0.1', 6379) ->willReturn(true); + $redis->expects($this->any()) + ->method('isConnected') + ->willReturnOnConsecutiveCalls(false, true); Connection::fromDsn('redis://127.0.0.1?tls=1', [], $redis); } @@ -93,6 +96,9 @@ public function testFromDsnWithTlsOption() ->method('connect') ->with('tls://127.0.0.1', 6379) ->willReturn(true); + $redis->expects($this->any()) + ->method('isConnected') + ->willReturnOnConsecutiveCalls(false, true); Connection::fromDsn('redis://127.0.0.1', ['tls' => true], $redis); } @@ -104,6 +110,9 @@ public function testFromDsnWithRedissScheme() ->method('connect') ->with('tls://127.0.0.1', 6379) ->willReturn(true); + $redis->expects($this->any()) + ->method('isConnected') + ->willReturnOnConsecutiveCalls(false, true); Connection::fromDsn('rediss://127.0.0.1?delete_after_ack=true', [], $redis); } @@ -116,21 +125,21 @@ public function testFromDsnWithQueryOptions() 'port' => 6379, ], [ 'serializer' => 2, - ], $this->createMock(\Redis::class)), - Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&delete_after_ack=1', [], $this->createMock(\Redis::class)) + ], $this->createRedisMock()), + Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&delete_after_ack=1', [], $this->createRedisMock()) ); } public function testFromDsnWithMixDsnQueryOptions() { $this->assertEquals( - Connection::fromDsn('redis://localhost/queue/group1?serializer=2', ['consumer' => 'specific-consumer', 'delete_after_ack' => true], $this->createMock(\Redis::class)), - Connection::fromDsn('redis://localhost/queue/group1/specific-consumer?serializer=2&delete_after_ack=1', [], $this->createMock(\Redis::class)) + Connection::fromDsn('redis://localhost/queue/group1?serializer=2', ['consumer' => 'specific-consumer', 'delete_after_ack' => true], $this->createRedisMock()), + Connection::fromDsn('redis://localhost/queue/group1/specific-consumer?serializer=2&delete_after_ack=1', [], $this->createRedisMock()) ); $this->assertEquals( - Connection::fromDsn('redis://localhost/queue/group1/consumer1', ['consumer' => 'specific-consumer', 'delete_after_ack' => true], $this->createMock(\Redis::class)), - Connection::fromDsn('redis://localhost/queue/group1/consumer1', ['delete_after_ack' => true], $this->createMock(\Redis::class)) + Connection::fromDsn('redis://localhost/queue/group1/consumer1', ['consumer' => 'specific-consumer', 'delete_after_ack' => true], $this->createRedisMock()), + Connection::fromDsn('redis://localhost/queue/group1/consumer1', ['delete_after_ack' => true], $this->createRedisMock()) ); } @@ -140,7 +149,7 @@ public function testFromDsnWithMixDsnQueryOptions() public function testDeprecationIfInvalidOptionIsPassedWithDsn() { $this->expectDeprecation('Since symfony/messenger 5.1: Invalid option(s) "foo" passed to the Redis Messenger transport. Passing invalid options is deprecated.'); - Connection::fromDsn('redis://localhost/queue?foo=bar', [], $this->createMock(\Redis::class)); + Connection::fromDsn('redis://localhost/queue?foo=bar', [], $this->createRedisMock()); } public function testRedisClusterInstanceIsSupported() @@ -151,7 +160,7 @@ public function testRedisClusterInstanceIsSupported() public function testKeepGettingPendingMessages() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(3))->method('xreadgroup') ->with('symfony', 'consumer', ['queue' => 0], 1, 1) @@ -170,7 +179,7 @@ public function testKeepGettingPendingMessages() */ public function testAuth($expected, string $dsn) { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('auth') ->with($expected) @@ -190,7 +199,7 @@ public static function provideAuthDsn(): \Generator public function testAuthFromOptions() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('auth') ->with('password') @@ -201,7 +210,7 @@ public function testAuthFromOptions() public function testAuthFromOptionsAndDsn() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('auth') ->with('password2') @@ -212,7 +221,7 @@ public function testAuthFromOptionsAndDsn() public function testNoAuthWithEmptyPassword() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(0))->method('auth') ->with('') @@ -223,7 +232,7 @@ public function testNoAuthWithEmptyPassword() public function testAuthZeroPassword() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('auth') ->with('0') @@ -236,7 +245,7 @@ public function testFailedAuth() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Redis connection '); - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('auth') ->with('password') @@ -247,7 +256,7 @@ public function testFailedAuth() public function testGetPendingMessageFirst() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('xreadgroup') ->with('symfony', 'consumer', ['queue' => '0'], 1, 1) @@ -269,7 +278,7 @@ public function testGetPendingMessageFirst() public function testClaimAbandonedMessageWithRaceCondition() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(3))->method('xreadgroup') ->willReturnCallback(function (...$args) { @@ -305,7 +314,7 @@ public function testClaimAbandonedMessageWithRaceCondition() public function testClaimAbandonedMessage() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(2))->method('xreadgroup') ->willReturnCallback(function (...$args) { @@ -341,7 +350,7 @@ public function testUnexpectedRedisError() { $this->expectException(TransportException::class); $this->expectExceptionMessage('Redis error happens'); - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->once())->method('xreadgroup')->willReturn(false); $redis->expects($this->once())->method('getLastError')->willReturn('Redis error happens'); @@ -351,7 +360,7 @@ public function testUnexpectedRedisError() public function testMaxEntries() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('xadd') ->with('queue', '*', ['message' => '{"body":"1","headers":[]}'], 20000, true) @@ -363,7 +372,7 @@ public function testMaxEntries() public function testDeleteAfterAck() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('xack') ->with('queue', 'symfony', ['1']) @@ -383,12 +392,12 @@ public function testLegacyOmitDeleteAfterAck() { $this->expectDeprecation('Since symfony/redis-messenger 5.4: Not setting the "delete_after_ack" boolean option explicitly is deprecated, its default value will change to true in 6.0.'); - Connection::fromDsn('redis://localhost/queue', [], $this->createMock(\Redis::class)); + Connection::fromDsn('redis://localhost/queue', [], $this->createRedisMock(\Redis::class)); } public function testDeleteAfterReject() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->exactly(1))->method('xack') ->with('queue', 'symfony', ['1']) @@ -403,7 +412,7 @@ public function testDeleteAfterReject() public function testLastErrorGetsCleared() { - $redis = $this->createMock(\Redis::class); + $redis = $this->createRedisMock(); $redis->expects($this->once())->method('xadd')->willReturn('0'); $redis->expects($this->once())->method('xack')->willReturn(0); @@ -427,4 +436,17 @@ public function testLastErrorGetsCleared() $this->assertSame('xack error', $e->getMessage()); } + + private function createRedisMock(): \Redis + { + $redis = $this->createMock(\Redis::class); + $redis->expects($this->any()) + ->method('connect') + ->willReturn(true); + $redis->expects($this->any()) + ->method('isConnected') + ->willReturnOnConsecutiveCalls(false, true); + + return $redis; + } } diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index a5e1c21707a78..d1c6ede8d2ce4 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -121,7 +121,21 @@ private static function initializeRedis(\Redis $redis, string $host, int $port, return $redis; } - $redis->connect($host, $port); + @$redis->connect($host, $port); + + $error = null; + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + + try { + $isConnected = $redis->isConnected(); + } finally { + restore_error_handler(); + } + + if (!$isConnected) { + throw new InvalidArgumentException('Redis connection failed: '.(preg_match('/^Redis::p?connect\(\): (.*)/', $error ?? $redis->getLastError() ?? '', $matches) ? \sprintf(' (%s)', $matches[1]) : '')); + } + $redis->setOption(\Redis::OPT_SERIALIZER, $serializer); if (null !== $auth && !$redis->auth($auth)) {