RedisCacheStore::getAll()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 4
eloc 14
c 1
b 1
f 0
nc 6
nop 1
dl 0
loc 23
rs 9.7998
1
<?php
2
3
namespace Silviooosilva\CacheerPhp\CacheStore;
4
5
use Exception;
6
use Silviooosilva\CacheerPhp\Utils\CacheLogger;
7
use Silviooosilva\CacheerPhp\Helpers\CacheRedisHelper;
8
use Silviooosilva\CacheerPhp\Interface\CacheerInterface;
9
use Silviooosilva\CacheerPhp\Exceptions\CacheRedisException;
10
use Silviooosilva\CacheerPhp\CacheStore\CacheManager\RedisCacheManager;
11
12
/**
13
 * Class RedisCacheStore
14
 * @author Sílvio Silva <https://github.com/silviooosilva>
15
 * @package Silviooosilva\CacheerPhp
16
 */
17
class RedisCacheStore implements CacheerInterface
18
{
19
    /** @var */
20
    private $redis;
21
22
    /** @param string $namespace */
23
    private string $namespace = '';
24
25
    /**
26
     * @var CacheLogger
27
     */
28
    private $logger = null;
29
30
    /**
31
     * @var string
32
     */
33
    private string $message = '';
34
35
    /**
36
     * @var boolean
37
     */
38
    private bool $success = false;
39
40
41
    /**
42
     * RedisCacheStore constructor.
43
     *
44
     * @param string $logPath
45
     */
46
    public function __construct(string $logPath)
47
    {
48
        $this->redis = RedisCacheManager::connect();
49
        $this->logger = new CacheLogger($logPath);
50
    }
51
52
    /**
53
     * Appends data to an existing cache item.
54
     * 
55
     * @param string $cacheKey
56
     * @param mixed  $cacheData
57
     * @param string $namespace
58
     * @return void
59
     */
60
    public function appendCache(string $cacheKey, mixed $cacheData, string $namespace = '')
61
    {
62
        $cacheFullKey = $this->buildKey($cacheKey, $namespace);
63
        $existingData = $this->getCache($cacheFullKey);
64
65
        $mergedCacheData = CacheRedisHelper::arrayIdentifier($existingData, $cacheData);
66
67
        $serializedData = CacheRedisHelper::serialize($mergedCacheData);
68
69
        if ($this->redis->set($cacheFullKey, $serializedData)) {
70
            $this->setMessage("Cache appended successfully", true);
71
        } else {
72
            $this->setMessage("Something went wrong. Please, try again.", false);
73
        }
74
75
        $this->logger->debug("{$this->getMessage()} from redis driver.");
76
    }
77
78
    /**
79
     * Builds a unique key for the Redis cache.
80
     * 
81
     * @param string $key
82
     * @param string $namespace
83
     * @return string
84
     */
85
    private function buildKey(string $key, string $namespace)
86
    {
87
        return $this->namespace . ($namespace ? $namespace . ':' : '') . $key;
88
    }
89
90
    /**
91
     * Clears a specific cache item.
92
     * 
93
     * @param string $cacheKey
94
     * @param string $namespace
95
     * @return void
96
     */
97
    public function clearCache(string $cacheKey, string $namespace = '')
98
    {
99
        $cacheFullKey = $this->buildKey($cacheKey, $namespace);
100
101
        if ($this->redis->del($cacheFullKey) > 0) {
102
            $this->setMessage("Cache cleared successfully", true);
103
        } else {
104
            $this->setMessage("Something went wrong. Please, try again.", false);
105
        }
106
107
        $this->logger->debug("{$this->getMessage()} from redis driver.");
108
    }
109
110
    /**
111
     * Flushes all cache items in Redis.
112
     * 
113
     * @return void
114
     */
115
    public function flushCache()
116
    {
117
        if ($this->redis->flushall()) {
118
            $this->setMessage("Cache flushed successfully", true);
119
        } else {
120
            $this->setMessage("Something went wrong. Please, try again.", false);
121
        }
122
123
        $this->logger->debug("{$this->getMessage()} from redis driver.");
124
    }
125
126
    /**
127
     * Retrieves a single cache item by its key.
128
     * 
129
     * @param string $cacheKey
130
     * @param string $namespace
131
     * @param string|int $ttl
132
     * @return mixed
133
     */
134
    public function getCache(string $cacheKey, string $namespace = '', string|int $ttl = 3600)
135
    {
136
        $fullCacheKey = $this->buildKey($cacheKey, $namespace);
137
        $cacheData = $this->redis->get($fullCacheKey);
138
139
        if ($cacheData) {
140
            $this->setMessage("Cache retrieved successfully", true);
141
            $this->logger->debug("{$this->getMessage()} from redis driver.");
142
            return CacheRedisHelper::serialize($cacheData, false);
143
        }
144
145
        $this->setMessage("CacheData not found, does not exists or expired", false);
146
        $this->logger->info("{$this->getMessage()} from redis driver.");
147
    }
148
149
    /**
150
     * Retrieves all cache items in a specific namespace.
151
     * 
152
     * @param string $namespace
153
     * @return array
154
     */
155
    public function getAll(string $namespace = '')
156
    {
157
        $keys = $this->redis->keys($this->buildKey('*', $namespace));
158
        $results = [];
159
160
        $prefix = $this->buildKey('', $namespace);
161
        $prefixLen = strlen($prefix);
162
163
        foreach ($keys as $fullKey) {
164
            $cacheKey = substr($fullKey, $prefixLen);
165
            $cacheData = $this->getCache($cacheKey, $namespace);
166
            if ($cacheData !== null) {
167
                $results[$cacheKey] = $cacheData;
168
            }
169
        }
170
171
        if (empty($results)) {
172
            $this->setMessage("No cache data found in the namespace", false);
173
        } else {
174
            $this->setMessage("Cache data retrieved successfully", true);
175
        }
176
177
        return $results;
178
    }
179
180
    /**
181
     * Retrieves multiple cache items by their keys.
182
     * 
183
     * @param array $cacheKeys
184
     * @param string $namespace
185
     * @param string|int $ttl
186
     * @return array
187
     */
188
    public function getMany(array $cacheKeys, string $namespace = '', string|int $ttl = 3600)
189
    {
190
        $results = [];
191
        foreach ($cacheKeys as $cacheKey) {
192
            $fullCacheKey = $this->buildKey($cacheKey, $namespace);
193
            $cacheData = $this->getCache($fullCacheKey, $namespace, $ttl);
194
            if ($cacheData !== null) {
195
                $results[$cacheKey] = $cacheData;
196
            }
197
        }
198
199
        if (empty($results)) {
200
            $this->setMessage("No cache data found for the provided keys", false);
201
        } else {
202
            $this->setMessage("Cache data retrieved successfully", true);
203
        }
204
205
        return $results;
206
    }
207
208
    /**
209
     * Gets the message from the last operation.
210
     * 
211
     * @return string
212
     */
213
    public function getMessage()
214
    {
215
        return $this->message;
216
    }
217
218
    /**
219
     * Gets the serialized dump of a cache item.
220
     * 
221
     * @param string $fullKey
222
     * @return string|null
223
     */
224
    private function getDump(string $fullKey)
225
    {
226
        return $this->redis->dump($fullKey);
227
    }
228
229
    /**
230
     * Checks if a cache item exists.
231
     * 
232
     * @param string $cacheKey
233
     * @param string $namespace
234
     * @return void
235
     */
236
    public function has(string $cacheKey, string $namespace = '')
237
    {
238
        $cacheFullKey = $this->buildKey($cacheKey, $namespace);
239
240
        if ($this->redis->exists($cacheFullKey) > 0) {
241
            $this->setMessage("Cache Key: {$cacheKey} exists!", true);
242
        } else {
243
            $this->setMessage("Cache Key: {$cacheKey} does not exists!", false);
244
        }
245
246
        $this->logger->debug("{$this->getMessage()} from redis driver.");
247
    }
248
249
    /**
250
     * Checks if the last operation was successful.
251
     * 
252
     * @return boolean
253
     */
254
    public function isSuccess()
255
    {
256
        return $this->success;
257
    }
258
259
    /**
260
     * Processes a batch of cache items and stores them in Redis.
261
     * 
262
     * @param array  $batchItems
263
     * @param string $namespace
264
     * @return void
265
     */
266
    private function processBatchItems(array $batchItems, string $namespace)
267
    {
268
        foreach ($batchItems as $item) {
269
            CacheRedisHelper::validateCacheItem($item);
270
            $cacheKey = $item['cacheKey'];
271
            $cacheData = $item['cacheData'];
272
            $mergedData = CacheRedisHelper::mergeCacheData($cacheData);
273
            $this->putCache($cacheKey, $mergedData, $namespace);
274
        }
275
    }
276
277
    /**
278
     * Stores a cache item in Redis with optional namespace and TTL.
279
     *
280
     * @param string $cacheKey
281
     * @param mixed  $cacheData
282
     * @param string $namespace
283
     * @param string|int|null $ttl
284
     * @return mixed
285
     */
286
    public function putCache(string $cacheKey, mixed $cacheData, string $namespace = '', string|int|null $ttl = null)
287
    {
288
        $cacheFullKey = $this->buildKey($cacheKey, $namespace);
289
        $serializedData = CacheRedisHelper::serialize($cacheData);
290
291
        $result = $ttl ? $this->redis->setex($cacheFullKey, (int) $ttl, $serializedData)
292
                       : $this->redis->set($cacheFullKey, $serializedData);
293
294
        if ($result) {
295
            $this->setMessage("Cache stored successfully", true);
296
        } else {
297
            $this->setMessage("Failed to store cache", false);
298
        }
299
300
        $this->logger->debug("{$this->getMessage()} from Redis driver.");
301
        return $result;
302
    }
303
304
    /**
305
     * Stores multiple cache items in Redis in batches.
306
     * 
307
     * @param array  $items
308
     * @param string $namespace
309
     * @param int    $batchSize
310
     * @return void
311
     */
312
    public function putMany(array $items, string $namespace = '', int $batchSize = 100)
313
    {
314
        $processedCount = 0;
315
        $itemCount = count($items);
316
317
        while ($processedCount < $itemCount) {
318
            $batchItems = array_slice($items, $processedCount, $batchSize);
319
            $this->processBatchItems($batchItems, $namespace);
320
            $processedCount += count($batchItems);
321
        }
322
    }
323
324
    /**
325
     * Renews the cache for a specific key with a new TTL.
326
     * 
327
     * @param string $cacheKey
328
     * @param string|int $ttl
329
     * @param string $namespace
330
     * @return void
331
     */
332
    public function renewCache(string $cacheKey, string|int $ttl, string $namespace = '')
333
    {
334
        $cacheFullKey = $this->buildKey($cacheKey, $namespace);
335
        $dump = $this->getDump($cacheFullKey);
336
337
        if (!$dump) {
338
            $this->setMessage("Cache Key: {$cacheKey} not found.", false);
339
            $this->logger->warning("{$this->getMessage()} from Redis driver.");
340
            return;
341
        }
342
343
        $this->clearCache($cacheFullKey);
344
345
        if ($this->restoreKey($cacheFullKey, $ttl, $dump)) {
346
            $this->setMessage("Cache Key: {$cacheKey} renewed successfully.", true);
347
            $this->logger->debug("{$this->getMessage()} from Redis driver.");
348
        } else {
349
            $this->setMessage("Failed to renew cache key: {$cacheKey}.", false);
350
            $this->logger->error("{$this->getMessage()} from Redis driver.");
351
        }
352
    }
353
354
    /**
355
     * Restores a key in Redis with a given TTL and serialized data.
356
     * 
357
     * @param string $fullKey
358
     * @param string|int $ttl
359
     * @param mixed $dump
360
     * @return bool
361
     */
362
    private function restoreKey(string $fullKey, string|int $ttl, mixed $dump)
363
    {
364
        try {
365
            $this->redis->restore($fullKey, $ttl * 1000, $dump, 'REPLACE');
366
            return true;
367
        } catch (Exception $e) {
368
            throw CacheRedisException::create($e->getMessage());
369
        }
370
    }
371
372
    /**
373
     * Sets a message and its success status.
374
     * 
375
     * @param string  $message
376
     * @param boolean $success
377
     * @return void
378
     */
379
    private function setMessage(string $message, bool $success)
380
    {
381
        $this->message = $message;
382
        $this->success = $success;
383
    }
384
}