Passed
Branch main (6fd149)
by Sílvio
02:57
created

FileCacheStore::isSuccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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