Passed
Pull Request — master (#5)
by Deric
01:55
created

Filesystem   D

Complexity

Total Complexity 60

Size/Duplication

Total Lines 376
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 376
rs 4.2857
c 0
b 0
f 0
wmc 60

14 Methods

Rating   Name   Duplication   Size   Complexity  
A copy() 0 16 3
A chgrp() 0 9 3
A rename() 0 17 3
A lsdir() 0 17 4
B get_error_message() 0 13 9
C rmdir() 0 28 7
A tmpfile() 0 5 3
A tmpdir() 0 5 2
B delete() 0 17 5
B mkdir() 0 14 5
A lsfile() 0 17 4
B readdir() 0 14 5
A glob() 0 13 4
A chmod() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like Filesystem 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.

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 Filesystem, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Filesystem;
3
4
use Filesystem\Exceptions\FileModeException;
5
use Filesystem\Exceptions\FileCopyException;
6
use Filesystem\Exceptions\FileDeleteException;
7
use Filesystem\Exceptions\DirectoryCreateException;
8
use Filesystem\Exceptions\DirectoryDeleteException;
9
use Filesystem\Exceptions\FileMoveException;
10
use Filesystem\Exceptions\FileExistException;
11
12
/**
13
 * Filesystem class.
14
 *
15
 * @package Filesystem
16
 */
17
class Filesystem
18
{
19
20
    /**
21
     * FileModeException code.
22
     *
23
     * @var int
24
     */
25
    const FILEMODE = 10;
26
27
    /**
28
     * FileCopyException code.
29
     *
30
     * @var int
31
     */
32
    const FILECOPY = 11;
33
34
    /**
35
     * FileDeleteException code.
36
     *
37
     * @var int
38
     */
39
    const FILEDELETE = 12;
40
41
    /**
42
     * DirectoryCreateException code.
43
     *
44
     * @var int
45
     */
46
    const DIRECTORYCREATE = 13;
47
48
    /**
49
     * DirectoryDeleteException code.
50
     *
51
     * @var int
52
     */
53
    const DIRECTORYDELETE = 14;
54
55
    /**
56
     * FileMoveException code.
57
     *
58
     * @var int
59
     */
60
    const FILEMOVE = 15;
61
62
    /**
63
     * FileExistException code.
64
     *
65
     * @var int
66
     */
67
    const FILEEXISTS = 16;
68
69
    /**
70
     * Changes file mode.
71
     *
72
     * @param  string $filename  path to the file
73
     * @param  int 	  $mode 	 file mode
74
     * @param  bool   $recursive recursive file mode change
75
     * @return mixed  true on success exception or error message on failure
76
     */
77
    final public static function chmod($filename, $mode, $recursive = false, $strict = true)
78
    {
79
        $recursive = ($recursive) ? "-R" : "";
80
        $mode = (int) $mode;
81
        $retval = shell_exec("chmod {$recursive} {$mode} {$filename}; echo $?");
82
        if ($retval != 0)
83
        {
84
            $code = self::FILEMODE;
85
            $message = "Failed to set file mode for {$filename}";
86
            return self::get_error_message($code, $message, $strict);
87
        }
88
        return true;
89
    }
90
91
    /**
92
     * Changes group of file.
93
     *
94
     * @param  string $filename  path to the file
95
     * @param  int 	  $group 	 file group
96
     * @param  bool   $recursive recursive file mode change
97
     * @return mixed  true on success exception or error message on failure
98
     */
99
    final public static function chgrp($filename, $group, $recursive = false, $strict = true)
100
    {
101
        $recursive = ($recursive) ? "-R " : "";
102
        $retval = shell_exec("chgrp {$recursive} {$group} {$filename}; echo $?");
103
        if ($retval != 0)
104
        {
105
            $code = self::FILEMODE;
106
            $message = "Failed to set file group for {$filename} to {$group}";
107
            return self::get_error_message($code, $message, $strict);
108
        }
109
    }
110
111
    /**
112
     * Copies file.
113
     *
114
     * @param  string $srcfile  path to the source file
115
     * @param  string $destfile path to the destination file
116
     * @return mixed  true on success exception or error message on failure
117
     */
118
    final public static function copy($srcfile, $destfile)
119
    {
120
        if (is_file($srcfile) === false)
121
        {
122
            $code = self::FILEEXISTS;
123
            $message = "Original (source) file does not exist {$srcfile}";
124
            return self::get_error_message($code, $message, true);
125
        }
126
127
        if (@copy($srcfile, $destfile) === false)
128
        {
129
            $code = self::FILECOPY;
130
            $message = "Failed to create copy of {$srcfile} to {$destfile}";
131
            return self::get_error_message($code, $message, true);
132
        }
133
        return true;
134
    }
135
136
    /**
137
     * Removes files.
138
     *
139
     * @param  mixed $files  one or more files to delete
140
     * @param  bool  $strict strict error checking
141
     * @return bool
142
     */
143
    final public static function delete($files, $strict = true)
144
    {
145
        $files = !is_array($files) ? [$files] : $files;
146
        foreach ($files as $file)
147
        {
148
            if (is_file($file))
149
            {
150
                if (@unlink($file) === false)
151
                {
152
                    $code = self::FILEDELETE;
153
                    $message = "Failed to delete {$file}";
154
                    return self::get_error_message($code, $message, $strict);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::get_error_m...ode, $message, $strict) returns the type string which is incompatible with the documented return type boolean.
Loading history...
155
                }
156
                continue;
157
            }
158
        }
159
        return true;
160
    }
161
162
    /**
163
     * Returns pathnames matching a pattern (for files only & hidden files).
164
     *
165
     * @param  string $directory directory
166
     * @param  mixed  $pattern   pattern (does not support tilde expansion)
167
     * @return mixed  matched files or false for non-existent directory
168
     */
169
    final public static function glob($directory, $pattern = null)
170
    {
171
        if (is_dir($directory) === false)
172
        {
173
            return false;
174
        }
175
176
        $pattern = is_array(($pattern)) ? '{'.implode(',', $pattern).'}' : $pattern;
177
        if ($pattern === null)
178
        {
179
            return array_diff(glob($directory.'/*'), ['.', '..']);
180
        }
181
        return glob($directory.'/{,.}*'.$pattern.'*', GLOB_BRACE);
182
    }
183
184
    /**
185
     * Tells whether the filename is a regular file.
186
     *
187
     * @param string $filename path to the file
188
     * @param boolean
189
     */
190
    final public static function lsdir($directory)
191
    {
192
        if (is_dir($directory) === false)
193
        {
194
            return false;
195
        }
196
197
        $directories = [];
198
        $directory = rtrim($directory, '/');
199
        foreach (self::readdir($directory) as $directory)
200
        {
201
            if (is_dir($directory))
202
            {
203
                $directories[] = $directory;
204
            }
205
        }
206
        return $directories;
207
    }
208
209
    /**
210
     * Tells whether the filename is a regular file.
211
     *
212
     * @param string $filename path to the file
213
     * @param boolean
214
     */
215
    final public static function lsfile($directory)
216
    {
217
        if (is_dir($directory) === false)
218
        {
219
            return false;
220
        }
221
222
        $files = [];
223
        $directory = rtrim($directory, '/');
224
        foreach (self::readdir($directory) as $file)
225
        {
226
            if (is_file($file))
227
            {
228
                $files[] = $file;
229
            }
230
        }
231
        return $files;
232
    }
233
234
    /**
235
     * Create new directory.
236
     *
237
     * @param  mixed  $directories path to the directory
238
     * @param  int    $mode        directory permission mode value (octal)
239
     * @param  bool   $recursive   create directories as needed
240
     * @param  bool   $strict      strict error checking
241
     * @return mixed
242
     */
243
    final public static function mkdir($directories, $mode = null, $recursive = false, $strict = true)
244
    {
245
        $directories = !is_array($directories) ? [$directories] : $directories;
246
        $mode = ($mode === null) ? (int) $mode : 0777;
247
        foreach ($directories as $directory)
248
        {
249
            if (@mkdir($directory, (int) $mode, $recursive) === false)
250
            {
251
                $code = self::DIRECTORYCREATE;
252
                $message = "Failed to create new directory {$directory}";
253
                return self::get_error_message($code, $message, $strict);
254
            }
255
        }
256
        return true;
257
    }
258
259
    /**
260
     * Reads a directory and returns all files/directories.
261
     *
262
     * @param string $directory directory path
263
     * @param mixed
264
     */
265
    final private static function readdir($directory)
266
    {
267
        $files = [];
268
        if ($handle = @opendir($directory))
269
        {
270
            while (($file = @readdir($handle)) !== false)
271
            {
272
                if ($file != '.' && $file != '..')
273
                {
274
                    $files[] = $directory.'/'.$file;
275
                }
276
            }
277
        }
278
        return $files;
279
    }
280
281
    /**
282
     * Removes directory.
283
     *
284
     * NOTES:
285
     *  -	directories that are not empty are emptied then deleted
286
     *	-	accepts one or more directories (recursive)
287
     *
288
     * @param  mixed   $directories directory or directories to remove
289
     * @param  bool    $strict strict error checking
290
     * @return mixed
291
     */
292
    final public static function rmdir($directories, $strict = true)
293
    {
294
        $directories = !is_array($directories) ? [$directories] : $directories;
295
        foreach ($directories as $directory)
296
        {
297
            if (is_dir($directory))
298
            {
299
                start:
300
                $files = @self::lsfile($directory);
301
                if (count($files) == 0)
0 ignored issues
show
Bug introduced by
It seems like $files can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

301
                if (count(/** @scrutinizer ignore-type */ $files) == 0)
Loading history...
302
                {
303
                    if (@rmdir($directory) === false)
304
                    {
305
                        $code = self::DIRECTORYDELETE;
306
                        $message = "Failed to delete directory {$directory}";
307
                        return self::get_error_message($code, $message, $strict);
308
                    }
309
                    continue;
310
                }
311
312
                if (count($files) != 0)
313
                {
314
                    self::delete($files);
315
                    goto start;
316
                }
317
            }
318
        }
319
        return true;
320
    }
321
322
    /**
323
     * Moves file to different path (rename).
324
     *
325
     * @param  string $oldfile path to the old file
326
     * @param  string $newfile path to the new file
327
     * @return mixed
328
     */
329
    final public static function rename($oldfile, $newfile)
330
    {
331
        if (is_file($oldfile) === false)
332
        {
333
            $code = self::FILEEXISTS;
334
            $message = "Original (source) file does not exist {$srcfile}";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $srcfile seems to be never defined.
Loading history...
335
            return self::get_error_message($code, $message, true);
336
        }
337
338
        if (!@rename($oldfile, $newfile))
339
        {
340
            $code = self::FILEMOVE;
341
            $message = "Failed to move (rename) file from {$oldfile} to {$newfile}";
342
            return self::get_error_message($code, $message, true);
343
344
        }
345
        return true;
346
    }
347
348
    /**
349
     * Create temporary file.
350
     *
351
     * @param  string $template temporary filename template (default=tmp-XXXXXXXXXXXXXX)
352
     * @param  bool   $resource request file pointer into temporary file
353
     * @return mixed
354
     */
355
    final public static function tmpfile($template = null, $resource = false)
356
    {
357
        $template = ($template === null) ? " tmp-XXXXXXXXXXXXXX" : " {$template}-XXXXXXXXXXXXXX";
358
        $tempfile = shell_exec("mktemp -p /tmp {$template}");
359
        return ($resource == false) ? rtrim($tempfile) : fopen($tempfile, "w+");
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
360
    }
361
362
    /**
363
     * Create temporary directory.
364
     *
365
     * @param  string $template temporary directory template (default=tmpXXXXXXXXXXXXXX)
366
     * @return mixed
367
     */
368
    final public static function tmpdir($template = null)
369
    {
370
        $template = ($template === null) ? " tmpXXXXXXXXXXXXXX" : " {$template}XXXXXXXXXXXXXX";
371
        $tempdir = shell_exec("mktemp -p /tmp -d {$template}");
372
        return rtrim($tempdir);
373
    }
374
375
    /**
376
     * Returns error message or throws exception.
377
     *
378
     * @return string error message
379
     */
380
    final private static function get_error_message($code, $message, $strict)
381
    {
382
        if ($strict == false) { return $message; }
383
384
        switch ($code)
385
        {
386
            case '10':  throw new FileModeException($message);
387
            case '11':  throw new FileCopyException($message);
388
            case '12':  throw new FileDeleteException($message);
389
            case '13':  throw new DirectoryCreateException($message);
390
            case '14':  throw new DirectoryDeleteException($message);
391
            case '15':  throw new FileMoveException($message);
392
            case '16':  throw new FileExistException($message);
393
        }
394
    }
395
}
396