Passed
Push — main ( 72c6fa...9344f0 )
by Sílvio
02:59
created

FileCacheStore::flushCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
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\Exceptions\CacheFileException;
8
use Silviooosilva\CacheerPhp\Helpers\CacheFileHelper;
9
use Silviooosilva\CacheerPhp\Utils\CacheLogger;
10
11
/**
12
 * Class FileCacheStore
13
 * @author Sílvio Silva <https://github.com/silviooosilva>
14
 * @package Silviooosilva\CacheerPhp
15
 */
16
class FileCacheStore implements CacheerInterface
17
{
18
    /**
19
     * @param string $cacheDir
20
     */
21
    private string $cacheDir;
22
23
    /**
24
     * @param array $options
25
     */
26
    private array $options = [];
27
28
    /**
29
     * @param string $message
30
     */
31
    private string $message = '';
32
33
    /**
34
     * @param integer $defaultTTL
35
     */
36
    private int $defaultTTL = 3600; // 1 hora por padrão
37
38
    /**
39
     * @param boolean $success
40
     */
41
    private bool $success = false;
42
43
    /**
44
     * @param string $lastFlushTimeFile
45
     */
46
    private string $lastFlushTimeFile;
47
48
    /**
49
    * @var CacheLogger
50
    */
51
    private $logger = null;
52
53
    /**
54
    * @var FileCacheManager
55
    */
56
    private FileCacheManager $fileManager;
57
58
    public function __construct(array $options = [])
59
    {
60
        $this->validateOptions($options);
61
        $this->fileManager = new FileCacheManager();
62
        $this->initializeCacheDir($options['cacheDir']);
63
        $this->defaultTTL = $this->getExpirationTime($options);
64
        $this->lastFlushTimeFile = "{$this->cacheDir}/last_flush_time";
65
        $this->handleAutoFlush($options);
66
        $this->logger = new CacheLogger($options['loggerPath']);
67
    }
68
69
    /**
70
     * @param string $cacheKey
71
     * @param string $namespace
72
     * @param string|int $ttl
73
     * @return string|null
74
     */
75
    public function getCache(string $cacheKey, string $namespace = '', string|int $ttl = 3600)
76
    {
77
       
78
        $ttl = CacheFileHelper::ttl($ttl, $this->defaultTTL);
79
        $cacheFile = $this->buildCacheFilePath($cacheKey, $namespace);
80
        if ($this->isCacheValid($cacheFile, $ttl)) {
81
            $cacheData = $this->fileManager->serialize($this->fileManager->readFile($cacheFile), false);
82
83
            $this->setMessage("Cache retrieved successfully", true);
84
            $this->logger->debug("{$this->getMessage()} from file driver.");
85
            return $cacheData;
86
        }
87
88
        $this->setMessage("cacheFile not found, does not exists or expired", false);
89
        $this->logger->info("{$this->getMessage()} from file driver.");
90
        return null;
91
    }
92
93
    /**
94
     * @param string $cacheKey
95
     * @param mixed  $cacheData
96
     * @param string $namespace
97
     * @param string|int $ttl
98
     * @return void
99
     */
100
    public function putCache(string $cacheKey, mixed $cacheData, string $namespace = '', string|int $ttl = 3600)
101
    {
102
        $cacheFile = $this->buildCacheFilePath($cacheKey, $namespace);
103
        $data = $this->fileManager->serialize($cacheData);
104
105
        $this->fileManager->writeFile($cacheFile, $data);
106
        $this->setMessage("Cache file created successfully", true);
107
108
        $this->logger->debug("{$this->getMessage()} from file driver.");
109
    }
110
111
    /**
112
     * @param array   $items
113
     * @param string  $namespace
114
     * @param integer $batchSize
115
     * @return void
116
     */
117
    public function putMany(array $items, string $namespace = '', int $batchSize = 100)
118
    {
119
        $processedCount = 0;
120
        $itemCount = count($items);
121
122
        while ($processedCount < $itemCount) {
123
            $batchItems = array_slice($items, $processedCount, $batchSize);
124
            $this->processBatchItems($batchItems, $namespace);
125
            $processedCount += count($batchItems);
126
        }
127
    }
128
129
    /**
130
     * @param string $cacheKey
131
     * @param mixed  $cacheData
132
     * @param string $namespace
133
     * @return void
134
     */
135
    public function appendCache(string $cacheKey, mixed $cacheData, string $namespace = '')
136
    {
137
        $currentCacheFileData = $this->getCache($cacheKey, $namespace);
138
139
        if (!$this->isSuccess()) {
140
            return $this->getMessage();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getMessage() returns the type string which is incompatible with the documented return type void.
Loading history...
141
        }
142
143
        $mergedCacheData = CacheFileHelper::arrayIdentifier($currentCacheFileData, $cacheData);
144
145
146
        $this->putCache($cacheKey, $mergedCacheData, $namespace);
147
        if ($this->isSuccess()) {
148
            $this->setMessage("Cache updated successfully", true);
149
            $this->logger->debug("{$this->getMessage()} from file driver.");
150
        }
151
    }
152
153
154
    /**
155
     * @param string $cacheKey
156
     * @param string $namespace
157
     * @return bool
158
     */
159
    public function has(string $cacheKey, string $namespace = '')
160
    {
161
        $cacheData = $this->getCache($cacheKey, $namespace);
162
        if ($cacheData) {
163
            $this->setMessage("Cache key: {$cacheKey} exists and it's available! from file driver", true);
164
        }
165
        $this->setMessage("Cache key: {$cacheKey} does not exists or it's expired! from file driver", false);
166
    }
167
168
    /**
169
     * @param string $cacheKey
170
     * @param string|int $ttl
171
     * @param string $namespace
172
     * @return void
173
     */
174
    public function renewCache(string $cacheKey, string|int $ttl, string $namespace = '')
175
    {
176
        $cacheData = $this->getCache($cacheKey, $namespace);
177
        if ($cacheData) {
178
            $this->putCache($cacheKey, $cacheData, $namespace, $ttl);
179
            $this->setMessage("Cache with key {$cacheKey} renewed successfully", true);
180
        }
181
        $this->setMessage("Failed to renew Cache with key {$cacheKey}", false);
182
        $this->logger->debug("{$this->getMessage()} from file driver.");
183
    }
184
185
    /**
186
     * @param string $cacheKey
187
     * @param string $namespace
188
     * @return void
189
     */
190
    public function clearCache(string $cacheKey, string $namespace = '')
191
    {
192
        $cacheFile = $this->buildCacheFilePath($cacheKey, $namespace);
193
        if ($this->fileManager->readFile($cacheFile)) {
194
            $this->fileManager->removeFile($cacheFile);
195
            $this->setMessage("Cache file deleted successfully!", true);
196
        } else {
197
            $this->setMessage("Cache file does not exist!", false);
198
        }
199
        $this->logger->debug("{$this->getMessage()} from file driver.");
200
    }
201
202
    /**
203
     * @return void
204
     */
205
    public function flushCache()
206
    {
207
        $this->fileManager->clearDirectory($this->cacheDir);
208
        file_put_contents($this->lastFlushTimeFile, time());
209
    }
210
211
    /**
212
     * @return string
213
     */
214
    public function getMessage()
215
    {
216
        return $this->message;
217
    }
218
219
    /**
220
     * @return boolean
221
     */
222
    public function isSuccess()
223
    {
224
        return $this->success;
225
    }
226
227
    /**
228
     * @param array $options
229
     * @return void
230
     */
231
    private function validateOptions(array $options)
232
    {
233
        if (!isset($options['cacheDir']) && $options['drive'] === 'file') {
234
            $this->logger->debug("The 'cacheDir' option is required from file driver.");
235
            throw CacheFileException::create("The 'cacheDir' option is required.");
236
        }
237
        $this->options = $options;
238
    }
239
240
    /**
241
     * @param string $cacheDir
242
     * @return void
243
     */
244
    private function initializeCacheDir(string $cacheDir)
245
    {
246
        $this->cacheDir = realpath($cacheDir) ?: "";
247
        $this->fileManager->createDirectory($cacheDir);
248
    }
249
250
    /**
251
     * @param array $options
252
     * @return integer
253
     */
254
    private function getExpirationTime(array $options)
255
    {
256
        return isset($options['expirationTime'])
257
            ? CacheFileHelper::convertExpirationToSeconds($options['expirationTime'])
258
            : $this->defaultTTL;
259
    }
260
261
262
    /**
263
     * @param array $options
264
     * @return void
265
     */
266
    private function handleAutoFlush(array $options)
267
    {
268
        if (isset($options['flushAfter'])) {
269
            $this->scheduleFlush($options['flushAfter']);
270
        }
271
    }
272
273
    /**
274
     * @param string $flushAfter
275
     * @return void
276
     */
277
    private function scheduleFlush(string $flushAfter)
278
    {
279
        $flushAfterSeconds = CacheFileHelper::convertExpirationToSeconds($flushAfter);
280
        if ($this->fileManager->readFile($this->lastFlushTimeFile)) {
281
            $lastFlushTime = $this->fileManager->readFile($this->lastFlushTimeFile);
282
            if ((time() - (int)$lastFlushTime) >= $flushAfterSeconds) {
283
                $this->flushCache();
284
            }
285
        } else {
286
            file_put_contents($this->lastFlushTimeFile, time());
287
        }
288
    }
289
290
    /**
291
     * @param array  $batchItems
292
     * @param string $namespace
293
     * @return void
294
     */
295
    private function processBatchItems(array $batchItems, string $namespace)
296
    {
297
        foreach ($batchItems as $item) {
298
            CacheFileHelper::validateCacheItem($item);
299
            $cacheKey = $item['cacheKey'];
300
            $cacheData = $item['cacheData'];
301
            $mergedData = CacheFileHelper::mergeCacheData($cacheData);
302
            $this->putCache($cacheKey, $mergedData, $namespace);
303
        }
304
    }
305
306
    /**
307
     * @param string  $message
308
     * @param boolean $success
309
     * @return void
310
     */
311
    private function setMessage(string $message, bool $success)
312
    {
313
        $this->message = $message;
314
        $this->success = $success;
315
    }
316
317
    /**
318
     * @param string $cacheKey
319
     * @param string $namespace
320
     * @return string
321
     */
322
    private function buildCacheFilePath(string $cacheKey, string $namespace)
323
    {
324
        $namespace = $namespace ? md5($namespace) . '/' : '';
325
        $cacheDir = "{$this->cacheDir}/";
326
327
        if (!empty($namespace)) {
328
            $cacheDir = "{$this->cacheDir}/{$namespace}";
329
            $this->fileManager->createDirectory($cacheDir);
330
        }
331
        return $cacheDir . md5($cacheKey) . ".cache";
332
    }
333
334
    /**
335
     * @param string  $cacheFile
336
     * @param integer $ttl
337
     * @return boolean
338
     */
339
    private function isCacheValid(string $cacheFile, int $ttl)
340
    {
341
        return file_exists($cacheFile) && (filemtime($cacheFile) > (time() - $ttl));
342
    }
343
}
344