FileSystemHelper   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 92.68%

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 1
dl 0
loc 124
ccs 38
cts 41
cp 0.9268
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A createFolder() 0 13 2
A createFileWithContents() 0 13 2
A deleteFile() 0 8 3
A deleteFolderRecursively() 0 19 3
A throwIfOperationNotInBaseFolder() 0 11 3
1
<?php
2
3
namespace Box\Spout\Common\Helper;
4
5
use Box\Spout\Common\Exception\IOException;
6
7
/**
8
 * Class FileSystemHelper
9
 * This class provides helper functions to help with the file system operations
10
 * like files/folders creation & deletion
11
 */
12
class FileSystemHelper implements FileSystemHelperInterface
13
{
14
    /** @var string Real path of the base folder where all the I/O can occur */
15
    protected $baseFolderRealPath;
16
17
    /**
18
     * @param string $baseFolderPath The path of the base folder where all the I/O can occur
19
     */
20 95
    public function __construct(string $baseFolderPath)
21
    {
22 95
        $this->baseFolderRealPath = \realpath($baseFolderPath);
23 95
    }
24
25
    /**
26
     * Creates an empty folder with the given name under the given parent folder.
27
     *
28
     * @param string $parentFolderPath The parent folder path under which the folder is going to be created
29
     * @param string $folderName The name of the folder to create
30
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or if the folder path is not inside of the base folder
31
     * @return string Path of the created folder
32
     */
33 92
    public function createFolder($parentFolderPath, $folderName)
34
    {
35 92
        $this->throwIfOperationNotInBaseFolder($parentFolderPath);
36
37 91
        $folderPath = $parentFolderPath . '/' . $folderName;
38
39 91
        $wasCreationSuccessful = \mkdir($folderPath, 0777, true);
40 91
        if (!$wasCreationSuccessful) {
41
            throw new IOException("Unable to create folder: $folderPath");
42
        }
43
44 91
        return $folderPath;
45
    }
46
47
    /**
48
     * Creates a file with the given name and content in the given folder.
49
     * The parent folder must exist.
50
     *
51
     * @param string $parentFolderPath The parent folder path where the file is going to be created
52
     * @param string $fileName The name of the file to create
53
     * @param string $fileContents The contents of the file to create
54
     * @throws \Box\Spout\Common\Exception\IOException If unable to create the file or if the file path is not inside of the base folder
55
     * @return string Path of the created file
56
     */
57 83
    public function createFileWithContents($parentFolderPath, $fileName, $fileContents)
58
    {
59 83
        $this->throwIfOperationNotInBaseFolder($parentFolderPath);
60
61 82
        $filePath = $parentFolderPath . '/' . $fileName;
62
63 82
        $wasCreationSuccessful = \file_put_contents($filePath, $fileContents);
64 82
        if ($wasCreationSuccessful === false) {
65
            throw new IOException("Unable to create file: $filePath");
66
        }
67
68 82
        return $filePath;
69
    }
70
71
    /**
72
     * Delete the file at the given path
73
     *
74
     * @param string $filePath Path of the file to delete
75
     * @throws \Box\Spout\Common\Exception\IOException If the file path is not inside of the base folder
76
     * @return void
77
     */
78 76
    public function deleteFile($filePath)
79
    {
80 76
        $this->throwIfOperationNotInBaseFolder($filePath);
81
82 75
        if (\file_exists($filePath) && \is_file($filePath)) {
83 75
            \unlink($filePath);
84
        }
85 75
    }
86
87
    /**
88
     * Delete the folder at the given path as well as all its contents
89
     *
90
     * @param string $folderPath Path of the folder to delete
91
     * @throws \Box\Spout\Common\Exception\IOException If the folder path is not inside of the base folder
92
     * @return void
93
     */
94 85
    public function deleteFolderRecursively($folderPath)
95
    {
96 85
        $this->throwIfOperationNotInBaseFolder($folderPath);
97
98 84
        $itemIterator = new \RecursiveIteratorIterator(
99 84
            new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS),
100 84
            \RecursiveIteratorIterator::CHILD_FIRST
101
        );
102
103 84
        foreach ($itemIterator as $item) {
104 77
            if ($item->isDir()) {
105 75
                \rmdir($item->getPathname());
106
            } else {
107 77
                \unlink($item->getPathname());
108
            }
109
        }
110
111 84
        \rmdir($folderPath);
112 84
    }
113
114
    /**
115
     * All I/O operations must occur inside the base folder, for security reasons.
116
     * This function will throw an exception if the folder where the I/O operation
117
     * should occur is not inside the base folder.
118
     *
119
     * @param string $operationFolderPath The path of the folder where the I/O operation should occur
120
     * @throws \Box\Spout\Common\Exception\IOException If the folder where the I/O operation should occur
121
     * is not inside the base folder or the base folder does not exist
122
     * @return void
123
     */
124 95
    protected function throwIfOperationNotInBaseFolder(string $operationFolderPath)
125
    {
126 95
        $operationFolderRealPath = \realpath($operationFolderPath);
127 95
        if (!$this->baseFolderRealPath) {
128
            throw new IOException("The base folder path is invalid: {$this->baseFolderRealPath}");
129
        }
130 95
        $isInBaseFolder = (\strpos($operationFolderRealPath, $this->baseFolderRealPath) === 0);
131 95
        if (!$isInBaseFolder) {
132 4
            throw new IOException("Cannot perform I/O operation outside of the base folder: {$this->baseFolderRealPath}");
133
        }
134 91
    }
135
}
136