Completed
Push — master ( fd15a5...e11a6e )
by Lars
01:46
created

AdapterFileAbstract   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 291
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 77.78%

Importance

Changes 0
Metric Value
wmc 44
lcom 2
cbo 5
dl 0
loc 291
ccs 70
cts 90
cp 0.7778
rs 8.8798
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 4
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
B writeFile() 0 42 9
A validateDataFromCache() 0 14 4

How to fix   Complexity   

Complex Class

Complex classes like AdapterFileAbstract often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AdapterFileAbstract, and based on these observations, apply Extract Interface, too.

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
14
    const CACHE_FILE_SUBFIX = '.php.cache';
15
16
    /**
17
     * @var bool
18
     */
19
    public $installed = false;
20
21
    /**
22
     * @var string
23
     */
24
    protected $cacheDir;
25
26
    /**
27
     * @var iSerializer
28
     */
29
    protected $serializer;
30
31
    /**
32
     * @var string
33
     */
34
    protected $fileMode = '0755';
35
36
    /**
37
     * @param callable|string|null $cacheDir
38
     */
39 46
    public function __construct($cacheDir = null)
40
    {
41 46
        $this->serializer = new SerializerIgbinary();
42
43 46
        if (!$cacheDir) {
44 45
            $cacheDir = \realpath(\sys_get_temp_dir()) . '/simple_php_cache';
45
        }
46
47 46
        if (\is_callable($cacheDir)) {
48 1
            $this->cacheDir = (string) \call_user_func($cacheDir);
49
        } else {
50 45
            $this->cacheDir = (string) $cacheDir;
51
        }
52
53 46
        if ($this->createCacheDirectory($this->cacheDir) === true) {
54 46
            $this->installed = true;
55
        }
56 46
    }
57
58
    /**
59
     * Recursively creates & chmod directories.
60
     *
61
     * @param string $path
62
     *
63
     * @return bool
64
     */
65 46
    protected function createCacheDirectory($path): bool
66
    {
67
        if (
68 46
            !$path
69
            ||
70 46
            $path === '/'
71
            ||
72 46
            $path === '.'
73
            ||
74 46
            $path === '\\'
75
        ) {
76
            return false;
77
        }
78
79
        // if the directory already exists, just return true
80 46
        if (\is_dir($path) && \is_writable($path)) {
81 46
            return true;
82
        }
83
84
        // if more than one level, try parent first
85 2
        if (\dirname($path) !== '.') {
86 2
            $return = $this->createCacheDirectory(\dirname($path));
87
            // if creating parent fails, we can abort immediately
88 2
            if (!$return) {
89
                return false;
90
            }
91
        }
92
93 2
        $mode_dec = \intval($this->fileMode, 8);
94 2
        $old_umask = \umask(0);
95
96
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
97 2
        if (!@\mkdir($path, $mode_dec) && !\is_dir($path)) {
98
            $return = false;
99
        } else {
100 2
            $return = true;
101
        }
102
103 2
        if (\is_dir($path) && !\is_writable($path)) {
104
            $return = \chmod($path, $mode_dec);
105
        }
106
107 2
        \umask($old_umask);
108
109 2
        return $return;
110
    }
111
112
    /**
113
     * @param string $cacheFileWithPath
114
     *
115
     * @return bool
116
     */
117 15
    protected function deleteFile($cacheFileWithPath): bool
118
    {
119 15
        if (\is_file($cacheFileWithPath)) {
120 15
            return \unlink($cacheFileWithPath);
121
        }
122
123
        return false;
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129 7
    public function exists(string $key): bool
130
    {
131 7
        $value = $this->get($key);
132
133 7
        return $value !== null;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139
    abstract public function get(string $key);
140
141
    /**
142
     * {@inheritdoc}
143
     */
144 1
    public function installed(): bool
145
    {
146 1
        return $this->installed;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152 15
    public function remove(string $key): bool
153
    {
154 15
        $cacheFile = $this->getFileName($key);
155
156 15
        return $this->deleteFile($cacheFile);
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 4
    public function removeAll(): bool
163
    {
164 4
        if (!$this->cacheDir) {
165
            return false;
166
        }
167
168 4
        $return = [];
169 4
        foreach (new \DirectoryIterator($this->cacheDir) as $fileInfo) {
170 4
            if (!$fileInfo->isDot()) {
171 4
                $return[] = \unlink($fileInfo->getPathname());
172
            }
173
        }
174
175 4
        return \in_array(false, $return, true) === false;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181 13
    public function set(string $key, $value): bool
182
    {
183 13
        return $this->setExpired($key, $value);
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    abstract public function setExpired(string $key, $value, int $ttl = 0): bool;
190
191
    /**
192
     * @param string $key
193
     *
194
     * @return string
195
     */
196 16
    protected function getFileName(string $key): string
197
    {
198 16
        return $this->cacheDir . \DIRECTORY_SEPARATOR . self::CACHE_FILE_PREFIX . $key . self::CACHE_FILE_SUBFIX;
199
    }
200
201
    /**
202
     * Set the file-mode for new cache-files.
203
     *
204
     * e.g. '0777', or '0755' ...
205
     *
206
     * @param string $fileMode
207
     *
208
     * @return void
209
     */
210
    public function setFileMode($fileMode)
211
    {
212
        $this->fileMode = $fileMode;
213
    }
214
215
    /**
216
     * @param int $ttl
217
     *
218
     * @return bool
219
     */
220 29
    protected function ttlHasExpired(int $ttl): bool
221
    {
222 29
        if ($ttl === 0) {
223 17
            return false;
224
        }
225
226 12
        return \time() > $ttl;
227
    }
228
229
    /**
230
     * @param mixed $data
231
     *
232
     * @return bool
233
     */
234 29
    protected function validateDataFromCache($data): bool
235
    {
236 29
        if (!\is_array($data)) {
237
            return false;
238
        }
239
240 29
        foreach (['value', 'ttl'] as $missing) {
241 29
            if (!\array_key_exists($missing, $data)) {
242 29
                return false;
243
            }
244
        }
245
246 29
        return true;
247
    }
248
249
    /**
250
     * copy&past from https://github.com/webimpress/safe-writer (thx @michalbundyra)
251
     *
252
     * @param string   $file
253
     * @param string   $content
254
     * @param int|null $chmod
255
     *
256
     * @return bool
257
     */
258 28
    protected function writeFile($file, $content, $chmod = null): bool
259
    {
260 28
        if (!$file) {
261
            return false;
262
        }
263
264 28
        if ($chmod === null) {
265 28
            $chmod = \intval($this->fileMode, 8);
266
        }
267
268 28
        $dir = \dirname($file);
269
270 28
        $tmp = \tempnam($dir, 'wsw');
271 28
        if ($tmp === false) {
272
            throw Exception\RuntimeException::unableToCreateTemporaryFile($dir);
273
        }
274
275 28
        if (\file_put_contents($tmp, $content) === false) {
276
            \unlink($tmp);
277
278
            throw Exception\WriteContentException::unableToWriteContent($tmp);
279
        }
280
281 28
        if (\chmod($tmp, $chmod & ~\umask()) === false) {
282
            \unlink($tmp);
283
284
            throw Exception\ChmodException::unableToChangeChmod($tmp);
285
        }
286
287
        // On windows try again if rename was not successful but target file is writable.
288
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
289 28
        while (@\rename($tmp, $file) === false) {
290
            if (\is_writable($file) && \stripos(\PHP_OS, 'WIN') === 0) {
291
                continue;
292
            }
293
            \unlink($tmp);
294
295
            throw Exception\RenameException::unableToMoveFile($tmp, $file);
296
        }
297
298 28
        return true;
299
    }
300
}
301