Completed
Push — master ( 94f5e8...a88b9e )
by Lars
01:57 queued 11s
created

AdapterFileAbstract   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 76.67%

Importance

Changes 0
Metric Value
wmc 44
lcom 2
cbo 5
dl 0
loc 289
ccs 69
cts 90
cp 0.7667
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
A validateDataFromCache() 0 14 4
B writeFile() 0 42 9

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 34
    public function __construct($cacheDir = null)
40
    {
41 34
        $this->serializer = new SerializerIgbinary();
42
43 34
        if (!$cacheDir) {
44 33
            $cacheDir = \realpath(\sys_get_temp_dir()) . '/simple_php_cache';
45
        }
46
47 34
        if (\is_callable($cacheDir)) {
48 1
            $this->cacheDir = (string) \call_user_func($cacheDir);
49
        } else {
50 33
            $this->cacheDir = (string) $cacheDir;
51
        }
52
53 34
        if ($this->createCacheDirectory($this->cacheDir) === true) {
54 34
            $this->installed = true;
55
        }
56 34
    }
57
58
    /**
59
     * Recursively creates & chmod directories.
60
     *
61
     * @param string $path
62
     *
63
     * @return bool
64
     */
65 34
    protected function createCacheDirectory($path): bool
66
    {
67
        if (
68 34
            !$path
69
            ||
70 34
            $path === '/'
71
            ||
72 34
            $path === '.'
73
            ||
74 34
            $path === '\\'
75
        ) {
76
            return false;
77
        }
78
79
        // if the directory already exists, just return true
80 34
        if (\is_dir($path) && \is_writable($path)) {
81 34
            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 12
    protected function deleteFile($cacheFileWithPath): bool
118
    {
119 12
        if (\is_file($cacheFileWithPath)) {
120 12
            return \unlink($cacheFileWithPath);
121
        }
122
123
        return false;
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129 6
    public function exists(string $key): bool
130
    {
131 6
        $value = $this->get($key);
132
133 6
        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 12
    public function remove(string $key): bool
153
    {
154 12
        $cacheFile = $this->getFileName($key);
155
156 12
        return $this->deleteFile($cacheFile);
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 3
    public function removeAll(): bool
163
    {
164 3
        if (!$this->cacheDir) {
165
            return false;
166
        }
167
168 3
        $return = [];
169 3
        foreach (new \DirectoryIterator($this->cacheDir) as $fileInfo) {
170 3
            if (!$fileInfo->isDot()) {
171 3
                $return[] = \unlink($fileInfo->getPathname());
172
            }
173
        }
174
175 3
        return \in_array(false, $return, true) === false;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181 9
    public function set(string $key, $value): bool
182
    {
183 9
        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
    public function setFileMode($fileMode)
209
    {
210
        $this->fileMode = $fileMode;
211
    }
212
213
    /**
214
     * @param int $ttl
215
     *
216
     * @return bool
217
     */
218 22
    protected function ttlHasExpired(int $ttl): bool
219
    {
220 22
        if ($ttl === 0) {
221 12
            return false;
222
        }
223
224 10
        return \time() > $ttl;
225
    }
226
227
    /**
228
     * @param mixed $data
229
     *
230
     * @return bool
231
     */
232 22
    protected function validateDataFromCache($data): bool
233
    {
234 22
        if (!\is_array($data)) {
235
            return false;
236
        }
237
238 22
        foreach (['value', 'ttl'] as $missing) {
239 22
            if (!\array_key_exists($missing, $data)) {
240
                return false;
241
            }
242
        }
243
244 22
        return true;
245
    }
246
247
    /**
248
     * copy&past from https://github.com/webimpress/safe-writer (thx @michalbundyra)
249
     *
250
     * @param string   $file
251
     * @param string   $content
252
     * @param int|null $chmod
253
     *
254
     * @return bool
255
     */
256 21
    protected function writeFile($file, $content, $chmod = null): bool
257
    {
258 21
        if (!$file) {
259
            return false;
260
        }
261
262 21
        if ($chmod === null) {
263 21
            $chmod = \intval($this->fileMode, 8);
264
        }
265
266 21
        $dir = \dirname($file);
267
268 21
        $tmp = \tempnam($dir, 'wsw');
269 21
        if ($tmp === false) {
270
            throw Exception\RuntimeException::unableToCreateTemporaryFile($dir);
271
        }
272
273 21
        if (\file_put_contents($tmp, $content) === false) {
274
            \unlink($tmp);
275
276
            throw Exception\WriteContentException::unableToWriteContent($tmp);
277
        }
278
279 21
        if (\chmod($tmp, $chmod & ~\umask()) === false) {
280
            \unlink($tmp);
281
282
            throw Exception\ChmodException::unableToChangeChmod($tmp);
283
        }
284
285
        // On windows try again if rename was not successful but target file is writable.
286
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
287 21
        while (@\rename($tmp, $file) === false) {
288
            if (\is_writable($file) && \stripos(\PHP_OS, 'WIN') === 0) {
289
                continue;
290
            }
291
            \unlink($tmp);
292
293
            throw Exception\RenameException::unableToMoveFile($tmp, $file);
294
        }
295
296 21
        return true;
297
    }
298
}
299