Completed
Push — master ( e24d5b...189838 )
by Alessandro
01:43
created

Driver   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 296
rs 8.8
c 1
b 0
f 0
wmc 36

13 Methods

Rating   Name   Duplication   Size   Complexity  
A delete() 0 10 3
A filterValidateKey() 0 6 2
A set() 0 14 4
A deleteStorage() 0 6 2
A setMultiple() 0 12 4
A has() 0 11 3
A saveDataToStorage() 0 3 1
A clear() 0 3 1
A deleteMultiple() 0 12 4
A getDataFromStorage() 0 12 3
A getMultiple() 0 10 3
A __construct() 0 14 1
B get() 0 13 5
1
<?php
2
/**
3
 * @package Infobiotech\JsonCache
4
 * @version v1.0.0-alpha
5
 * @author Alessandro Raffa, Infobiotech S.r.l. <[email protected]>
6
 * @copyright (c) 2014-2017, Infobiotech S.r.l.
7
 * @license http://mit-license.org/
8
 * @uses league/flysystem
9
 * @uses psr/simple-cache
10
 */
11
12
namespace Infobiotech\JsonCache\Psr16;
13
14
/*
15
 *
16
 */
17
18
use DateInterval;
19
use Psr\SimpleCache\CacheInterface;
20
use League\Flysystem\Filesystem;
21
use League\Flysystem\AdapterInterface as FlysystemAdapter;
22
use Infobiotech\JsonCache\Psr16\InvalidArgumentException;
23
24
/**
25
 * A key-value JSON-based PSR-16 cache implementation.
26
 *
27
 * @author Alessandro Raffa, Infobiotech S.r.l. <[email protected]>
28
 */
29
class Driver implements CacheInterface
30
{
31
    /*
32
     *
33
     */
34
    const FILED_VALUE      = 'value';
35
    const FILED_EXPIRATION = 'expiration';
36
    /*
37
     *
38
     */
39
    const DEFAULT_TTL      = 1200;
40
41
    /**
42
     *
43
     * @var \League\Flysystem\Filesystem
44
     */
45
    protected $filesystem;
46
47
    /**
48
     *
49
     * @var string
50
     */
51
    protected $namespace;
52
53
    /**
54
     *
55
     * @param FlysystemAdapter $filesystemAdapter
56
     * @param string $namespace
57
     * @return Driver
58
     */
59
    public function __construct(FlysystemAdapter $filesystemAdapter, $namespace)
60
    {
61
        /*
62
         *
63
         */
64
        $this->filesystem = new Filesystem($filesystemAdapter);
65
        /*
66
         *
67
         */
68
        $this->namespace  = $namespace.'.json';
69
        /*
70
         *
71
         */
72
        return $this;
73
    }
74
75
    /**
76
     * Fetches a value from the cache.
77
     *
78
     * @param string $key     The unique key of this item in the cache.
79
     * @param mixed  $default Default value to return if the key does not exist.
80
     *
81
     * @return mixed The value of the item from the cache, or $default in case of cache miss.
82
     *
83
     * @throws InvalidArgumentException
84
     *   MUST be thrown if the $key string is not a legal value.
85
     *
86
     * @todo Implement better $key validation and filtering
87
     */
88
    public function get($key, $default = null)
89
    {
90
        $value = $default;
91
        if (!is_string($this->filterValidateKey($key))) {
92
            throw new InvalidArgumentException();
93
        }
94
        $data = $this->getDataFromStorage();
95
        if (isset($data[$key]) && isset($data[$key][self::FILED_EXPIRATION])) {
96
            if ($data[$key][self::FILED_EXPIRATION] > microtime(true)) {
97
                $value = $data[$key][self::FILED_VALUE];
98
            }
99
        }
100
        return $value;
101
    }
102
103
    /**
104
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
105
     *
106
     * @param string                $key   The key of the item to store.
107
     * @param mixed                 $value The value of the item to store, must be serializable.
108
     * @param null|int|DateInterval $ttl   Optional. The TTL value of this item. If no value is sent and
109
     *                                     the driver supports TTL then the library may set a default value
110
     *                                     for it or let the driver take care of that.
111
     *
112
     * @return bool True on success and false on failure.
113
     *
114
     * @throws InvalidArgumentException
115
     *   MUST be thrown if the $key string is not a legal value.
116
     *
117
     * @todo Implement better $key validation and filtering
118
     */
119
    public function set($key, $value, $ttl = null)
120
    {
121
        if (!is_string($this->filterValidateKey($key))) {
122
            throw new InvalidArgumentException();
123
        }
124
        if (empty($ttl) || !is_numeric($ttl)) {
125
            $ttl = self::DEFAULT_TTL;
126
        }
127
        $data       = $this->getDataFromStorage();
128
        $data[$key] = [
129
            self::FILED_VALUE      => $value,
130
            self::FILED_EXPIRATION => microtime(true) + (float) $ttl,
131
        ];
132
        return $this->saveDataToStorage($data);
133
    }
134
135
    /**
136
     * Delete an item from the cache by its unique key.
137
     *
138
     * @param string $key The unique cache key of the item to delete.
139
     *
140
     * @return bool True if the item was successfully removed. False if there was an error.
141
     *
142
     * @throws InvalidArgumentException
143
     *   MUST be thrown if the $key string is not a legal value.
144
     */
145
    public function delete($key)
146
    {
147
        if (!is_string($this->filterValidateKey($key))) {
148
            throw new InvalidArgumentException();
149
        }
150
        $data = $this->getDataFromStorage();
151
        if (isset($data[$key])) {
152
            unset($data[$key]);
153
        }
154
        return $this->saveDataToStorage($data);
155
    }
156
157
    /**
158
     * Wipes clean the entire cache's keys.
159
     *
160
     * @return bool True on success and false on failure.
161
     */
162
    public function clear()
163
    {
164
        return $this->deleteStorage();
165
    }
166
167
    /**
168
     * Obtains multiple cache items by their unique keys.
169
     *
170
     * @param iterable $keys    A list of keys that can obtained in a single operation.
171
     * @param mixed    $default Default value to return for keys that do not exist.
172
     *
173
     * @return iterable A list of key => value pairs.
174
     *      Cache keys that do not exist or are stale will have $default as value.
175
     *
176
     * @throws InvalidArgumentException
177
     *      MUST be thrown if $keys is neither an array nor a Traversable,
178
     *      or if any of the $keys are not a legal value.
179
     */
180
    public function getMultiple($keys, $default = null)
181
    {
182
        if (!is_array($keys)) {
183
            throw new InvalidArgumentException();
184
        }
185
        $values = [];
186
        foreach ($keys as $key) {
187
            $values[$key] = $this->get($key, $default);
188
        }
189
        return $values;
190
    }
191
192
    /**
193
     * Persists a set of key => value pairs in the cache, with an optional TTL.
194
     *
195
     * @param iterable              $values A list of key => value pairs for a multiple-set operation.
196
     * @param null|int|DateInterval $ttl    Optional. The TTL value of this item. If no value is sent and
197
     *                                      the driver supports TTL then the library may set a default value
198
     *                                      for it or let the driver take care of that.
199
     *
200
     * @return bool True on success and false on failure.
201
     *
202
     * @throws InvalidArgumentException
203
     *   MUST be thrown if $values is neither an array nor a Traversable,
204
     *   or if any of the $values are not a legal value.
205
     */
206
    public function setMultiple($values, $ttl = null)
207
    {
208
        $failure = false;
209
        if (!is_array($values)) {
210
            throw new InvalidArgumentException();
211
        }
212
        foreach ($values as $key => $value) {
213
            if (!$this->set($key, $value, $ttl)) {
214
                $failure = true;
215
            }
216
        }
217
        return (bool) !$failure;
218
    }
219
220
    /**
221
     * Deletes multiple cache items in a single operation.
222
     *
223
     * @param iterable $keys A list of string-based keys to be deleted.
224
     *
225
     * @return bool True if the items were successfully removed. False if there was an error.
226
     *
227
     * @throws InvalidArgumentException
228
     *   MUST be thrown if $keys is neither an array nor a Traversable,
229
     *   or if any of the $keys are not a legal value.
230
     */
231
    public function deleteMultiple($keys)
232
    {
233
        $failure = false;
234
        if (!is_array($keys)) {
235
            throw new InvalidArgumentException();
236
        }
237
        foreach ($keys as $key) {
238
            if (!$this->delete($key)) {
239
                $failure = true;
240
            }
241
        }
242
        return (bool) !$failure;
243
    }
244
245
    /**
246
     * Determines whether an item is present in the cache.
247
     *
248
     * NOTE: It is recommended that has() is only to be used for cache warming type purposes
249
     * and not to be used within your live applications operations for get/set, as this method
250
     * is subject to a race condition where your has() will return true and immediately after,
251
     * another script can remove it making the state of your app out of date.
252
     *
253
     * @param string $key The cache item key.
254
     *
255
     * @return bool
256
     *
257
     * @throws InvalidArgumentException
258
     *   MUST be thrown if the $key string is not a legal value.
259
     */
260
    public function has($key)
261
    {
262
        $has = false;
263
        if (!is_string($this->filterValidateKey($key))) {
264
            throw new InvalidArgumentException();
265
        }
266
        $data = $this->getDataFromStorage();
267
        if (isset($data[$key])) {
268
            $has = true;
269
        }
270
        return $has;
271
    }
272
273
    /**
274
     *
275
     * @return array
276
     */
277
    protected function getDataFromStorage()
278
    {
279
        $dataArrayFromStorage = [];
280
        if (!$this->filesystem->has($this->namespace)) {
281
            $this->filesystem->write($this->namespace, json_encode([]));
282
        }
283
        $rawDataFromStorage = $this->filesystem->read($this->namespace);
284
        if ($rawDataFromStorage !== false) {
285
            $dataArrayFromStorage = json_decode($rawDataFromStorage, true);
286
        }
287
        unset($rawDataFromStorage);
288
        return $dataArrayFromStorage;
289
    }
290
291
    /**
292
     *
293
     * @param array $data
294
     * @return boolean
295
     */
296
    protected function saveDataToStorage($data)
297
    {
298
        return (bool) $this->filesystem->put($this->namespace, json_encode($data));
299
    }
300
301
    /**
302
     *
303
     * @param array $data
304
     * @return boolean
305
     */
306
    protected function deleteStorage()
307
    {
308
        if ($this->filesystem->has($this->namespace)) {
309
            return (bool) $this->filesystem->delete($this->namespace);
310
        }
311
        return true;
312
    }
313
314
    /**
315
     *
316
     * @param string $key
317
     * @return string|boolean
318
     */
319
    protected function filterValidateKey($key)
320
    {
321
        if (!is_string($key)) {
322
            return false;
323
        }
324
        return $key;
325
    }
326
}
327