Completed
Push — master ( 654b22...5f2cd6 )
by duan
02:17
created

Filesystem   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 492
Duplicated Lines 0 %

Test Coverage

Coverage 9.84%

Importance

Changes 0
Metric Value
eloc 92
dl 0
loc 492
ccs 12
cts 122
cp 0.0984
rs 3.28
c 0
b 0
f 0
wmc 64

33 Methods

Rating   Name   Duplication   Size   Complexity  
A glob() 0 3 1
A lastModified() 0 3 1
A size() 0 3 1
A move() 0 3 1
A getRequire() 0 7 2
A extension() 0 3 1
A basename() 0 3 1
A put() 0 3 2
A makeDirectory() 0 7 2
A prepend() 0 7 2
A delete() 0 17 5
A type() 0 3 1
A hash() 0 3 1
A copy() 0 3 1
A isReadable() 0 3 1
A sharedGet() 0 21 4
A chmod() 0 7 2
A get() 0 7 3
A name() 0 3 1
A isDirectory() 0 3 1
A mimeType() 0 3 1
A isWritable() 0 3 1
A isFile() 0 3 1
A append() 0 3 1
A exists() 0 3 1
A dirname() 0 3 1
A requireOnce() 0 3 1
A deleteDirectory() 0 21 6
B copyDirectory() 0 31 8
A moveDirectory() 0 7 4
A link() 0 9 3
A windowsOs() 0 3 1
A cleanDirectory() 0 3 1

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
/**
3
 * Created by PhpStorm.
4
 * User: yiranzai
5
 * Date: 19-3-6
6
 * Time: 下午6:32
7
 */
8
9
namespace Yiranzai\Dht;
10
11
use FilesystemIterator;
12
13
class Filesystem
14
{
15
16
    /**
17
     * Determine if a file or directory exists.
18
     *
19
     * @param  string $path
20
     * @return bool
21
     */
22 18
    public function exists($path): bool
23
    {
24 18
        return file_exists($path);
25
    }
26
27
    /**
28
     * Get the contents of a file.
29
     *
30
     * @param  string $path
31
     * @param  bool   $lock
32
     * @return string
33
     */
34 3
    public function get($path, $lock = false): string
35
    {
36 3
        if ($this->isFile($path)) {
37 3
            return $lock ? $this->sharedGet($path) : file_get_contents($path);
38
        }
39
40
        throw new \RuntimeException("File does not exist at path {$path}");
41
    }
42
43
    /**
44
     * Get contents of a file with shared access.
45
     *
46
     * @param  string $path
47
     * @return string
48
     */
49
    public function sharedGet($path): string
50
    {
51
        $contents = '';
52
53
        $handle = fopen($path, 'rb');
54
55
        if ($handle !== false) {
56
            try {
57
                if (flock($handle, LOCK_SH)) {
58
                    clearstatcache(true, $path);
59
60
                    $contents = fread($handle, $this->size($path) ?: 1);
61
62
                    flock($handle, LOCK_UN);
63
                }
64
            } finally {
65
                fclose($handle);
66
            }
67
        }
68
69
        return $contents;
70
    }
71
72
    /**
73
     * Get the returned value of a file.
74
     *
75
     * @param  string $path
76
     * @return mixed
77
     *
78
     */
79
    public function getRequire($path)
80
    {
81
        if ($this->isFile($path)) {
82
            return require $path;
83
        }
84
85
        throw new \RuntimeException("File does not exist at path {$path}");
86
    }
87
88
    /**
89
     * Require the given file once.
90
     *
91
     * @param  string $file
92
     * @return void
93
     */
94
    public function requireOnce($file): void
95
    {
96
        require_once $file;
97
    }
98
99
    /**
100
     * Get the MD5 hash of the file at the given path.
101
     *
102
     * @param  string $path
103
     * @return string
104
     */
105
    public function hash($path): string
106
    {
107
        return md5_file($path);
108
    }
109
110
    /**
111
     * Write the contents of a file.
112
     *
113
     * @param  string $path
114
     * @param  string $contents
115
     * @param  bool   $lock
116
     * @return int
117
     */
118 3
    public function put($path, $contents, $lock = false): int
119
    {
120 3
        return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
121
    }
122
123
    /**
124
     * Prepend to a file.
125
     *
126
     * @param  string $path
127
     * @param  string $data
128
     * @return int
129
     */
130
    public function prepend($path, $data): int
131
    {
132
        if ($this->exists($path)) {
133
            return $this->put($path, $data . $this->get($path));
134
        }
135
136
        return $this->put($path, $data);
137
    }
138
139
    /**
140
     * Append to a file.
141
     *
142
     * @param  string $path
143
     * @param  string $data
144
     * @return int
145
     */
146
    public function append($path, $data): int
147
    {
148
        return file_put_contents($path, $data, FILE_APPEND);
149
    }
150
151
    /**
152
     * Get or set UNIX mode of a file or directory.
153
     *
154
     * @param  string $path
155
     * @param  int    $mode
156
     * @return mixed
157
     */
158
    public function chmod($path, $mode = null)
159
    {
160
        if ($mode !== null) {
161
            return chmod($path, $mode);
162
        }
163
164
        return substr(sprintf('%o', fileperms($path)), -4);
165
    }
166
167
    /**
168
     * Delete the file at a given path.
169
     *
170
     * @param  string|array $paths
171
     * @return bool
172
     */
173
    public function delete($paths): bool
174
    {
175
        $paths = is_array($paths) ? $paths : func_get_args();
176
177
        $success = true;
178
179
        foreach ($paths as $path) {
180
            try {
181
                if (!@unlink($path)) {
182
                    $success = false;
183
                }
184
            } catch (\Exception $e) {
185
                $success = false;
186
            }
187
        }
188
189
        return $success;
190
    }
191
192
    /**
193
     * Move a file to a new location.
194
     *
195
     * @param  string $path
196
     * @param  string $target
197
     * @return bool
198
     */
199
    public function move($path, $target): bool
200
    {
201
        return rename($path, $target);
202
    }
203
204
    /**
205
     * Copy a file to a new location.
206
     *
207
     * @param  string $path
208
     * @param  string $target
209
     * @return bool
210
     */
211
    public function copy($path, $target): bool
212
    {
213
        return copy($path, $target);
214
    }
215
216
    /**
217
     * Create a hard link to the target file or directory.
218
     *
219
     * @param  string $target
220
     * @param  string $link
221
     * @return void
222
     */
223
    public function link($target, $link): void
224
    {
225
        if (!$this->windowsOs()) {
226
            symlink($target, $link);
227
        }
228
229
        $mode = $this->isDirectory($target) ? 'J' : 'H';
230
231
        exec("mklink /{$mode} \"{$link}\" \"{$target}\"");
232
    }
233
234
    /**
235
     * Extract the file name from a file path.
236
     *
237
     * @param  string $path
238
     * @return string
239
     */
240
    public function name($path): string
241
    {
242
        return pathinfo($path, PATHINFO_FILENAME);
243
    }
244
245
    /**
246
     * Extract the trailing name component from a file path.
247
     *
248
     * @param  string $path
249
     * @return string
250
     */
251
    public function basename($path): string
252
    {
253
        return pathinfo($path, PATHINFO_BASENAME);
254
    }
255
256
    /**
257
     * Extract the parent directory from a file path.
258
     *
259
     * @param  string $path
260
     * @return string
261
     */
262
    public function dirname($path): string
263
    {
264
        return pathinfo($path, PATHINFO_DIRNAME);
265
    }
266
267
    /**
268
     * Extract the file extension from a file path.
269
     *
270
     * @param  string $path
271
     * @return string
272
     */
273
    public function extension($path): string
274
    {
275
        return pathinfo($path, PATHINFO_EXTENSION);
276
    }
277
278
    /**
279
     * Get the file type of a given file.
280
     *
281
     * @param  string $path
282
     * @return string
283
     */
284
    public function type($path): string
285
    {
286
        return filetype($path);
287
    }
288
289
    /**
290
     * Get the mime-type of a given file.
291
     *
292
     * @param  string $path
293
     * @return string|false
294
     */
295
    public function mimeType($path)
296
    {
297
        return finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
298
    }
299
300
    /**
301
     * Get the file size of a given file.
302
     *
303
     * @param  string $path
304
     * @return int
305
     */
306
    public function size($path): int
307
    {
308
        return filesize($path);
309
    }
310
311
    /**
312
     * Get the file's last modification time.
313
     *
314
     * @param  string $path
315
     * @return int
316
     */
317
    public function lastModified($path): int
318
    {
319
        return filemtime($path);
320
    }
321
322
    /**
323
     * Determine if the given path is a directory.
324
     *
325
     * @param  string $directory
326
     * @return bool
327
     */
328
    public function isDirectory($directory): bool
329
    {
330
        return is_dir($directory);
331
    }
332
333
    /**
334
     * Determine if the given path is readable.
335
     *
336
     * @param  string $path
337
     * @return bool
338
     */
339
    public function isReadable($path): bool
340
    {
341
        return is_readable($path);
342
    }
343
344
    /**
345
     * Determine if the given path is writable.
346
     *
347
     * @param  string $path
348
     * @return bool
349
     */
350
    public function isWritable($path): bool
351
    {
352
        return is_writable($path);
353
    }
354
355
    /**
356
     * Determine if the given path is a file.
357
     *
358
     * @param  string $file
359
     * @return bool
360
     */
361 3
    public function isFile($file): bool
362
    {
363 3
        return is_file($file);
364
    }
365
366
    /**
367
     * Find path names matching a given pattern.
368
     *
369
     * @param  string $pattern
370
     * @param  int    $flags
371
     * @return array
372
     */
373
    public function glob($pattern, $flags = 0): array
374
    {
375
        return glob($pattern, $flags);
376
    }
377
378
    /**
379
     * Create a directory.
380
     *
381
     * @param  string $path
382
     * @param  int    $mode
383
     * @param  bool   $recursive
384
     * @param  bool   $force
385
     * @return bool
386
     */
387 3
    public function makeDirectory($path, $mode = 0755, $recursive = false, $force = false): bool
388
    {
389 3
        if ($force) {
390
            return @mkdir($path, $mode, $recursive);
391
        }
392
393 3
        return mkdir($path, $mode, $recursive);
394
    }
395
396
    /**
397
     * Move a directory.
398
     *
399
     * @param  string $from
400
     * @param  string $to
401
     * @param  bool   $overwrite
402
     * @return bool
403
     */
404
    public function moveDirectory($from, $to, $overwrite = false): bool
405
    {
406
        if ($overwrite && $this->isDirectory($to) && !$this->deleteDirectory($to)) {
407
            return false;
408
        }
409
410
        return @rename($from, $to) === true;
411
    }
412
413
    /**
414
     * Copy a directory from one location to another.
415
     *
416
     * @param  string $directory
417
     * @param  string $destination
418
     * @param  int    $options
419
     * @return bool
420
     */
421
    public function copyDirectory($directory, $destination, $options = null): bool
422
    {
423
        if (!$this->isDirectory($directory)) {
424
            return false;
425
        }
426
427
        $options = $options ?: FilesystemIterator::SKIP_DOTS;
428
429
        if (!$this->isDirectory($destination)) {
430
            $this->makeDirectory($destination, 0777, true);
431
        }
432
433
        $items = new FilesystemIterator($directory, $options);
434
435
        foreach ($items as $item) {
436
            $target = $destination . '/' . $item->getBasename();
437
438
            if (!$item->isDir()) {
439
                if (!$this->copy($item->getPathname(), $target)) {
440
                    return false;
441
                }
442
            } else {
443
                $path = $item->getPathname();
444
445
                if (!$this->copyDirectory($path, $target, $options)) {
446
                    return false;
447
                }
448
            }
449
        }
450
451
        return true;
452
    }
453
454
    /**
455
     * Recursively delete a directory.
456
     *
457
     * The directory itself may be optionally preserved.
458
     *
459
     * @param  string $directory
460
     * @param  bool   $preserve
461
     * @return bool
462
     */
463
    public function deleteDirectory($directory, $preserve = false): bool
464
    {
465
        if (!$this->isDirectory($directory)) {
466
            return false;
467
        }
468
469
        $items = new FilesystemIterator($directory);
470
471
        foreach ($items as $item) {
472
            if ($item->isDir() && !$item->isLink()) {
473
                $this->deleteDirectory($item->getPathname());
474
            } else {
475
                $this->delete($item->getPathname());
476
            }
477
        }
478
479
        if (!$preserve) {
480
            @rmdir($directory);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rmdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

480
            /** @scrutinizer ignore-unhandled */ @rmdir($directory);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
481
        }
482
483
        return true;
484
    }
485
486
    /**
487
     * Empty the specified directory of all files and folders.
488
     *
489
     * @param  string $directory
490
     * @return bool
491
     */
492
    public function cleanDirectory($directory): bool
493
    {
494
        return $this->deleteDirectory($directory, true);
495
    }
496
497
    /**
498
     * Determine whether the current environment is Windows based.
499
     *
500
     * @return bool
501
     */
502
    public function windowsOs(): bool
503
    {
504
        return stripos(PHP_OS, 'win') === 0;
505
    }
506
}
507