Completed
Push — master ( 097ee4...b7f9bf )
by Lars
02:18
created

AdapterFileAbstract   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 1

Importance

Changes 0
Metric Value
wmc 34
lcom 2
cbo 1
dl 0
loc 232
rs 9.68
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 3
C createCacheDirectory() 0 46 13
A deleteFile() 0 8 2
A exists() 0 6 1
get() 0 1 ?
A installed() 0 4 1
A remove() 0 6 1
A removeAll() 0 15 4
A set() 0 4 1
setExpired() 0 1 ?
A getFileName() 0 4 1
A setFileMode() 0 4 1
A ttlHasExpired() 0 8 2
A validateDataFromCache() 0 14 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace voku\cache;
6
7
/**
8
 * AdapterFileSimple: File-adapter (simple)
9
 */
10
abstract class AdapterFileAbstract 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|null $cacheDir
37
   */
38
  public function __construct($cacheDir = null)
39
  {
40
    $this->serializer = new SerializerIgbinary();
41
42
    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
      $cacheDir = \realpath(\sys_get_temp_dir()) . '/simple_php_cache';
44
    }
45
46
    $this->cacheDir = (string)$cacheDir;
47
48
    if ($this->createCacheDirectory($cacheDir) === true) {
49
      $this->installed = true;
50
    }
51
  }
52
53
  /**
54
   * Recursively creates & chmod directories.
55
   *
56
   * @param string $path
57
   *
58
   * @return bool
59
   */
60
  protected function createCacheDirectory($path): bool
61
  {
62
    if (
63
        !$path
64
        ||
65
        $path === '/'
66
        ||
67
        $path === '.'
68
        ||
69
        $path === '\\'
70
    ) {
71
      return false;
72
    }
73
74
    // if the directory already exists, just return true
75
    if (\is_dir($path) && \is_writable($path)) {
76
      return true;
77
    }
78
79
    // if more than one level, try parent first
80
    if (\dirname($path) !== '.') {
81
      $return = $this->createCacheDirectory(\dirname($path));
82
      // if creating parent fails, we can abort immediately
83
      if (!$return) {
84
        return false;
85
      }
86
    }
87
88
    $mode_dec = \intval($this->fileMode, 8);
89
    $old_umask = \umask(0);
90
91
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
92
    if (!@\mkdir($path, $mode_dec) && !\is_dir($path)) {
93
      $return = false;
94
    } else {
95
      $return = true;
96
    }
97
98
    if (\is_dir($path) && !\is_writable($path)) {
99
      $return = \chmod($path, $mode_dec);
100
    }
101
102
    \umask($old_umask);
103
104
    return $return;
105
  }
106
107
  /**
108
   * @param $cacheFile
109
   *
110
   * @return bool
111
   */
112
  protected function deleteFile($cacheFile): bool
113
  {
114
    if (\is_file($cacheFile)) {
115
      return \unlink($cacheFile);
116
    }
117
118
    return false;
119
  }
120
121
  /**
122
   * @inheritdoc
123
   */
124
  public function exists(string $key): bool
125
  {
126
    $value = $this->get($key);
127
128
    return null !== $value;
129
  }
130
131
  /**
132
   * @inheritdoc
133
   */
134
  abstract public function get(string $key);
135
136
  /**
137
   * @inheritdoc
138
   */
139
  public function installed(): bool
140
  {
141
    return $this->installed;
142
  }
143
144
  /**
145
   * @inheritdoc
146
   */
147
  public function remove(string $key): bool
148
  {
149
    $cacheFile = $this->getFileName($key);
150
151
    return $this->deleteFile($cacheFile);
152
  }
153
154
  /**
155
   * @inheritdoc
156
   */
157
  public function removeAll(): bool
158
  {
159
    if (!$this->cacheDir) {
160
      return false;
161
    }
162
163
    $return = [];
164
    foreach (new \DirectoryIterator($this->cacheDir) as $fileInfo) {
165
      if (!$fileInfo->isDot()) {
166
        $return[] = \unlink($fileInfo->getPathname());
167
      }
168
    }
169
170
    return \in_array(false, $return, true) === false;
171
  }
172
173
  /**
174
   * @inheritdoc
175
   */
176
  public function set(string $key, $value): bool
177
  {
178
    return $this->setExpired($key, $value);
179
  }
180
181
  /**
182
   * @inheritdoc
183
   */
184
  abstract public function setExpired(string $key, $value, int $ttl = 0): bool;
185
186
  /**
187
   * @param string $key
188
   *
189
   * @return string
190
   */
191
  protected function getFileName(string $key): string
192
  {
193
    return $this->cacheDir . DIRECTORY_SEPARATOR . self::CACHE_FILE_PREFIX . $key . self::CACHE_FILE_SUBFIX;
194
  }
195
196
  /**
197
   * Set the file-mode for new cache-files.
198
   *
199
   * e.g. '0777', or '0755' ...
200
   *
201
   * @param $fileMode
202
   */
203
  public function setFileMode($fileMode)
204
  {
205
    $this->fileMode = $fileMode;
206
  }
207
208
  /**
209
   * @param $ttl
210
   *
211
   * @return bool
212
   */
213
  protected function ttlHasExpired(int $ttl): bool
214
  {
215
    if ($ttl === 0) {
216
      return false;
217
    }
218
219
    return (\time() > $ttl);
220
  }
221
222
  /**
223
   * @param mixed $data
224
   *
225
   * @return bool
226
   */
227
  protected function validateDataFromCache($data): bool
228
  {
229
    if (!\is_array($data)) {
230
      return false;
231
    }
232
233
    foreach (['value', 'ttl'] as $missing) {
234
      if (!\array_key_exists($missing, $data)) {
235
        return false;
236
      }
237
    }
238
239
    return true;
240
  }
241
}
242