FileSystemAbstract   C
last analyzed

Complexity

Total Complexity 58

Size/Duplication

Total Lines 331
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 5
dl 0
loc 331
ccs 135
cts 135
cp 1
rs 6.3005
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
dirExistsRaw() 0 1 ?
linkExistsRaw() 0 1 ?
fileExistsRaw() 0 1 ?
getFileCreationTimestampRaw() 0 1 ?
readFileRaw() 0 1 ?
writeFileRaw() 0 1 ?
copyFileRaw() 0 1 ?
deleteFileRaw() 0 1 ?
createLinkRaw() 0 1 ?
getLinkTargetRaw() 0 1 ?
createDirRaw() 0 1 ?
deleteDirRaw() 0 1 ?
readDirRaw() 0 1 ?
A __construct() 0 4 1
A dirExists() 0 4 1
A linkExists() 0 4 1
A fileExists() 0 6 2
A getFileCreationTimestamp() 0 10 2
A readFile() 0 10 2
A writeFile() 0 15 3
B copyFile() 0 26 5
A deleteFile() 0 16 4
A copyLink() 0 8 2
A createLink() 0 19 4
A getLinkTarget() 0 8 2
B createDir() 0 23 6
A deleteDir() 0 15 4
A readDir() 0 14 2
C copy() 0 37 8
A getAbsolutePath() 0 18 4
A getExtension() 0 6 1
A sanitizeDirPath() 0 4 1
A sanitizeFilePath() 0 4 1
A sanitizePath() 0 4 1
A isStrictMode() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like FileSystemAbstract 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 FileSystemAbstract, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Hgraca\FileSystem;
4
5
use Hgraca\FileSystem\Exception\DirNotFoundException;
6
use Hgraca\FileSystem\Exception\FileNotFoundException;
7
use Hgraca\FileSystem\Exception\InvalidPathException;
8
use Hgraca\FileSystem\Exception\PathIsDirException;
9
use Hgraca\FileSystem\Exception\PathIsFileException;
10
use Hgraca\FileSystem\Exception\PathIsLinkException;
11
use SplFileInfo;
12
13
abstract class FileSystemAbstract implements FileSystemInterface
14
{
15
    const STRICT = 0;
16
    const IDEMPOTENT = 1;
17
18
    private $mode;
19
20
    abstract protected function dirExistsRaw(string $path): bool;
21
22
    abstract protected function linkExistsRaw(string $path): bool;
23
24
    abstract protected function fileExistsRaw(string $path): bool;
25
26
    abstract protected function getFileCreationTimestampRaw(string $path): int;
27
28
    abstract protected function readFileRaw(string $path): string;
29
30
    abstract protected function writeFileRaw(string $path, string $content);
31
32
    abstract protected function copyFileRaw(string $sourcePath, string $destinationPath);
33
34
    abstract protected function deleteFileRaw(string $path);
35
36
    abstract protected function createLinkRaw(string $path, string $targetPath);
37
38
    abstract protected function getLinkTargetRaw(string $path): string;
39
40
    abstract protected function createDirRaw(string $path);
41
42
    abstract protected function deleteDirRaw(string $path);
43
44
    /**
45
     * @return string[] The array with the fine names
46
     */
47
    abstract protected function readDirRaw(string $path): array;
48
49 140
    public function __construct(int $mode = self::STRICT)
50
    {
51 140
        $this->mode = $mode;
52 140
    }
53
54 100
    public function dirExists(string $path): bool
55
    {
56 100
        return $this->dirExistsRaw($this->sanitizeDirPath($path));
57
    }
58
59 70
    public function linkExists(string $path): bool
60
    {
61 70
        return $this->linkExistsRaw($this->sanitizeFilePath($path));
62
    }
63
64 76
    public function fileExists(string $path): bool
65
    {
66 76
        $path = $this->sanitizeFilePath($path);
67
68 76
        return $this->fileExistsRaw($path) && !$this->linkExists($path);
69
    }
70
71 4
    public function getFileCreationTimestamp(string $path): int
72
    {
73 4
        $path = $this->sanitizeFilePath($path);
74
75 4
        if (!$this->fileExists($path)) {
76 2
            throw new FileNotFoundException("File not found: '$path'");
77
        }
78
79 2
        return $this->getFileCreationTimestampRaw($path);
80
    }
81
82 22
    public function readFile(string $path): string
83
    {
84 22
        $path = $this->sanitizeFilePath($path);
85
86 22
        if (!$this->fileExists($path)) {
87 6
            throw new FileNotFoundException("File not found: '$path'");
88
        }
89
90 16
        return $this->readFileRaw($path);
91
    }
92
93 12
    public function writeFile(string $path, string $content)
94
    {
95 12
        $path = $this->sanitizeFilePath($path);
96
97 12
        if ($this->dirExists($path)) {
98 4
            throw new PathIsDirException("The path '$path' already exists and is a dir.");
99
        }
100
101 8
        $dirPath = dirname($path);
102 8
        if (!$this->dirExists($dirPath)) {
103 4
            $this->createDir($dirPath);
104
        }
105
106 8
        $this->writeFileRaw($path, $content);
107 8
    }
108
109 14
    public function copyFile(string $sourcePath, string $destinationPath): bool
110
    {
111 14
        $sourcePath = $this->sanitizeFilePath($sourcePath);
112 14
        $destinationPath = $this->sanitizeFilePath($destinationPath);
113
114 14
        if (!$this->fileExists($sourcePath)) {
115 2
            throw new FileNotFoundException("File not found: '$sourcePath'");
116
        }
117
118 12
        if ($sourcePath === $destinationPath) {
119 2
            return true;
120
        }
121
122 10
        if ($this->dirExists($destinationPath)) {
123 2
            throw new PathIsDirException("The destination path '$destinationPath' already exists and is a dir.");
124
        }
125
126 8
        $dirPath = dirname($destinationPath);
127 8
        if (!$this->dirExists($dirPath)) {
128 2
            $this->createDir($dirPath);
129
        }
130
131 8
        $this->copyFileRaw($sourcePath, $destinationPath);
132
133 8
        return $this->fileExists($destinationPath);
134
    }
135
136 14
    public function deleteFile(string $path): bool
137
    {
138 14
        $path = $this->sanitizeFilePath($path);
139
140 14
        $fileExists = $this->fileExists($path);
141
142 14
        if ($this->isStrictMode() && !$fileExists) {
143 10
            throw new FileNotFoundException("File not found: '$path'");
144
        }
145
146 4
        if ($fileExists) {
147 4
            $this->deleteFileRaw($path);
148
        }
149
150 4
        return !$this->fileExists($path);
151
    }
152
153 12
    public function copyLink(string $path, string $toPath): bool
154
    {
155 12
        if (!$this->linkExists($path)) {
156 2
            throw new FileNotFoundException("Link not found: '$path'");
157
        }
158
159 10
        return $this->createLink($toPath, $this->getLinkTarget($path));
160
    }
161
162 18
    public function createLink(string $path, string $targetPath): bool
163
    {
164 18
        $path = $this->sanitizeFilePath($path);
165
166 18
        if ($this->linkExists($path)) {
167 4
            $this->deleteFile($path);
168
        }
169
170 14
        if ($this->fileExists($path)) {
171 4
            throw new PathIsFileException("The path '$path' already exists and is a file");
172
        }
173 10
        if ($this->dirExists($path)) {
174 4
            throw new PathIsDirException("The path '$path' already exists and is a dir");
175
        }
176
177 6
        $this->createLinkRaw($path, $targetPath);
178
179 6
        return $this->linkExists($path);
180
    }
181
182 20
    public function getLinkTarget(string $path): string
183
    {
184 20
        if (!$this->linkExists($path)) {
185 6
            throw new FileNotFoundException("Link not found: '$path'");
186
        }
187
188 14
        return $this->getLinkTargetRaw($path);
189
    }
190
191 16
    public function createDir(string $path): bool
192
    {
193 16
        $path = $this->sanitizeDirPath($path);
194
195 16
        if ($this->linkExists($path)) {
196 2
            throw new PathIsLinkException("The path '$path' already exists and is a file");
197
        }
198
199 14
        if ($this->fileExists($path)) {
200 2
            throw new PathIsFileException("The path '$path' already exists and is a file");
201
        }
202
203 12
        $dirExists = $this->dirExists($path);
204 12
        if ($this->isStrictMode() && $dirExists) {
205 2
            throw new PathIsDirException("The path '$path' already exists and is a dir");
206
        }
207
208 10
        if (!$dirExists) {
209 10
            $this->createDirRaw($path);
210
        }
211
212 10
        return $this->dirExists($path);
213
    }
214
215 75
    public function deleteDir(string $path): bool
216
    {
217 75
        $path = $this->sanitizeDirPath($path);
218
219 75
        $dirExists = $this->dirExists($path);
220 75
        if ($this->isStrictMode() && !$dirExists) {
221 4
            throw new DirNotFoundException();
222
        }
223
224 73
        if ($dirExists) {
225 73
            $this->deleteDirRaw($path);
226
        }
227
228 73
        return !$this->dirExists($path);
229
    }
230
231
    /**
232
     * @throws DirNotFoundException
233
     *
234
     * @return string[] The array with the file and dir names
235
     */
236 14
    public function readDir(string $path): array
237
    {
238 14
        $path = $this->sanitizeDirPath($path);
239
240 14
        if (!$this->dirExists($path)) {
241 4
            throw new DirNotFoundException("Dir not found: '$path'");
242
        }
243
244 10
        $result = $this->readDirRaw($path);
245
246 10
        sort($result);
247
248 10
        return $result;
249
    }
250
251 6
    public function copy(string $sourcePath, string $destinationPath): bool
252
    {
253 6
        if ($sourcePath === $destinationPath) {
254 2
            return true;
255
        }
256
257
        // Check for symlinks
258 4
        if ($this->linkExists($sourcePath)) {
259 2
            return $this->copyLink($sourcePath, $destinationPath);
260
        }
261
262
        // Simple copy for a file
263 4
        if ($this->fileExists($sourcePath)) {
264 4
            return $this->copyFile($sourcePath, $destinationPath);
265
        }
266
267
        // Make destination directory
268 4
        if ($this->dirExists($destinationPath)) {
269 2
            $this->deleteDir($destinationPath);
270
        }
271 4
        $this->createDir($destinationPath);
272
273
        // Loop through the folder
274 4
        foreach ($this->readDir($sourcePath) as $fileName) {
275
            // Skip pointers
276 4
            if ($fileName === '.' || $fileName === '..') {
277 4
                continue;
278
            }
279
280 4
            $this->copy(
281 4
                $this->sanitizeDirPath($sourcePath) . $fileName,
282 4
                $this->sanitizeDirPath($destinationPath) . $fileName
283
            );
284
        }
285
286 4
        return true;
287
    }
288
289 136
    public function getAbsolutePath(string $path): string
290
    {
291 136
        $path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
292 136
        $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
293 136
        $absolutes = [];
294 136
        foreach ($parts as $part) {
295 136
            if ('.' == $part) {
296 2
                continue;
297
            }
298 136
            if ('..' == $part) {
299 6
                array_pop($absolutes);
300
            } else {
301 136
                $absolutes[] = $part;
302
            }
303
        }
304
305 136
        return DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $absolutes);
306
    }
307
308 6
    public function getExtension(string $path): string
309
    {
310 6
        $path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
311
312 6
        return (new SplFileInfo($path))->getExtension();
313
    }
314
315
    /**
316
     * @throws InvalidPathException
317
     */
318 102
    protected function sanitizeDirPath(string $path): string
319
    {
320 102
        return $this->sanitizePath($path) . '/';
321
    }
322
323
    /**
324
     * @throws InvalidPathException
325
     */
326 98
    protected function sanitizeFilePath(string $path): string
327
    {
328 98
        return $this->sanitizePath($path);
329
    }
330
331
    /**
332
     * @throws InvalidPathException
333
     */
334 133
    protected function sanitizePath(string $path): string
335
    {
336 133
        return $this->getAbsolutePath(trim($path, " \t\n\r\0\x0B\\/"));
337
    }
338
339 87
    private function isStrictMode(): bool
340
    {
341 87
        return $this->mode === self::STRICT;
342
    }
343
}
344