DatabaseCacheStore::storeCache()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 2
eloc 6
c 2
b 0
f 1
nc 2
nop 4
dl 0
loc 9
rs 10
1
<?php
2
3
namespace Silviooosilva\CacheerPhp\CacheStore;
4
5
use Silviooosilva\CacheerPhp\Interface\CacheerInterface;
6
use Silviooosilva\CacheerPhp\Helpers\CacheDatabaseHelper;
7
use Silviooosilva\CacheerPhp\Utils\CacheLogger;
8
use Silviooosilva\CacheerPhp\Repositories\CacheDatabaseRepository;
9
use Silviooosilva\CacheerPhp\CacheStore\CacheManager\GenericFlusher;
10
use Silviooosilva\CacheerPhp\Helpers\CacheFileHelper;
11
use Silviooosilva\CacheerPhp\Helpers\FlushHelper;
12
use Silviooosilva\CacheerPhp\Core\Connect;
13
use Silviooosilva\CacheerPhp\Core\MigrationManager;
14
15
/**
16
 * Class DatabaseCacheStore
17
 * @author Sílvio Silva <https://github.com/silviooosilva>
18
 * @package Silviooosilva\CacheerPhp
19
 */
20
class DatabaseCacheStore implements CacheerInterface
21
{
22
    /**
23
     * @var boolean
24
     */
25
    private bool $success = false;
26
27
    /**
28
     * @var string
29
     */
30
    private string $message = '';
31
32
    /**
33
     * @var ?CacheLogger
34
     */
35
    private ?CacheLogger $logger = null;
36
37
    /**
38
     * @var CacheDatabaseRepository
39
     */
40
    private CacheDatabaseRepository $cacheRepository;
41
42
    /** @var int|null */
43
    private ?int $defaultTTL = null;
44
45
    /** @var GenericFlusher|null */
46
    private ?GenericFlusher $flusher = null;
47
48
    /**
49
     * DatabaseCacheStore constructor.
50
     *
51
     * @param string $logPath
52
     * @param array $options
53
     */
54
    public function __construct(string $logPath, array $options = [])
55
    {
56
        $this->logger = new CacheLogger($logPath);
57
        $table = $options['table'] ?? 'cacheer_table';
58
        $this->cacheRepository = new CacheDatabaseRepository($table);
59
60
        // Ensure the custom table exists by running a targeted migration
61
        $pdo = Connect::getInstance();
62
        MigrationManager::migrate($pdo, $table);
63
64
        if (!empty($options['expirationTime'])) {
65
            $this->defaultTTL = (int) CacheFileHelper::convertExpirationToSeconds((string) $options['expirationTime']);
66
        }
67
68
        $lastFlushFile = FlushHelper::pathFor('db', $table);
0 ignored issues
show
Bug introduced by
It seems like $table can also be of type null; however, parameter $identifier of Silviooosilva\CacheerPhp...\FlushHelper::pathFor() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

68
        $lastFlushFile = FlushHelper::pathFor('db', /** @scrutinizer ignore-type */ $table);
Loading history...
69
        $this->flusher = new GenericFlusher($lastFlushFile, function () {
70
            $this->flushCache();
71
        });
72
        $this->flusher->handleAutoFlush($options);
73
    }
74
75
    /**
76
     * Appends data to an existing cache item.
77
     * 
78
     * @param string $cacheKey
79
     * @param mixed  $cacheData
80
     * @param string $namespace
81
     * @return bool
82
     */
83
    public function appendCache(string $cacheKey, mixed $cacheData, string $namespace = ''): bool
84
    {
85
        $currentCacheData = $this->getCache($cacheKey, $namespace);
86
        $mergedCacheData = CacheDatabaseHelper::arrayIdentifier($currentCacheData, $cacheData);
87
88
        if ($this->updateCache($cacheKey, $mergedCacheData, $namespace)) {
89
            $this->logger->debug("{$this->getMessage()} from database driver.");
0 ignored issues
show
Bug introduced by
The method debug() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

89
            $this->logger->/** @scrutinizer ignore-call */ 
90
                           debug("{$this->getMessage()} from database driver.");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
90
            return true;
91
        }
92
93
        $this->logger->error("{$this->getMessage()} from database driver.");
94
        return false;
95
    }
96
97
    /**
98
     * Clears a specific cache item.
99
     * 
100
     * @param string $cacheKey
101
     * @param string $namespace
102
     * @return void
103
     */
104
    public function clearCache(string $cacheKey, string $namespace = ''): void
105
    {
106
        $data = $this->cacheRepository->clear($cacheKey, $namespace);
107
        if($data) {
108
            $this->setMessage("Cache deleted successfully!", true);
109
        } else {
110
            $this->setMessage("Cache does not exists!", false);
111
        }
112
113
        $this->logger->debug("{$this->getMessage()} from database driver.");
114
    }
115
116
    /**
117
     * Flushes all cache items.
118
     * 
119
     * @return void
120
     */
121
    public function flushCache(): void
122
    {
123
        if($this->cacheRepository->flush()){
124
            $this->setMessage("Flush finished successfully", true);
125
        } else {
126
            $this->setMessage("Something went wrong. Please, try again.", false);
127
        }
128
129
        $this->logger->info("{$this->getMessage()} from database driver.");
130
131
    }
132
133
    /**
134
     * Gets a single cache item.
135
     * 
136
     * @param string $cacheKey
137
     * @param string $namespace
138
     * @param string|int $ttl
139
     * @return mixed
140
     */
141
    public function getCache(string $cacheKey, string $namespace = '', string|int $ttl = 3600): mixed
142
    {
143
        $cacheData = $this->retrieveCache($cacheKey, $namespace);
144
        if ($cacheData) {
145
            $this->setMessage("Cache retrieved successfully", true);
146
            $this->logger->debug("{$this->getMessage()} from database driver.");
147
            return $cacheData;
148
        }
149
        $this->setMessage("CacheData not found, does not exists or expired", false);
150
        $this->logger->info("{$this->getMessage()} from database driver.");
151
        return null;
152
    }
153
154
    /**
155
     * Gets all items in a specific namespace.
156
     * 
157
     * @param string $namespace
158
     * @return array
159
     */
160
    public function getAll(string $namespace = ''): array
161
    {
162
        $cacheData = $this->cacheRepository->getAll($namespace);
163
        if ($cacheData) {
164
            $this->setMessage("Cache retrieved successfully", true);
165
            $this->logger->debug("{$this->getMessage()} from database driver.");
166
            return $cacheData;
167
        }
168
        $this->setMessage("No cache data found for the provided namespace", false);
169
        $this->logger->info("{$this->getMessage()} from database driver.");
170
        return [];
171
    }
172
173
    /**
174
     * Retrieves multiple cache items by their keys.
175
     * 
176
     * @param array  $cacheKeys
177
     * @param string $namespace
178
     * @param string|int $ttl
179
     * @return array
180
     */
181
    public function getMany(array $cacheKeys, string $namespace = '', string|int $ttl = 3600): array
182
    {
183
        $cacheData = [];
184
        foreach ($cacheKeys as $cacheKey) {
185
            $data = $this->getCache($cacheKey, $namespace, $ttl);
186
            if ($data) {
187
                $cacheData[$cacheKey] = $data;
188
            }
189
        }
190
        if (!empty($cacheData)) {
191
            $this->setMessage("Cache retrieved successfully", true);
192
            $this->logger->debug("{$this->getMessage()} from database driver.");
193
            return $cacheData;
194
        }
195
        $this->setMessage("No cache data found for the provided keys", false);
196
        $this->logger->info("{$this->getMessage()} from database driver.");
197
        return [];
198
    }
199
200
    /**
201
     * Checks if a cache item exists.
202
     * 
203
     * @return string
204
     */
205
    public function getMessage(): string
206
    {
207
        return $this->message;
208
    }
209
210
    /**
211
     * Checks if a cache item exists.
212
     * 
213
     * @param string $cacheKey
214
     * @param string $namespace
215
     * @return bool
216
     */
217
    public function has(string $cacheKey, string $namespace = ''): bool
218
    {
219
        $cacheData = $this->getCache($cacheKey, $namespace);
220
221
        if ($cacheData) {
222
            $this->setMessage("Cache key: {$cacheKey} exists and it's available from database driver.", true);
223
            $this->logger->debug("{$this->getMessage()}");
224
            return true;
225
        }
226
227
        $this->setMessage("Cache key: {$cacheKey} does not exist or it's expired from database driver.", false);
228
        $this->logger->debug("{$this->getMessage()}");
229
230
        return false;
231
    }
232
233
    /**
234
     * Checks if the last operation was successful.
235
     * 
236
     * @return boolean
237
     */
238
    public function isSuccess(): bool
239
    {
240
        return $this->success;
241
    }
242
243
    /**
244
     * Store multiple items in the cache.
245
     * 
246
     * @param array   $items
247
     * @param string  $namespace
248
     * @param integer $batchSize
249
     * @return void
250
     */
251
    public function putMany(array $items, string $namespace = '', int $batchSize = 100): void
252
    {
253
        $processedCount = 0;
254
        $itemCount = count($items);
255
        while ($processedCount < $itemCount) {
256
            $batchItems = array_slice($items, $processedCount, $batchSize);
257
            $this->processBatchItems($batchItems, $namespace);
258
            $processedCount += count($batchItems);
259
        }
260
    }
261
262
    /**
263
     * Stores an item in the cache with a specific TTL.
264
     * 
265
     * @param string $cacheKey
266
     * @param mixed  $cacheData
267
     * @param string $namespace
268
     * @param string|int $ttl
269
     * @return bool
270
     */
271
    public function putCache(string $cacheKey, mixed $cacheData, string $namespace = '', string|int $ttl = 3600): bool
272
    {
273
        $ttlToUse = $ttl;
274
        if ($this->defaultTTL !== null && ($ttl === null || (int)$ttl === 3600)) {
275
            $ttlToUse = $this->defaultTTL;
276
        }
277
        if (is_string($ttlToUse)) {
278
            $ttlToUse = (int) CacheFileHelper::convertExpirationToSeconds($ttlToUse);
279
        }
280
281
        if($this->storeCache($cacheKey, $cacheData, $namespace, $ttlToUse)){
282
            $this->logger->debug("{$this->getMessage()} from database driver.");
283
            return true;
284
        }
285
        $this->logger->error("{$this->getMessage()} from database driver.");
286
        return false;
287
    }
288
289
    /**
290
     * Renews the cache for a specific key with a new TTL.
291
     * 
292
     * @param string $cacheKey
293
     * @param string|int $ttl
294
     * @param string $namespace
295
     * @return void
296
     */
297
    public function renewCache(string $cacheKey, int | string $ttl, string $namespace = ''): void
298
    {
299
        $cacheData = $this->getCache($cacheKey, $namespace);
300
        if ($cacheData) {
301
            $this->renew($cacheKey, $ttl, $namespace);
302
            $this->setMessage("Cache with key {$cacheKey} renewed successfully", true);
303
            $this->logger->debug("{$this->getMessage()} from database driver.");
304
        }
305
    }
306
307
    /**
308
     * Processes a batch of cache items.
309
     * 
310
     * @param array  $batchItems
311
     * @param string $namespace
312
     * @return void
313
     */
314
    private function processBatchItems(array $batchItems, string $namespace): void
315
    {
316
        foreach($batchItems as $item) {
317
            CacheDatabaseHelper::validateCacheItem($item);
318
            $cacheKey = $item['cacheKey'];
319
            $cacheData = $item['cacheData'];
320
            $mergedData = CacheDatabaseHelper::mergeCacheData($cacheData);
321
            $this->putCache($cacheKey, $mergedData, $namespace);
322
        }
323
    }
324
325
    /**
326
     * Renews the expiration time of a cache item.
327
     * 
328
     * @param string $cacheKey
329
     * @param string|int $ttl
330
     * @param string $namespace
331
     * @return bool
332
     */
333
    private function renew(string $cacheKey, string|int $ttl = 3600, string $namespace = ''): bool
334
    {
335
        $cacheData = $this->getCache($cacheKey, $namespace);
336
        if ($cacheData) {
337
            $renewedCache = $this->cacheRepository->renew($cacheKey, $ttl, $namespace);
338
            if ($renewedCache) {
339
                $this->setMessage("Cache with key {$cacheKey} renewed successfully", true);
340
                $this->logger->debug("{$this->getMessage()} from database driver.");
341
                return true;
342
            }
343
            return false;
344
        }
345
        return false;
346
    }
347
348
    /**
349
     * Sets a message and its success status.
350
     * 
351
     * @param string  $message
352
     * @param boolean $success
353
     * @return void
354
     */
355
    private function setMessage(string $message, bool $success): void
356
    {
357
        $this->message = $message;
358
        $this->success = $success;
359
    }
360
361
    /**
362
     * Retrieves a cache item by its key.
363
     * @param string $cacheKey
364
     * @param string $namespace
365
     * @return mixed
366
     */
367
    private function retrieveCache(string $cacheKey, string $namespace = ''): mixed
368
    {
369
        return $this->cacheRepository->retrieve($cacheKey, $namespace);
370
    }
371
372
    /**
373
     * Stores a cache item.
374
     *
375
     * @param string $cacheKey
376
     * @param mixed $cacheData
377
     * @param string $namespace
378
     * @param string|int $ttl
379
     * @return bool
380
     */
381
    private function storeCache(string $cacheKey, mixed $cacheData, string $namespace = '', string|int $ttl = 3600): bool
382
    {
383
        $data = $this->cacheRepository->store($cacheKey, $cacheData, $namespace, $ttl);
384
        if($data) {
385
            $this->setMessage("Cache Stored Successfully", true);
386
            return true;
387
        }
388
        $this->setMessage("Already exists a cache with this key...", false);
389
        return false;
390
    }
391
392
    /**
393
     * Updates an existing cache item.
394
     * 
395
     * @param string $cacheKey
396
     * @param mixed  $cacheData
397
     * @param string $namespace
398
     * @return bool
399
     */
400
    private function updateCache(string $cacheKey, mixed $cacheData, string $namespace = ''): bool
401
    {
402
        $data = $this->cacheRepository->update($cacheKey, $cacheData, $namespace);
403
        if($data) {
404
            $this->setMessage("Cache updated successfully.", true);
405
            return true;
406
        }
407
        $this->setMessage("Cache does not exist or update failed!", false);
408
        return false;
409
    }
410
}
411