AbstractFile::removeRecursively()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.1502

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 11
nc 5
nop 1
dl 0
loc 20
ccs 9
cts 11
cp 0.8182
crap 5.1502
rs 9.6111
c 1
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Cache package.
5
 *
6
 * Copyright (c) Daniel González
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @author Daniel González <[email protected]>
12
 * @author Arnold Daniels <[email protected]>
13
 */
14
15
namespace Desarrolla2\Cache;
16
17
use Desarrolla2\Cache\Exception\InvalidArgumentException;
18
use Desarrolla2\Cache\Option\FilenameTrait as FilenameOption;
19
20
/**
21
 * Abstract class for using files as cache.
22
 *
23
 * @package Desarrolla2\Cache
24
 */
25
abstract class AbstractFile extends AbstractCache
26
{
27
    use FilenameOption;
28
29
    /**
30
     * @var string
31
     */
32
    protected $cacheDir;
33
34
35
    /**
36
     * Class constructor
37
     *
38
     * @param string|null $cacheDir
39
     */
40 788
    public function __construct(?string $cacheDir = null)
41
    {
42 788
        if (!$cacheDir) {
43
            $cacheDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'cache';
44
            if(!is_dir($cacheDir)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
45
                mkdir($cacheDir, 0777, true);
46
            }
47
        }
48
49 788
        $this->cacheDir = rtrim($cacheDir, '/');
50
    }
51
52
    /**
53
     * Validate the key
54
     *
55
     * @param string $key
56
     * @return void
57
     * @throws InvalidArgumentException
58
     */
59 756
    protected function assertKey($key): void
60
    {
61 756
        parent::assertKey($key);
62
63 468
        if (strpos($key, '*')) {
64
            throw new InvalidArgumentException("Key may not contain the character '*'");
65
        }
66
    }
67
68
69
    /**
70
     * Get the contents of the cache file.
71
     *
72
     * @param string $cacheFile
73
     * @return string
74
     */
75 108
    protected function readFile(string $cacheFile): string
76
    {
77 108
        return file_get_contents($cacheFile);
78
    }
79
80
    /**
81
     * Read the first line of the cache file.
82
     *
83
     * @param string $cacheFile
84
     * @return string
85
     */
86 78
    protected function readLine(string $cacheFile): string
87
    {
88 78
        $fp = fopen($cacheFile, 'r');
89 78
        $line = fgets($fp);
90 78
        fclose($fp);
91
92 78
        return $line;
93
    }
94
95
    /**
96
     * Create a cache file
97
     *
98
     * @param string $cacheFile
99
     * @param string $contents
100
     * @return bool
101
     */
102 240
    protected function writeFile(string $cacheFile, string $contents): bool
103
    {
104 240
        $dir = dirname($cacheFile);
105
106 240
        if ($dir !== $this->cacheDir && !is_dir($dir)) {
107 60
            mkdir($dir, 0775, true);
108
        }
109
110 240
        return (bool)file_put_contents($cacheFile, $contents);
111
    }
112
113
    /**
114
     * Delete a cache file
115
     *
116
     * @param string $file
117
     * @return bool
118
     */
119 312
    protected function deleteFile(string $file): bool
120
    {
121 312
        return !is_file($file) || unlink($file);
122
    }
123
124
    /**
125
     * Remove all files from a directory.
126
     */
127 788
    protected function removeFiles(string $dir): bool
128
    {
129 788
        $success = true;
130
131 788
        $generator = $this->getFilenameOption();
132 788
        $objects = $this->streamSafeGlob($dir, $generator('*'));
133
134 788
        foreach ($objects as $object) {
135 220
            $success = $this->deleteFile($object) && $success;
136
        }
137
138 788
        return $success;
139
    }
140
141
    /**
142
     * Recursive delete an empty directory.
143
     *
144
     * @param string $dir
145
     */
146 788
    protected function removeRecursively(string $dir): bool
147
    {
148 788
        $success = $this->removeFiles($dir);
149
150 788
        $objects = $this->streamSafeGlob($dir, '*');
151
152 788
        foreach ($objects as $object) {
153 60
            if (!is_dir($object)) {
154
                continue;
155
            }
156
157 60
            if (is_link($object)) {
158
                unlink($object);
159
            } else {
160 60
                $success = $this->removeRecursively($object) && $success;
161 60
                rmdir($object);
162
            }
163
        }
164
165 788
        return $success;
166
    }
167
168
169
    /**
170
     * {@inheritdoc}
171
     */
172 156
    public function delete($key)
173
    {
174 156
        $cacheFile = $this->getFilename($key);
175
176 84
        return $this->deleteFile($cacheFile);
177
    }
178
179
    /**
180
     * Delete cache directory.
181
     *
182
     * {@inheritdoc}
183
     */
184 788
    public function clear()
185
    {
186 788
        $this->removeRecursively($this->cacheDir);
187
188 788
        return true;
189
    }
190
191
    /**
192
     * Glob that is safe with streams (vfs for example)
193
     *
194
     * @param string $directory
195
     * @param string $filePattern
196
     * @return array
197
     */
198 788
    protected function streamSafeGlob(string $directory, string $filePattern): array
199
    {
200 788
        $filePattern = basename($filePattern);
201 788
        $files = scandir($directory);
202 788
        $found = [];
203
204 788
        foreach ($files as $filename) {
205 788
            if (in_array($filename, ['.', '..'])) {
206 788
                continue;
207
            }
208
209 227
            if (fnmatch($filePattern, $filename) || fnmatch($filePattern . '.ttl', $filename)) {
210 227
                $found[] = "{$directory}/{$filename}";
211
            }
212
        }
213
214 788
        return $found;
215
    }
216
}
217