RedisStorage::getLastEventId()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BenTools\MercurePHP\Storage\Redis;
4
5
use BenTools\MercurePHP\Security\TopicMatcher;
6
use BenTools\MercurePHP\Storage\StorageInterface;
7
use BenTools\MercurePHP\Model\Message;
8
use Clue\React\Redis\Client as AsynchronousClient;
9
use Predis\Client as SynchronousClient;
10
use React\Promise\PromiseInterface;
11
12
use function React\Promise\resolve;
13
14
final class RedisStorage implements StorageInterface
15
{
16
    private AsynchronousClient $async;
17
    private SynchronousClient $sync;
18
19 2
    public function __construct(AsynchronousClient $asyncClient, SynchronousClient $syncClient)
20
    {
21 2
        $this->async = $asyncClient;
22 2
        $this->sync = $syncClient;
23 2
    }
24
25 1
    public function retrieveMessagesAfterId(string $id, array $subscribedTopics): PromiseInterface
26
    {
27 1
        return resolve($this->findNextMessages($id, $subscribedTopics));
28
    }
29
30 1
    public function storeMessage(string $topic, Message $message): PromiseInterface
31
    {
32 1
        $id = $message->getId();
33 1
        $payload = \json_encode($message, \JSON_THROW_ON_ERROR);
34
35
        /** @phpstan-ignore-next-line */
36 1
        return $this->async->set('data:' . $id, $topic . \PHP_EOL . $payload)
37 1
            ->then(fn() => $this->getLastEventId())
38 1
            ->then(fn(?string $lastEventId) => $this->storeLastEventId($lastEventId, $id))
39 1
            ->then(fn() => $id);
40
    }
41
42 1
    private function getLastEventId(): PromiseInterface
43
    {
44 1
        return $this->async->get('Last-Event-ID'); /** @phpstan-ignore-line */
45
    }
46
47 1
    private function storeLastEventId(?string $previousEventId, string $newEventId): PromiseInterface
48
    {
49 1
        $promise = $this->async->set('Last-Event-ID', $newEventId); /** @phpstan-ignore-line */
50
51 1
        if (null === $previousEventId) {
52 1
            return $promise;
53
        }
54
55
        /** @phpstan-ignore-next-line */
56 1
        return $promise->then(fn() => $this->async->set('next:' . $previousEventId, $newEventId));
57
    }
58
59 1
    private function findNextMessages(string $id, array $subscribedTopics): iterable
60
    {
61 1
        $nextId = $this->sync->get('next:' . $id);
62
63
        if (null === $nextId) {
64 1
            return [];
65 1
        }
66
67
        $payload = $this->sync->get('data:' . $nextId);
68 1
69
        if (null === $payload) {
70
            return [];
71 1
        }
72
73
        $item = \explode(\PHP_EOL, $payload);
74
        $topic = \array_shift($item);
75 1
        $message = Message::fromArray(
76 1
            \json_decode(
77 1
                \implode(\PHP_EOL, $item),
78 1
                true,
79 1
                512,
80 1
                \JSON_THROW_ON_ERROR
81 1
            )
82 1
        );
83
84
        if (TopicMatcher::matchesTopicSelectors($topic, $subscribedTopics)) {
85
            yield $topic => $message;
86 1
        }
87 1
88
        yield from $this->findNextMessages($message->getId(), $subscribedTopics); // Sync client needed because of this
89
    }
90
}
91