Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.30% covered (success)
96.30%
26 / 27
85.71% covered (warning)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
RedisCache
96.30% covered (success)
96.30%
26 / 27
85.71% covered (warning)
85.71%
6 / 7
14
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 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
 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\Internal\CacheMultipleOperationsTrait;
16use Phexium\Plugin\Cache\Port\CacheInterface;
17use Redis;
18
19final readonly class RedisCache implements CacheInterface
20{
21    use CacheMultipleOperationsTrait;
22
23    public function __construct(
24        private Redis $redis,
25        private array|bool $allowedClasses = [],
26    ) {}
27
28    #[Override]
29    public function get(string $key, mixed $default = null): mixed
30    {
31        if (!$this->has($key)) {
32            return $default;
33        }
34
35        $value = $this->redis->get($key);
36
37        if (!is_string($value)) {
38            return $default;
39        }
40
41        return unserialize($value, ['allowed_classes' => $this->allowedClasses]);
42    }
43
44    #[Override]
45    public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool
46    {
47        $ttlMilliseconds = $this->normalizeTtl($ttl);
48
49        if ($ttlMilliseconds !== null && $ttlMilliseconds <= 0) {
50            $this->delete($key);
51
52            return true;
53        }
54
55        $serialized = serialize($value);
56
57        if ($ttlMilliseconds === null) {
58            return $this->redis->set($key, $serialized);
59        }
60
61        return $this->redis->psetex($key, $ttlMilliseconds, $serialized);
62    }
63
64    #[Override]
65    public function has(string $key): bool
66    {
67        return $this->redis->exists($key) > 0;
68    }
69
70    #[Override]
71    public function delete(string $key): bool
72    {
73        return $this->redis->del($key) > 0;
74    }
75
76    #[Override]
77    public function clear(): bool
78    {
79        return $this->redis->flushDB();
80    }
81
82    private function normalizeTtl(DateInterval|int|null $ttl): ?int
83    {
84        if ($ttl === null) {
85            return null;
86        }
87
88        if ($ttl instanceof DateInterval) {
89            $now = new DateTimeImmutable();
90            $future = $now->add($ttl);
91
92            $nowMicroseconds = intval($now->format('Uu'));
93            $futureMicroseconds = intval($future->format('Uu'));
94
95            return ($futureMicroseconds - $nowMicroseconds) / 1000;
96        }
97
98        return $ttl * 1000;
99    }
100}