Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
1 / 1
RedisCache
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
10 / 10
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 set
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 has
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clear
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMultiple
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 setMultiple
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 deleteMultiple
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 normalizeTtl
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10namespace Phexium\Plugin\Cache\Adapter;
11
12use DateInterval;
13use DateTimeImmutable;
14use Override;
15use Phexium\Plugin\Cache\Port\CacheInterface;
16use Redis;
17
18final readonly class RedisCache implements CacheInterface
19{
20    public function __construct(
21        private Redis $redis,
22        private array|bool $allowedClasses = [],
23    ) {}
24
25    #[Override]
26    public function get(string $key, mixed $default = null): mixed
27    {
28        $value = $this->redis->get($key);
29
30        if ($value === false && !$this->has($key)) {
31            return $default;
32        }
33
34        return unserialize($value, ['allowed_classes' => $this->allowedClasses]);
35    }
36
37    #[Override]
38    public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool
39    {
40        $ttlMilliseconds = $this->normalizeTtl($ttl);
41
42        if ($ttlMilliseconds !== null && $ttlMilliseconds <= 0) {
43            $this->delete($key);
44
45            return true;
46        }
47
48        $serialized = serialize($value);
49
50        if ($ttlMilliseconds === null) {
51            return $this->redis->set($key, $serialized);
52        }
53
54        return $this->redis->psetex($key, $ttlMilliseconds, $serialized);
55    }
56
57    #[Override]
58    public function has(string $key): bool
59    {
60        return $this->redis->exists($key) > 0;
61    }
62
63    #[Override]
64    public function delete(string $key): bool
65    {
66        return $this->redis->del($key) > 0;
67    }
68
69    #[Override]
70    public function clear(): bool
71    {
72        return $this->redis->flushDB();
73    }
74
75    #[Override]
76    public function getMultiple(iterable $keys, mixed $default = null): iterable
77    {
78        $result = [];
79
80        foreach ($keys as $key) {
81            $result[$key] = $this->get($key, $default);
82        }
83
84        return $result;
85    }
86
87    #[Override]
88    public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool
89    {
90        foreach ($values as $key => $value) {
91            $this->set($key, $value, $ttl);
92        }
93
94        return true;
95    }
96
97    #[Override]
98    public function deleteMultiple(iterable $keys): bool
99    {
100        foreach ($keys as $key) {
101            $this->delete($key);
102        }
103
104        return true;
105    }
106
107    private function normalizeTtl(DateInterval|int|null $ttl): ?int
108    {
109        if ($ttl === null) {
110            return null;
111        }
112
113        if ($ttl instanceof DateInterval) {
114            $now = new DateTimeImmutable();
115            $future = $now->add($ttl);
116
117            $nowMicroseconds = (int) $now->format('Uu');
118            $futureMicroseconds = (int) $future->format('Uu');
119
120            return (int) (($futureMicroseconds - $nowMicroseconds) / 1000);
121        }
122
123        return $ttl * 1000;
124    }
125}