Completed
Pull Request — master (#104)
by Ankit
02:02
created

FileStore::createCacheDir()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace TusPhp\Cache;
4
5
use Carbon\Carbon;
6
use TusPhp\Config;
7
8
class FileStore extends AbstractCache
9
{
10
    /** @var string */
11
    protected $cacheDir;
12
13
    /** @var string */
14
    protected $cacheFile;
15
16
    /**
17
     * FileStore constructor.
18
     *
19
     * @param string|null $cacheDir
20
     * @param string|null $cacheFile
21
     */
22 2
    public function __construct(string $cacheDir = null, string $cacheFile = null)
23
    {
24 2
        $cacheDir  = $cacheDir ?? Config::get('file.dir');
25 2
        $cacheFile = $cacheFile ?? Config::get('file.name');
26
27 2
        $this->setCacheDir($cacheDir);
1 ignored issue
show
Bug introduced by
It seems like $cacheDir can also be of type null; however, parameter $path of TusPhp\Cache\FileStore::setCacheDir() 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

27
        $this->setCacheDir(/** @scrutinizer ignore-type */ $cacheDir);
Loading history...
28 2
        $this->setCacheFile($cacheFile);
1 ignored issue
show
Bug introduced by
It seems like $cacheFile can also be of type null; however, parameter $file of TusPhp\Cache\FileStore::setCacheFile() 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

28
        $this->setCacheFile(/** @scrutinizer ignore-type */ $cacheFile);
Loading history...
29 2
    }
30
31
    /**
32
     * Set cache dir.
33
     *
34
     * @param string $path
35
     *
36
     * @return self
37
     */
38 1
    public function setCacheDir(string $path) : self
39
    {
40 1
        $this->cacheDir = $path;
41
42 1
        return $this;
43
    }
44
45
    /**
46
     * Get cache dir.
47
     *
48
     * @return string
49
     */
50 1
    public function getCacheDir() : string
51
    {
52 1
        return $this->cacheDir;
53
    }
54
55
    /**
56
     * Set cache file.
57
     *
58
     * @param string $file
59
     *
60
     * @return self
61
     */
62 1
    public function setCacheFile(string $file) : self
63
    {
64 1
        $this->cacheFile = $file;
65
66 1
        return $this;
67
    }
68
69
    /**
70
     * Get cache file.
71
     *
72
     * @return string
73
     */
74 5
    public function getCacheFile() : string
75
    {
76 5
        return $this->cacheDir . $this->cacheFile;
77
    }
78
79
    /**
80
     * Create cache dir if not exists.
81
     *
82
     * @return void
83
     */
84 2
    protected function createCacheDir()
85
    {
86 2
        if ( ! file_exists($this->cacheDir)) {
87 2
            mkdir($this->cacheDir);
88
        }
89 2
    }
90
91
    /**
92
     * Create cache file and add required meta.
93
     *
94
     * @return void
95
     */
96 2
    protected function createCacheFile()
97
    {
98 2
        $this->createCacheDir();
99
100 2
        $cacheFilePath = $this->getCacheFile();
101
102 2
        if ( ! file_exists($cacheFilePath)) {
103 2
            touch($cacheFilePath);
104
        }
105 2
    }
106
107
    /**
108
     * {@inheritDoc}
109
     */
110 8
    public function get(string $key, bool $withExpired = false)
111
    {
112 8
        $key      = $this->getActualCacheKey($key);
113 8
        $contents = $this->getCacheContents();
114
115 8
        if (empty($contents[$key])) {
116 3
            return null;
117
        }
118
119 5
        if ($withExpired) {
120 1
            return $contents[$key];
121
        }
122
123 5
        return $this->isValid($key) ? $contents[$key] : null;
124
    }
125
126
    /**
127
     * Get contents of a file with shared access.
128
     *
129
     * @param string $path
130
     *
131
     * @return string
132
     */
133 2
    public function sharedGet(string $path) : string
134
    {
135 2
        $contents = '';
136 2
        $handle   = @fopen($path, 'r');
137
138 2
        if (false === $handle) {
139 1
            return $contents;
140
        }
141
142
        try {
143 1
            if (flock($handle, LOCK_SH)) {
144 1
                clearstatcache(true, $path);
145
146 1
                $contents = fread($handle, filesize($path) ?: 1);
147
148 1
                flock($handle, LOCK_UN);
149
            }
150 1
        } finally {
151 1
            fclose($handle);
152
        }
153
154 1
        return $contents;
155
    }
156
157
    /**
158
     * Write the contents of a file with exclusive lock.
159
     *
160
     * @param string $path
161
     * @param string $contents
162
     *
163
     * @return int
164
     */
165 3
    public function put(string $path, string $contents) : int
166
    {
167 3
        return file_put_contents($path, $contents, LOCK_EX);
168
    }
169
170
    /**
171
     * {@inheritDoc}
172
     */
173 8
    public function set(string $key, $value)
174
    {
175 8
        $cacheKey  = $this->getActualCacheKey($key);
176 8
        $cacheFile = $this->getCacheFile();
177
178 8
        if ( ! file_exists($cacheFile) || ! $this->isValid($cacheKey)) {
179 8
            $this->createCacheFile();
180
        }
181
182 8
        $contents = json_decode($this->sharedGet($cacheFile), true) ?? [];
183
184 8
        if ( ! empty($contents[$cacheKey]) && is_array($value)) {
185 3
            $contents[$cacheKey] = $value + $contents[$cacheKey];
186
        } else {
187 8
            $contents[$cacheKey] = $value;
188
        }
189
190 8
        return $this->put($cacheFile, json_encode($contents));
191
    }
192
193
    /**
194
     * {@inheritDoc}
195
     */
196 1
    public function delete(string $key) : bool
197
    {
198 1
        $cacheKey = $this->getActualCacheKey($key);
199 1
        $contents = $this->getCacheContents();
200
201 1
        if (isset($contents[$cacheKey])) {
202 1
            unset($contents[$cacheKey]);
203
204 1
            return false !== $this->put($this->getCacheFile(), json_encode($contents));
205
        }
206
207 1
        return false;
208
    }
209
210
    /**
211
     * {@inheritDoc}
212
     */
213 2
    public function keys() : array
214
    {
215 2
        $contents = $this->getCacheContents();
216
217 2
        if (is_array($contents)) {
218 1
            return array_keys($this->getCacheContents());
0 ignored issues
show
Bug introduced by
It seems like $this->getCacheContents() can also be of type boolean; however, parameter $input of array_keys() does only seem to accept array, 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

218
            return array_keys(/** @scrutinizer ignore-type */ $this->getCacheContents());
Loading history...
219
        }
220
221 1
        return [];
222
    }
223
224
    /**
225
     * Check if cache is still valid.
226
     *
227
     * @param string $key
228
     *
229
     * @return bool
230
     */
231 3
    public function isValid(string $key) : bool
232
    {
233 3
        $key  = $this->getActualCacheKey($key);
234 3
        $meta = $this->getCacheContents()[$key] ?? [];
235
236 3
        if (empty($meta['expires_at'])) {
237 1
            return false;
238
        }
239
240 2
        return Carbon::now() < Carbon::createFromFormat(self::RFC_7231, $meta['expires_at']);
241
    }
242
243
    /**
244
     * Get cache contents.
245
     *
246
     * @return array|bool
247
     */
248 3
    public function getCacheContents()
249
    {
250 3
        $cacheFile = $this->getCacheFile();
251
252 3
        if ( ! file_exists($cacheFile)) {
253 1
            return false;
254
        }
255
256 2
        return json_decode($this->sharedGet($cacheFile), true) ?? [];
257
    }
258
259
    /**
260
     * Get actual cache key with prefix.
261
     *
262
     * @param string $key
263
     *
264
     * @return string
265
     */
266 1
    public function getActualCacheKey(string $key) : string
267
    {
268 1
        $prefix = $this->getPrefix();
269
270 1
        if (false === strpos($key, $prefix)) {
271 1
            $key = $prefix . $key;
272
        }
273
274 1
        return $key;
275
    }
276
}
277