Completed
Push — master ( b339ab...2f90cc )
by Ankit
06:47
created

src/Cache/FileStore.php (2 issues)

Labels
Severity
1
<?php
2
3
namespace TusPhp\Cache;
4
5
use TusPhp\File;
6
use Carbon\Carbon;
7
use TusPhp\Config;
8
9
class FileStore extends AbstractCache
10
{
11
    /** @var string */
12
    protected $cacheDir;
13
14
    /** @var string */
15
    protected $cacheFile;
16
17
    /**
18
     * FileStore constructor.
19
     *
20
     * @param string|null $cacheDir
21
     * @param string|null $cacheFile
22
     */
23 3
    public function __construct(string $cacheDir = null, string $cacheFile = null)
24
    {
25 3
        $cacheDir  = $cacheDir ?? Config::get('file.dir');
26 3
        $cacheFile = $cacheFile ?? Config::get('file.name');
27
28 3
        $this->setCacheDir($cacheDir);
1 ignored issue
show
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

28
        $this->setCacheDir(/** @scrutinizer ignore-type */ $cacheDir);
Loading history...
29 3
        $this->setCacheFile($cacheFile);
1 ignored issue
show
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

29
        $this->setCacheFile(/** @scrutinizer ignore-type */ $cacheFile);
Loading history...
30 3
    }
31
32
    /**
33
     * Set cache dir.
34
     *
35
     * @param string $path
36
     *
37
     * @return self
38
     */
39 2
    public function setCacheDir(string $path) : self
40
    {
41 2
        $this->cacheDir = $path;
42
43 2
        return $this;
44
    }
45
46
    /**
47
     * Get cache dir.
48
     *
49
     * @return string
50
     */
51 1
    public function getCacheDir() : string
52
    {
53 1
        return $this->cacheDir;
54
    }
55
56
    /**
57
     * Set cache file.
58
     *
59
     * @param string $file
60
     *
61
     * @return self
62
     */
63 2
    public function setCacheFile(string $file) : self
64
    {
65 2
        $this->cacheFile = $file;
66
67 2
        return $this;
68
    }
69
70
    /**
71
     * Get cache file.
72
     *
73
     * @return string
74
     */
75 5
    public function getCacheFile() : string
76
    {
77 5
        return $this->cacheDir . $this->cacheFile;
78
    }
79
80
    /**
81
     * Create cache dir if not exists.
82
     *
83
     * @return void
84
     */
85 2
    protected function createCacheDir()
86
    {
87 2
        if ( ! file_exists($this->cacheDir)) {
88 2
            mkdir($this->cacheDir);
89
        }
90 2
    }
91
92
    /**
93
     * Create a cache file.
94
     *
95
     * @return void
96
     */
97 2
    protected function createCacheFile()
98
    {
99 2
        $this->createCacheDir();
100
101 2
        $cacheFilePath = $this->getCacheFile();
102
103 2
        if ( ! file_exists($cacheFilePath)) {
104 2
            touch($cacheFilePath);
105
        }
106 2
    }
107
108
    /**
109
     * {@inheritDoc}
110
     */
111 8
    public function get(string $key, bool $withExpired = false)
112
    {
113 8
        $key      = $this->getActualCacheKey($key);
114 8
        $contents = $this->getCacheContents();
115
116 8
        if (empty($contents[$key])) {
117 3
            return null;
118
        }
119
120 5
        if ($withExpired) {
121 1
            return $contents[$key];
122
        }
123
124 5
        return $this->isValid($key) ? $contents[$key] : null;
125
    }
126
127
    /**
128
     * Get contents of a file with shared access.
129
     *
130
     * @param string $path
131
     *
132
     * @return string
133
     */
134 2
    public function sharedGet(string $path) : string
135
    {
136 2
        $contents = '';
137 2
        $handle   = @fopen($path, File::READ_BINARY);
138
139 2
        if (false === $handle) {
140 1
            return $contents;
141
        }
142
143
        try {
144 1
            if (flock($handle, LOCK_SH)) {
145 1
                clearstatcache(true, $path);
146
147 1
                $contents = fread($handle, filesize($path) ?: 1);
148
149 1
                flock($handle, LOCK_UN);
150
            }
151 1
        } finally {
152 1
            fclose($handle);
153
        }
154
155 1
        return $contents;
156
    }
157
158
    /**
159
     * Write the contents of a file with exclusive lock.
160
     *
161
     * @param string $path
162
     * @param string $contents
163
     *
164
     * @return int
165
     */
166 3
    public function put(string $path, string $contents) : int
167
    {
168 3
        return file_put_contents($path, $contents, LOCK_EX);
169
    }
170
171
    /**
172
     * {@inheritDoc}
173
     */
174 8
    public function set(string $key, $value)
175
    {
176 8
        $cacheKey  = $this->getActualCacheKey($key);
177 8
        $cacheFile = $this->getCacheFile();
178
179 8
        if ( ! file_exists($cacheFile) || ! $this->isValid($cacheKey)) {
180 8
            $this->createCacheFile();
181
        }
182
183 8
        $contents = json_decode($this->sharedGet($cacheFile), true) ?? [];
184
185 8
        if ( ! empty($contents[$cacheKey]) && is_array($value)) {
186 3
            $contents[$cacheKey] = $value + $contents[$cacheKey];
187
        } else {
188 8
            $contents[$cacheKey] = $value;
189
        }
190
191 8
        return $this->put($cacheFile, json_encode($contents));
192
    }
193
194
    /**
195
     * {@inheritDoc}
196
     */
197 1
    public function delete(string $key) : bool
198
    {
199 1
        $cacheKey = $this->getActualCacheKey($key);
200 1
        $contents = $this->getCacheContents();
201
202 1
        if (isset($contents[$cacheKey])) {
203 1
            unset($contents[$cacheKey]);
204
205 1
            return false !== $this->put($this->getCacheFile(), json_encode($contents));
206
        }
207
208 1
        return false;
209
    }
210
211
    /**
212
     * {@inheritDoc}
213
     */
214 2
    public function keys() : array
215
    {
216 2
        $contents = $this->getCacheContents();
217
218 2
        if (is_array($contents)) {
219 1
            return array_keys($this->getCacheContents());
220
        }
221
222 1
        return [];
223
    }
224
225
    /**
226
     * Check if cache is still valid.
227
     *
228
     * @param string $key
229
     *
230
     * @return bool
231
     */
232 3
    public function isValid(string $key) : bool
233
    {
234 3
        $key  = $this->getActualCacheKey($key);
235 3
        $meta = $this->getCacheContents()[$key] ?? [];
236
237 3
        if (empty($meta['expires_at'])) {
238 1
            return false;
239
        }
240
241 2
        return Carbon::now() < Carbon::createFromFormat(self::RFC_7231, $meta['expires_at']);
242
    }
243
244
    /**
245
     * Get cache contents.
246
     *
247
     * @return array|bool
248
     */
249 3
    public function getCacheContents()
250
    {
251 3
        $cacheFile = $this->getCacheFile();
252
253 3
        if ( ! file_exists($cacheFile)) {
254 1
            return false;
255
        }
256
257 2
        return json_decode($this->sharedGet($cacheFile), true) ?? [];
258
    }
259
260
    /**
261
     * Get actual cache key with prefix.
262
     *
263
     * @param string $key
264
     *
265
     * @return string
266
     */
267 1
    public function getActualCacheKey(string $key) : string
268
    {
269 1
        $prefix = $this->getPrefix();
270
271 1
        if (false === strpos($key, $prefix)) {
272 1
            $key = $prefix . $key;
273
        }
274
275 1
        return $key;
276
    }
277
}
278