Completed
Push — master ( bf8a34...c83bbf )
by Lars
03:00
created

AdapterFile   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 260
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 69.88%

Importance

Changes 2
Bugs 1 Features 1
Metric Value
wmc 38
c 2
b 1
f 1
lcom 2
cbo 2
dl 0
loc 260
ccs 58
cts 83
cp 0.6988
rs 8.3999

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 3
A remove() 0 6 1
B get() 0 22 5
A validateDataFromCache() 0 14 4
A exists() 0 4 1
A set() 0 4 1
A setExpired() 0 15 3
C createCacheDirectory() 0 45 13
A deleteFile() 0 8 2
A getFileName() 0 4 1
A ttlHasExpired() 0 8 2
A setFileMode() 0 4 1
A installed() 0 4 1
1
<?php
2
3
namespace voku\cache;
4
5
/**
6
 * AdapterFile: File-adapter
7
 *
8
 * @package   voku\cache
9
 */
10
class AdapterFile implements iAdapter
11
{
12
  const CACHE_FILE_PREFIX = '__';
13
  const CACHE_FILE_SUBFIX = '.php.cache';
14
15
  /**
16
   * @var bool
17
   */
18
  public $installed = false;
19
20
  /**
21
   * @var string
22
   */
23
  protected $cacheDir;
24
25
  /**
26
   * @var iSerializer
27
   */
28
  protected $serializer;
29
30
  /**
31
   * @var string
32
   */
33
  protected $fileMode = '0755';
34
35
  /**
36
   * @param string $cacheDir
37
   */
38 6
  public function __construct($cacheDir = null)
39
  {
40 6
    $this->serializer = new SerializerIgbinary();
41
42 6
    if (!$cacheDir) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cacheDir of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
43 6
      $cacheDir = realpath(sys_get_temp_dir()) . '/cache';
44 6
    }
45
46 6
    $this->cacheDir = (string)$cacheDir;
47
48 6
    if ($this->createCacheDirectory($cacheDir) === true) {
49 6
      $this->installed = true;
50 6
    }
51 6
  }
52
53
  /**
54
   * @param string $key
55
   *
56
   * @param        $key
57
   *
58
   * @return bool
59
   */
60
  public function remove($key)
61
  {
62
    $cacheFile = $this->getFileName($key);
63
64
    return $this->deleteFile($cacheFile);
65
  }
66
67
  /**
68
   * @param string $key
69
   *
70
   * @return mixed
71
   */
72 4
  public function get($key)
73
  {
74 4
    $path = $this->getFileName($key);
75
76 4
    if (!file_exists($path)) {
77
      return null;
78
    }
79
80 4
    $data = $this->serializer->unserialize(file_get_contents($path));
81
82 4
    if (!$data || !$this->validateDataFromCache($data)) {
83
      return null;
84
    }
85
86 4
    if ($this->ttlHasExpired($data['ttl']) === true) {
87
      $this->remove($key);
88
      
89
      return null;
90
    }
91
92 4
    return $data['value'];
93
  }
94
95
  /**
96
   * @param $data
97
   *
98
   * @return bool
99
   */
100 4
  protected function validateDataFromCache($data)
101
  {
102 4
    if (!is_array($data)) {
103
      return false;
104
    }
105
106 4
    foreach (array('value', 'ttl') as $missing) {
107 4
      if (!array_key_exists($missing, $data)) {
108
        return false;
109
      }
110 4
    }
111
112 4
    return true;
113
  }
114
115
  /**
116
   * @param string $key
117
   *
118
   * @return bool
119
   */
120 1
  public function exists($key)
121
  {
122 1
    return null !== $this->get($key);
123
  }
124
125
  /**
126
   * @param string $key
127
   * @param mixed  $value
128
   *
129
   * @return bool
130
   */
131 2
  public function set($key, $value)
132
  {
133 2
    return $this->setExpired($key, $value);
134
  }
135
136
  /**
137
   * @param string $key
138
   * @param mixed  $value
139
   * @param int    $ttl
140
   *
141
   * @return bool
142
   */
143 3
  public function setExpired($key, $value, $ttl = 0)
144
  {
145 3
    $item = $this->serializer->serialize(
146
        array(
147 3
            'value' => $value,
148 3
            'ttl'   => $ttl ? (int)$ttl + time() : 0,
149
        )
150 3
    );
151
152 3
    if (!file_put_contents($this->getFileName($key), $item)) {
153
      return false;
154
    }
155
156 3
    return true;
157
  }
158
159
  /**
160
   * recursively creates & chmod directories
161
   *
162
   * @param string $path
163
   *
164
   * @return bool
165
   */
166 6
  protected function createCacheDirectory($path)
167
  {
168
    if (
169
        !$path
170 6
        ||
171
        $path === '/'
172 6
        ||
173
        $path === '.'
174 6
        ||
175
        $path === '\\'
176 6
    ) {
177
      return false;
178
    }
179
180
    // if the directory already exists, just return true
181 6
    if (is_dir($path) && is_writable($path)) {
182 6
      return true;
183
    }
184
185
    // if more than one level, try parent first
186 1
    if (dirname($path) !== '.') {
187 1
      $return = $this->createCacheDirectory(dirname($path));
188
      // if creating parent fails, we can abort immediately
189 1
      if (!$return) {
190
        return false;
191
      }
192 1
    }
193
194 1
    $mode_dec = intval($this->fileMode, 8);
195 1
    $oldumask = umask(0);
196
197 1
    if (!@mkdir($path, $mode_dec) && !is_dir($path)) {
198
      $return = false;
199
    } else {
200 1
      $return = true;
201
    }
202
203 1
    if (is_dir($path) && !is_writable($path)) {
204
      $return = chmod($path, $mode_dec);
205
    }
206
207 1
    umask($oldumask);
208
209 1
    return $return;
210
  }
211
212
  /**
213
   * @param $cacheFile
214
   *
215
   * @return bool
216
   */
217
  protected function deleteFile($cacheFile)
218
  {
219
    if (is_file($cacheFile)) {
220
      return unlink($cacheFile);
221
    }
222
223
    return false;
224
  }
225
226
  /**
227
   * @param string $key
228
   *
229
   * @return string
230
   */
231 5
  protected function getFileName($key)
232
  {
233 5
    return $this->cacheDir . DIRECTORY_SEPARATOR . self::CACHE_FILE_PREFIX . $key . self::CACHE_FILE_SUBFIX;
234
  }
235
236
  /**
237
   * @param $ttl
238
   *
239
   * @return bool
240
   */
241 4
  protected function ttlHasExpired($ttl)
242
  {
243 4
    if ($ttl === 0) {
244 3
      return false;
245
    }
246
247 1
    return (time() > $ttl);
248
  }
249
250
  /**
251
   * e.g. '0777', or '0755' ...
252
   *
253
   * @param $fileMode
254
   */
255
  public function setFileMode($fileMode)
256
  {
257
    $this->fileMode = $fileMode;
258
  }
259
260
  /**
261
   * check if cache is installed
262
   *
263
   * @return boolean
264
   */
265
  public function installed()
266
  {
267
    return $this->installed;
268
  }
269
}
270