Completed
Push — master ( ea3a48...b57255 )
by Lars
02:48
created

AdapterFile::removeAll()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

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