Skip to content

Commit b2bbf43

Browse files
committed
Waiting client
1 parent f592969 commit b2bbf43

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

etc/di/redis-client.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
use Clue\React\Redis\Client;
4+
use PHPDIDefinitions\Clue\Redis\Client\WaitingClient;
5+
use Psr\Log\LoggerInterface;
6+
use React\EventLoop\LoopInterface;
7+
8+
return [
9+
Client::class => function (LoopInterface $loop, string $dsn, LoggerInterface $logger = null) {
10+
return WaitingClient::create($loop, $dsn, $logger);
11+
},
12+
];

src/WaitingClient.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
namespace PHPDIDefinitions\Clue\Redis\Client;
4+
5+
use Clue\React\Redis\Client;
6+
use Clue\React\Redis\Factory;
7+
use Evenement\EventEmitterTrait;
8+
use Psr\Log\LoggerInterface;
9+
use Psr\Log\NullLogger;
10+
use React\EventLoop\LoopInterface;
11+
use React\Promise\Deferred;
12+
use SplQueue;
13+
use WyriHaximus\PSR3\CallableThrowableLogger\CallableThrowableLogger;
14+
15+
final class WaitingClient implements Client
16+
{
17+
use EventEmitterTrait;
18+
19+
/**
20+
* @var Client
21+
*/
22+
private $redis;
23+
24+
/**
25+
* @var SplQueue
26+
*/
27+
private $callQueue;
28+
29+
public static function create(LoopInterface $loop, string $dsn, LoggerInterface $logger = null): self
30+
{
31+
return new self(new Factory($loop), $dsn, $logger ?? new NullLogger());
32+
}
33+
34+
/**
35+
* @param Factory $factory
36+
* @param string $dsn
37+
* @param LoggerInterface $logger
38+
*
39+
* @internal
40+
*/
41+
public function __construct(Factory $factory, string $dsn, LoggerInterface $logger)
42+
{
43+
$this->callQueue = new \SplQueue();
44+
$logger->debug('Connecting');
45+
$factory->createClient($dsn)->done(function (Client $client) use ($logger) {
46+
$logger->debug('Connected');
47+
$this->redis = $client;
48+
49+
$logger->debug('Executing ' . $this->callQueue->count() . ' waiting call(s)');
50+
while ($this->callQueue->count() > 0) {
51+
$call = $this->callQueue->dequeue();
52+
$name = $call['name'];
53+
$args = $call['args'];
54+
$call['deferred']->resolve($this->redis->$name(...$args));
55+
}
56+
$logger->debug('Executed all waiting calls, any new calls will be send to Redis directly');
57+
}, CallableThrowableLogger::create($logger));
58+
}
59+
60+
public function __call($name, $args)
61+
{
62+
if ($this->redis instanceof Client) {
63+
return $this->redis->$name(...$args);
64+
}
65+
66+
$deferred = new Deferred();
67+
68+
$this->callQueue->enqueue([
69+
'deferred' => $deferred,
70+
'name' => $name,
71+
'args' => $args,
72+
]);
73+
74+
return $deferred->promise();
75+
}
76+
77+
public function end()
78+
{
79+
// TODO: Implement end() method.
80+
}
81+
82+
public function close()
83+
{
84+
// TODO: Implement close() method.
85+
}
86+
}

tests/RedisClient.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace PHPDIDefinitions\Tests\Clue\Redis\Client;
4+
5+
use Clue\React\Redis\Client;
6+
use Evenement\EventEmitterTrait;
7+
8+
class RedisClient implements Client
9+
{
10+
use EventEmitterTrait;
11+
12+
public function incr(...$args)
13+
{
14+
15+
}
16+
17+
public function __call($name, $args)
18+
{
19+
// TODO: Implement __call() method.
20+
}
21+
22+
public function end()
23+
{
24+
// TODO: Implement end() method.
25+
}
26+
27+
public function close()
28+
{
29+
// TODO: Implement close() method.
30+
}
31+
}

tests/WaitingClientTest.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace PHPDIDefinitions\Tests\Clue\Redis\Client;
4+
5+
use ApiClients\Tools\TestUtilities\TestCase;
6+
use Clue\React\Redis\Client;
7+
use Clue\React\Redis\Factory;
8+
use PHPDIDefinitions\Clue\Redis\Client\WaitingClient;
9+
use Prophecy\Argument;
10+
use Psr\Log\LoggerInterface;
11+
use React\EventLoop\LoopInterface;
12+
use React\Promise\Deferred;
13+
use function React\Promise\resolve;
14+
15+
final class WaitingClientTest extends TestCase
16+
{
17+
private const DSN = 'redis://::1/13';
18+
19+
public function testCreate()
20+
{
21+
$loop = $this->prophesize(LoopInterface::class);
22+
23+
$logger = $this->prophesize(LoggerInterface::class);
24+
$logger->debug('Connecting')->shouldBeCalled();
25+
$logger->debug('Connected')->shouldNotBeCalled();
26+
$logger->debug('Executing 0 waiting call(s)')->shouldNotBeCalled();
27+
$logger->debug('Executed all waiting calls, any new calls will be send to Redis directly')->shouldNotBeCalled();
28+
29+
WaitingClient::create($loop->reveal(), self::DSN, $logger->reveal());
30+
}
31+
32+
public function testLogging()
33+
{
34+
$logger = $this->prophesize(LoggerInterface::class);
35+
$logger->debug('Connecting')->shouldBeCalled();
36+
$logger->debug('Connected')->shouldBeCalled();
37+
$logger->debug('Executing 0 waiting call(s)')->shouldBeCalled();
38+
$logger->debug('Executed all waiting calls, any new calls will be send to Redis directly')->shouldBeCalled();
39+
40+
$redisFactory = $this->prophesize(Factory::class);
41+
$redisFactory->createClient(self::DSN)->shouldbeCalled()->willReturn(resolve($this->prophesize(Client::class)->reveal()));
42+
43+
new WaitingClient($redisFactory->reveal(), self::DSN, $logger->reveal());
44+
}
45+
46+
public function testCalls()
47+
{
48+
$logger = $this->prophesize(LoggerInterface::class);
49+
$logger->debug(Argument::type('string'))->shouldBeCalled();
50+
$logger->debug('Executing 1 waiting call(s)')->shouldBeCalled();
51+
52+
$deferred = new Deferred();
53+
54+
$redis = $this->prophesize(RedisClient::class);
55+
$redis->incr('key')->shouldBeCalled()->willReturn(resolve(true));
56+
$redis->incr('sleutel')->shouldBeCalled()->willReturn(resolve(true));
57+
58+
$redisFactory = $this->prophesize(Factory::class);
59+
$redisFactory->createClient(self::DSN)->shouldbeCalled()->willReturn($deferred->promise());
60+
61+
$waitingClient = new WaitingClient($redisFactory->reveal(), self::DSN, $logger->reveal());
62+
63+
$waitingClient->incr('key');
64+
65+
$deferred->resolve($redis->reveal());
66+
67+
$waitingClient->incr('sleutel');
68+
}
69+
}

0 commit comments

Comments
 (0)