Completed
Push — b0.27.0 ( c529b3...34cc26 )
by Sebastian
05:07
created

DiskCache   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 14
eloc 35
c 5
b 0
f 1
dl 0
loc 165
ccs 40
cts 40
cp 1
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A calculateTtl() 0 8 2
A __construct() 0 3 1
A get() 0 12 2
A has() 0 3 1
A delete() 0 13 2
A doesFileChecksFailed() 0 18 4
A clear() 0 5 1
A set() 0 18 1
1
<?php
2
3
/**
4
 * Linna Framework.
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2018, Sebastian Rapetti
8
 * @license http://opensource.org/licenses/MIT MIT License
9
 */
10
declare(strict_types=1);
11
12
namespace Linna\Cache;
13
14
use Psr\SimpleCache\CacheInterface;
15
16
/**
17
 * PSR-16 Disk Cache.
18
 *
19
 * Before use it, is possible configure ramdisk, work only on linux:
20
 * - mkdir /tmp/linna-cache
21
 * - sudo mount -t tmpfs -o size=128m tmpfs /tmp/linna-cache
22
 *
23
 * For check Ram Disk status
24
 * - df -h /tmp/linna-cache
25
 *
26
 * Serialize option is required when is needed store a class instance.
27
 * If you not utilize serialize, must declare __set_state() method inside
28
 * class or get from cache fail.
29
 */
30
class DiskCache implements CacheInterface
31
{
32
    use ActionMultipleTrait;
33
34
    /**
35
     * @var string Directory for cache storage.
36
     */
37
    protected string $dir = '/tmp';
38
39
    /**
40
     * Constructor.
41
     *
42
     * @param array<mixed> $options
43
     */
44 1
    public function __construct(array $options = [])
45
    {
46 1
        ['dir' => $this->dir] = \array_replace_recursive(['dir' => '/tmp'], $options);
47 1
    }
48
49
    /**
50
     * Fetches a value from the cache.
51
     *
52
     * @param string $key     The unique key of this item in the cache.
53
     * @param mixed  $default Default value to return if the key does not exist.
54
     *
55
     * @return mixed The value of the item from the cache, or $default in case of cache miss.
56
     */
57 7
    public function get(string $key, $default = null)
58
    {
59
        //create file name
60 7
        $file = $this->dir.'/'.\sha1($key).'.php';
61
62 7
        if ($this->doesFileChecksFailed($file)) {
63 4
            return $default;
64
        }
65
66 3
        $cacheValue = include $file;
67
68 3
        return \unserialize($cacheValue['value']);
69
    }
70
71
    /**
72
     * Checks for cache file.
73
     *
74
     * @param string $file
75
     *
76
     * @return bool
77
     */
78 14
    private function doesFileChecksFailed(string $file): bool
79
    {
80
        //check if file exist
81 14
        if (!\file_exists($file)) {
82 4
            return true;
83
        }
84
85
        //take cache from file
86 12
        $cacheValue = include $file;
87
88
        //check if cache is expired and delete file from storage
89 12
        if ($cacheValue['expires'] <= \time() && $cacheValue['expires'] !== 0) {
90 4
            \unlink($file);
91
92 4
            return true;
93
        }
94
95 9
        return false;
96
    }
97
98
    /**
99
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
100
     *
101
     * @param string $key   The key of the item to store.
102
     * @param mixed  $value The value of the item to store, must be serializable.
103
     * @param int    $ttl   Optional. The TTL (time to live) value in seconds of this item.
104
     *                      If no value is sent and the driver supports TTL then the
105
     *                      library may set a default value for it or let the driver take care of that.
106
     *
107
     * @return bool True on success and false on failure.
108
     */
109 13
    public function set(string $key, $value, int $ttl = 0): bool
110
    {
111
        //create cache array
112
        $cache = [
113 13
            'key'     => $key,
114 13
            'value'   => \serialize($value),
115 13
            'expires' => $this->calculateTtl($ttl),
116
        ];
117
118
        //export
119
        // HHVM fails at __set_state, so just use object cast for now
120 13
        $content = \str_replace('stdClass::__set_state', '(object)', \var_export($cache, true));
121 13
        $content = "<?php return {$content};";
122
123
        //write file
124 13
        \file_put_contents($this->dir.'/'.\sha1($key).'.php', $content);
125
126 13
        return true;
127
    }
128
129
    /**
130
     * Calculate ttl for cache file.
131
     *
132
     * @param int $ttl
133
     *
134
     * @return int
135
     */
136 13
    private function calculateTtl(int $ttl): int
137
    {
138
        //check for usage of ttl default class option value
139 13
        if ($ttl) {
140 4
            return \time() + $ttl;
141
        }
142
143 9
        return $ttl;
144
    }
145
146
    /**
147
     * Delete an item from the cache by its unique key.
148
     *
149
     * @param string $key The unique cache key of the item to delete.
150
     *
151
     * @return bool True if the item was successfully removed. False if there was an error.
152
     */
153 3
    public function delete(string $key): bool
154
    {
155
        //create file name
156 3
        $file = $this->dir.'/'.\sha1($key).'.php';
157
158
        //chek if file exist and delete
159 3
        if (\file_exists($file)) {
160 2
            \unlink($file);
161
162 2
            return true;
163
        }
164
165 1
        return false;
166
    }
167
168
    /**
169
     * Wipes clean the entire cache's keys.
170
     *
171
     * @return bool True on success and false on failure.
172
     */
173 51
    public function clear(): bool
174
    {
175 51
        \array_map('unlink', (array) \glob($this->dir.'/*.php'));
176
177 51
        return true;
178
    }
179
180
    /**
181
     * Determines whether an item is present in the cache.
182
     *
183
     * NOTE: It is recommended that has() is only to be used for cache warming type purposes
184
     * and not to be used within your live applications operations for get/set, as this method
185
     * is subject to a race condition where your has() will return true and immediately after,
186
     * another script can remove it making the state of your app out of date.
187
     *
188
     * @param string $key The cache item key.
189
     *
190
     * @return bool
191
     */
192 10
    public function has(string $key): bool
193
    {
194 10
        return !$this->doesFileChecksFailed($this->dir.'/'.\sha1($key).'.php');
195
    }
196
}
197