Passed
Pull Request — main (#48)
by Sílvio
03:09
created

FileCacheStore::putCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
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\CacheStore\CacheManager\FileCacheManager;
7
use Silviooosilva\CacheerPhp\CacheStore\CacheManager\FileCacheFlusher;
8
use Silviooosilva\CacheerPhp\Exceptions\CacheFileException;
9
use Silviooosilva\CacheerPhp\Helpers\CacheFileHelper;
10
use Silviooosilva\CacheerPhp\Utils\CacheLogger;
11
use Silviooosilva\CacheerPhp\CacheStore\Support\FileCachePathBuilder;
12
use Silviooosilva\CacheerPhp\CacheStore\Support\FileCacheBatchProcessor;
13
14
/**
15
 * Class FileCacheStore
16
 * @author Sílvio Silva <https://github.com/silviooosilva>
17
 * @package Silviooosilva\CacheerPhp
18
 */
19
class FileCacheStore implements CacheerInterface
20
{
21
    /**
22
     * @param string $cacheDir
23
     */
24
    private string $cacheDir;
25
26
    /**
27
     * @param array $options
28
     */
29
    private array $options = [];
30
31
    /**
32
     * @param string $message
33
     */
34
    private string $message = '';
35
36
    private FileCachePathBuilder $pathBuilder;
37
    private FileCacheBatchProcessor $batchProcessor;
38
    /**
39
     * @param integer $defaultTTL
40
     */
41
    private int $defaultTTL = 3600; // 1 hour default TTL
42
43
    /**
44
     * @param boolean $success
45
     */
46
    private bool $success = false;
47
48
49
    /**
50
    * @var CacheLogger
51
    */
52
    private $logger = null;
53
54
    /**
55
    * @var FileCacheManager
56
    */
57
    private FileCacheManager $fileManager;
58
59
    /**
60
    * @var FileCacheFlusher
61
    */
62
    private FileCacheFlusher $flusher;
63
64
65
    /**
66
     * FileCacheStore constructor.
67
     * @param array $options
68
     */
69
    public function __construct(array $options = [])
70
    {
71
        $this->validateOptions($options);
72
        $this->fileManager = new FileCacheManager();
73
        $this->initializeCacheDir($options['cacheDir']);
74
        $this->pathBuilder = new FileCachePathBuilder($this->fileManager, $this->cacheDir);
75
        $this->batchProcessor = new FileCacheBatchProcessor($this);
76
        $this->flusher = new FileCacheFlusher($this->fileManager, $this->cacheDir);
77
        $this->defaultTTL = $this->getExpirationTime($options);
78
        $this->flusher->handleAutoFlush($options);
79
        $this->logger = new CacheLogger($options['loggerPath']);
80
    }
81
82
    /**
83
     * Appends data to an existing cache item.
84
     * 
85
     * @param string $cacheKey
86
     * @param mixed  $cacheData
87
     * @param string $namespace
88
     * @return mixed
89
     */
90
    public function appendCache(string $cacheKey, mixed $cacheData, string $namespace = '')
91
    {
92
        $currentCacheFileData = $this->getCache($cacheKey, $namespace);
93
94
        if (!$this->isSuccess()) {
95
            return $this->getMessage();
96
        }
97
98
        $mergedCacheData = CacheFileHelper::arrayIdentifier($currentCacheFileData, $cacheData);
99
100
101
        $this->putCache($cacheKey, $mergedCacheData, $namespace);
102
        if ($this->isSuccess()) {
103
            $this->setMessage("Cache updated successfully", true);
104
            $this->logger->debug("{$this->getMessage()} from file driver.");
105
        }
106
    }
107
108
    /**
109
     * Builds the cache file path based on the cache key and namespace.
110
     * 
111
     * @param string $cacheKey
112
     * @param string $namespace
113
     * @return string
114
     */
115
    private function buildCacheFilePath(string $cacheKey, string $namespace)
116
    {
117
        return $this->pathBuilder->build($cacheKey, $namespace);
118
    }
119
120
    /**
121
     * Clears a specific cache item.
122
     * 
123
     * @param string $cacheKey
124
     * @param string $namespace
125
     * @return void
126
     */
127
    public function clearCache(string $cacheKey, string $namespace = '')
128
    {
129
        $cacheFile = $this->buildCacheFilePath($cacheKey, $namespace);
130
        if ($this->fileManager->readFile($cacheFile)) {
131
            $this->fileManager->removeFile($cacheFile);
132
            $this->setMessage("Cache file deleted successfully!", true);
133
        } else {
134
            $this->setMessage("Cache file does not exist!", false);
135
        }
136
        $this->logger->debug("{$this->getMessage()} from file driver.");
137
    }
138
139
    /**
140
     * Flushes all cache items.
141
     * 
142
     * @return void
143
     */
144
    public function flushCache()
145
    {
146
        $this->flusher->flushCache();
147
    }
148
149
    /**
150
     * Retrieves the expiration time from options or uses the default TTL.
151
     * 
152
     * @param array $options
153
     * @return integer
154
     */
155
    private function getExpirationTime(array $options)
156
    {
157
        return isset($options['expirationTime'])
158
            ? CacheFileHelper::convertExpirationToSeconds($options['expirationTime'])
159
            : $this->defaultTTL;
160
    }
161
162
    /**
163
     * Retrieves a message indicating the status of the last operation.
164
     * 
165
     * @return string
166
     */
167
    public function getMessage()
168
    {
169
        return $this->message;
170
    }
171
172
    /**
173
     * Retrieves a single cache item.
174
     * 
175
     * @param string $cacheKey
176
     * @param string $namespace
177
     * @param string|int $ttl
178
     * @return string
179
     */
180
    public function getCache(string $cacheKey, string $namespace = '', string|int $ttl = 3600)
181
    {
182
       
183
        $ttl = CacheFileHelper::ttl($ttl, $this->defaultTTL);
184
        $cacheFile = $this->buildCacheFilePath($cacheKey, $namespace);
185
        if ($this->isCacheValid($cacheFile, $ttl)) {
186
            $cacheData = $this->fileManager->serialize($this->fileManager->readFile($cacheFile), false);
187
188
            $this->setMessage("Cache retrieved successfully", true);
189
            $this->logger->debug("{$this->getMessage()} from file driver.");
190
            return $cacheData;
191
        }
192
193
        $this->setMessage("cacheFile not found, does not exists or expired", false);
194
        $this->logger->info("{$this->getMessage()} from file driver.");
195
    }
196
197
    /**
198
     * @param string $namespace
199
     * @return mixed
200
     */
201
    public function getAll(string $namespace = '')
202
    {
203
        $cacheDir = $this->getNamespaceCacheDir($namespace);
204
205
        if (!$this->fileManager->directoryExists($cacheDir)) {
206
            $this->setMessage("Cache directory does not exist", false);
207
            $this->logger->info("{$this->getMessage()} from file driver.");
208
            return [];
209
        }
210
211
        $results = $this->getAllCacheFiles($cacheDir);
212
213
        if (!empty($results)) {
214
            $this->setMessage("Cache retrieved successfully", true);
215
            $this->logger->debug("{$this->getMessage()} from file driver.");
216
            return $results;
217
        }
218
219
        $this->setMessage("No cache data found for the provided namespace", false);
220
        $this->logger->info("{$this->getMessage()} from file driver.");
221
        return [];
222
    }
223
224
    /**
225
     * Return the cache directory for the given namespace.
226
     * 
227
     * @param string $namespace
228
     * @return string
229
     */
230
    private function getNamespaceCacheDir(string $namespace)
231
    {
232
        return $this->pathBuilder->namespaceDir($namespace);
233
    }
234
235
    /**
236
     * Return all valid cache files from the specified directory.
237
     * 
238
     * @param string $cacheDir
239
     * @return array
240
     */
241
    private function getAllCacheFiles(string $cacheDir)
242
    {
243
        $files = $this->fileManager->getFilesInDirectory($cacheDir);
244
        $results = [];
245
246
        foreach ($files as $file) {
247
            if (pathinfo($file, PATHINFO_EXTENSION) === 'cache') {
248
                $cacheKey = basename($file, '.cache');
249
                $cacheData = $this->fileManager->serialize($this->fileManager->readFile($file), false);
250
                $results[$cacheKey] = $cacheData;
251
            }
252
        }
253
        return $results;
254
    }
255
256
    /**
257
     * Gets the cache data for multiple keys.
258
     * 
259
     * @param array  $cacheKeys
260
     * @param string $namespace
261
     * @param string|int $ttl
262
     * @return mixed
263
     */
264
    public function getMany(array $cacheKeys, string $namespace = '', string|int $ttl = 3600)
265
    {
266
        $ttl = CacheFileHelper::ttl($ttl, $this->defaultTTL);
267
        $results = [];
268
269
        foreach ($cacheKeys as $cacheKey) {
270
            $cacheData = $this->getCache($cacheKey, $namespace, $ttl);
271
            if ($this->isSuccess()) {
272
                $results[$cacheKey] = $cacheData;
273
            } else {
274
                $results[$cacheKey] = null;
275
            }
276
        }
277
278
        return $results;
279
    }
280
281
    /**
282
     * Stores multiple cache items in batches.
283
     * 
284
     * @param array   $items
285
     * @param string  $namespace
286
     * @param integer $batchSize
287
     * @return void
288
     */
289
    public function putMany(array $items, string $namespace = '', int $batchSize = 100)
290
    {
291
        $processedCount = 0;
292
        $itemCount = count($items);
293
294
        while ($processedCount < $itemCount) {
295
            $batchItems = array_slice($items, $processedCount, $batchSize);
296
            $this->processBatchItems($batchItems, $namespace);
297
            $processedCount += count($batchItems);
298
        }
299
    }
300
301
    /**
302
     * Stores an item in the cache with a specific TTL.
303
     * 
304
     * @param string $cacheKey
305
     * @param mixed  $cacheData
306
     * @param string $namespace
307
     * @param string|int $ttl
308
     * @return void
309
     */
310
    public function putCache(string $cacheKey, mixed $cacheData, string $namespace = '', string|int $ttl = 3600)
311
    {
312
        $cacheFile = $this->buildCacheFilePath($cacheKey, $namespace);
313
        $data = $this->fileManager->serialize($cacheData);
314
315
        $this->fileManager->writeFile($cacheFile, $data);
316
        $this->setMessage("Cache file created successfully", true);
317
318
    $this->logger->debug("{$this->getMessage()} from file driver.");
319
}
320
321
    /**
322
     * Checks if a cache key exists.
323
     * 
324
     * @param string $cacheKey
325
     * @param string $namespace
326
     * @return void
327
     */
328
    public function has(string $cacheKey, string $namespace = '')
329
    {
330
        $this->getCache($cacheKey, $namespace);
331
332
        if ($this->isSuccess()) {
333
            $this->setMessage("Cache key: {$cacheKey} exists and it's available! from file driver", true);
334
        } else {
335
            $this->setMessage("Cache key: {$cacheKey} does not exists or it's expired! from file driver", false);
336
        }
337
    }
338
339
    /**
340
     * Renews the cache for a specific key.
341
     * 
342
     * @param string $cacheKey
343
     * @param string|int $ttl
344
     * @param string $namespace
345
     * @return void
346
     */
347
    public function renewCache(string $cacheKey, string|int $ttl, string $namespace = '')
348
    {
349
        $cacheData = $this->getCache($cacheKey, $namespace);
350
        if ($cacheData) {
351
            $this->putCache($cacheKey, $cacheData, $namespace, $ttl);
352
            $this->setMessage("Cache with key {$cacheKey} renewed successfully", true);
353
            $this->logger->debug("{$this->getMessage()} from file driver.");
354
            return;
355
        }
356
        $this->setMessage("Failed to renew Cache with key {$cacheKey}", false);
357
        $this->logger->debug("{$this->getMessage()} from file driver.");
358
    }
359
360
    /**
361
     * Processes a batch of cache items.
362
     * 
363
     * @param array  $batchItems
364
     * @param string $namespace
365
     * @return void
366
     */
367
    private function processBatchItems(array $batchItems, string $namespace)
368
    {
369
        $this->batchProcessor->process($batchItems, $namespace);
370
    }
371
372
    /**
373
     * Checks if the last operation was successful.
374
     * 
375
     * @return boolean
376
     */
377
    public function isSuccess()
378
    {
379
        return $this->success;
380
    }
381
382
    /**
383
     * Sets a message indicating the status of the last operation.
384
     * 
385
     * @param string  $message
386
     * @param boolean $success
387
     * @return void
388
     */
389
    private function setMessage(string $message, bool $success)
390
    {
391
        $this->message = $message;
392
        $this->success = $success;
393
    }
394
395
    /**
396
     * Validates the options provided to the cache store.
397
     * 
398
     * @param array $options
399
     * @return void
400
     */
401
    private function validateOptions(array $options)
402
    {
403
        if (!isset($options['cacheDir']) && $options['drive'] === 'file') {
404
            $this->logger->debug("The 'cacheDir' option is required from file driver.");
405
            throw CacheFileException::create("The 'cacheDir' option is required.");
406
        }
407
        $this->options = $options;
408
    }
409
410
    /**
411
     * Initializes the cache directory.
412
     * 
413
     * @param string $cacheDir
414
     * @return void
415
     */
416
    private function initializeCacheDir(string $cacheDir)
417
    {
418
        $this->cacheDir = realpath($cacheDir) ?: "";
419
        $this->fileManager->createDirectory($cacheDir);
420
    }
421
422
423
424
    /**
425
     * Checks if the cache file is valid based on its existence and modification time.
426
     * 
427
     * @param string  $cacheFile
428
     * @param integer $ttl
429
     * @return boolean
430
     */
431
    private function isCacheValid(string $cacheFile, int $ttl)
432
    {
433
        return file_exists($cacheFile) && (filemtime($cacheFile) > (time() - $ttl));
434
    }
435
}