Completed
Branch master (1afc22)
by Dan
09:51 queued 07:56
created

FileStorage::__construct()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 3
nop 2
crap 4
1
<?php
2
/**
3
 * Src/Cache/Storage/FileStorage.php
4
 *
5
 * @package     Ds\Cache\Storage
6
 * @subpackage  Cache
7
 * @author      Dan Smith <[email protected]>
8
 * @version     v.1 (20/03/2017)
9
 * @copyright   Copyright (c) 2017, Dan Smith
10
 */
11
namespace Ds\Cache\Storage;
12
13
use \DateInterval;
14
use Ds\Cache\CacheException;
15
use Ds\Cache\InvalidArgumentException;
16
17
/**
18
 * Class NullStorage
19
 *
20
 * @package Ds\Cache
21
 */
22
class FileStorage extends AbstractStorage
23
{
24
    /**
25
     * @var string
26
     */
27
    const EXTENSION = 'cache';
28
29
    /**
30
     * @var string
31
     */
32
    private $dir;
33
34
    /**
35
     * @var bool|array
36
     */
37
    private $previousRequest;
38
39
    /**
40
     * File Storage constructor.
41
     *
42
     * @param string $directory
43
     * @param DateInterval $ttl
44
     * @throws \Exception
45
     */
46 14
    public function __construct($directory, DateInterval $ttl)
47
    {
48 14
        if (!is_string($directory)){
49 1
            throw new InvalidArgumentException('Directory must be a valid string');
50
        }
51
52 14
        $this->dir = $directory;
53
54 14
        if(!@mkdir($this->dir, 0755) && !is_dir($this->dir)){
55 1
            throw new CacheException('unable to create directory');
56
        }
57
58 14
        parent::__construct($ttl);
59 14
    }
60
61
    /**
62
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
63
     *
64
     * @param \string   $key   The key of the item to store.
65
     * @param mixed    $value The value of the item to store, must be serializable.
66
     * @param null|\int $ttl   Optional. The TTL value of this item. If no value is sent and
67
     *                                     the driver supports TTL then the library may set a default value
68
     *                                     for it or let the driver take care of that.
69
     *
70
     * @return bool True on success and false on failure.
71
     * @throws InvalidArgumentException
72
     */
73 6
    public function set($key, $value, $ttl = null)
74
    {
75 6
        $ttl = time() + $this->getTtlTimestamp($ttl);
76 6
        $cacheItem = $this->_createCacheItem($key, $value, $ttl);
77
78 6
        if (null !== $value){
79 6
            \file_put_contents($cacheItem['file'],$cacheItem['data']);
80 6
            if (!file_exists($cacheItem['file'])){
81
                return false;
82
            }
83
        }
84
85 6
        return true;
86
    }
87
88
89
    /**
90
     * Determines whether an item is present in the cache.
91
     *
92
     * NOTE: It is recommended that has() is only to be used for cache warming type purposes
93
     * and not to be used within your live applications operations for get/set, as this method
94
     * is subject to a race condition where your has() will return true and immediately after,
95
     * another script can remove it making the state of your app out of date.
96
     *
97
     * @param string $key The cache item key.
98
     *
99
     * @return bool
100
     */
101 5
    public function has($key){
102 5
       if ($this->_fetchCacheFile($key)){
103 2
         return true;
104
       }
105 3
       return false;
106
    }
107
108
    /**
109
     * Fetches a value from the cache.
110
     *
111
     * @param string $key     The unique key of this item in the cache.
112
     *
113
     * @return mixed The value of the item from the cache, or $default in case of cache miss.
114
     *
115
     */
116 1
    public function get($key){
117 1
        if ($this->has($key)){
118 1
            return $this->previousRequest[1];
119
        }
120
        return null;
121
    }
122
123
    /**
124
     * Delete an item from the cache by its unique key.
125
     *
126
     * @param string $key The unique cache key of the item to delete.
127
     *
128
     * @return bool True if the item was successfully removed. False if there was an error.
129
     */
130 1
    public function delete($key)
131
    {
132 1
        return true;
133
    }
134
135
    /**
136
     * Wipes clean the entire cache's keys.
137
     *
138
     * @return bool True on success and false on failure.
139
     */
140 1
    public function clear(){
141 1
        if (is_dir( $this->dir )){
142 1
            array_map('unlink', glob("$this->dir/*.*"));
143
        }
144
145 1
        $data = glob("$this->dir/*.*");
146
147 1
        if (!empty($data)){
148
            return false;
149
        }
150
151 1
        return true;
152
    }
153
154
    /**
155
     * Cache is only called one for both has/get via this internal method.
156
     *
157
     * @param $key
158
     * @return bool|mixed
159
     * @internal
160
     */
161 8
    private function _fetchCacheFile($key){
162
163 8
        $cacheFile = implode('.',[$key, FileStorage::EXTENSION]);
164 8
        $cacheLocation = $this->dir . DIRECTORY_SEPARATOR . $cacheFile;
165 8
        $this->previousRequest = false;
166
167 8
        if (file_exists($cacheLocation)){
168
169 5
            $data = \file_get_contents($cacheLocation);
170 5
            $cacheData = json_decode($data, TRUE);
171 5
            $this->previousRequest = $cacheData;
172
173
            //cache has expired
174 5
            if ( $cacheData[0] < time() ){
175 2
                \unlink($cacheLocation);
176 2
                $this->previousRequest = false;
177
            }
178
        }
179 8
        return $this->previousRequest;
180
    }
181
182
    /**
183
     * Create cache item.
184
     *
185
     * @param string $key
186
     * @param $value
187
     * @param int $ttl
188
     * @return array
189
     * @throws InvalidArgumentException
190
     */
191 8
    private function _createCacheItem($key, $value, $ttl){
192
193 8
        if (preg_match('/[\\/\\*\\\\\?.]+/i', $key)){
194 1
            throw new InvalidArgumentException('$key must be a valid string');
195
        }
196
197 7
        $filename = implode('.',[(string)$key, FileStorage::EXTENSION]);
198
199
        return [
200 7
            'file' => $this->dir . DIRECTORY_SEPARATOR . $filename,
201 7
            'data' => json_encode([$ttl, $value])
202
        ];
203
    }
204
}
205