Passed
Push — master ( 12c564...fbc502 )
by Petr
08:15
created

TVolumeCopy::xcopy()   B

Complexity

Conditions 9
Paths 12

Size

Total Lines 47
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 20
nc 12
nop 3
dl 0
loc 47
rs 8.0555
c 1
b 0
f 0
ccs 17
cts 17
cp 1
crap 9
1
<?php
2
3
namespace kalanis\kw_storage\Extras;
4
5
6
/**
7
 * Trait TVolumeCopy
8
 * @package kalanis\kw_storage\Extras
9
 * Copy dirs - deep
10
 */
11
trait TVolumeCopy
12
{
13
    /**
14
     * Copy a file, or recursively copy a folder and its contents
15
     * @param string $source Source path
16
     * @param string $dest Destination path
17
     * @param int $permissions New folder creation permissions
18
     * @return      bool     Returns true on success, false on failure
19
     * @version     1.0.1
20
     * @link        http://aidanlister.com/2004/04/recursively-copying-directories-in-php/
21
     * @link        https://stackoverflow.com/questions/2050859/copy-entire-contents-of-a-directory-to-another-using-php
22
     * @author      Aidan Lister <[email protected]>
23
     */
24 3
    protected function xcopy(string $source, string $dest, int $permissions = 0755): bool
25
    {
26
        // check if source exists
27 3
        if (!file_exists($source)) {
28 3
            return false;
29
        }
30
31 3
        $sourceHash = $this->hashDirectory($source);
32
33
        // Check for symlinks
34 3
        if (is_link($source)) {
35
            // @codeCoverageIgnoreStart
36
            $data = readlink($source);
37
            if (false === $data) {
38
                return false;
39
            }
40
            return symlink($data, $dest);
41
        }
42
        // @codeCoverageIgnoreEnd
43
44
        // Simple copy for a file
45 3
        if (is_file($source)) {
46 3
            return copy($source, $dest);
47
        }
48
49
        // Make destination directory
50 1
        if (!is_dir($dest)) {
51 1
            mkdir($dest, $permissions);
52
        }
53
54
        // Loop through the folder
55 1
        $dir = dir($source);
56 1
        while (false !== ($entry = $dir->read())) {
57
            // Skip pointers
58 1
            if (in_array($entry, ['.', '..'])) {
59 1
                continue;
60
            }
61
62
            // Deep copy directories
63 1
            if ($sourceHash != $this->hashDirectory($source . DIRECTORY_SEPARATOR . $entry)) {
64 1
                $this->xcopy($source . DIRECTORY_SEPARATOR . $entry, $dest . DIRECTORY_SEPARATOR . $entry, $permissions);
65
            }
66
        }
67
68
        // Clean up
69 1
        $dir->close();
70 1
        return true;
71
    }
72
73
    /**
74
     * In case of coping a directory inside itself, there is a need to hash check the directory otherwise and infinite loop of coping is generated
75
     * @param string $directory
76
     * @return string|null
77
     */
78 3
    protected function hashDirectory(string $directory): ?string
79
    {
80 3
        if (!is_dir($directory)) {
81 3
            return null;
82
        }
83
84 1
        $files = [];
85 1
        $dir = dir($directory);
86
87 1
        while (false !== ($file = $dir->read())) {
88 1
            if (in_array($file, ['.', '..'])) {
89 1
                continue;
90
            }
91 1
            if (is_dir($directory . DIRECTORY_SEPARATOR . $file)) {
92 1
                $files[] = $this->hashDirectory($directory . DIRECTORY_SEPARATOR . $file);
93
            } else {
94 1
                $files[] = md5_file($directory . DIRECTORY_SEPARATOR . $file);
95
            }
96
        }
97
98 1
        $dir->close();
99
100 1
        return md5(implode('', $files));
101
    }
102
}
103