Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace OpenSpout\Common\Helper;
6
 
7
use OpenSpout\Common\Exception\IOException;
8
use RecursiveDirectoryIterator;
9
use RecursiveIteratorIterator;
10
 
11
/**
12
 * @internal
13
 */
14
final class FileSystemHelper implements FileSystemHelperInterface
15
{
16
    /** @var string Real path of the base folder where all the I/O can occur */
17
    private readonly string $baseFolderRealPath;
18
 
19
    /**
20
     * @param string $baseFolderPath The path of the base folder where all the I/O can occur
21
     */
22
    public function __construct(string $baseFolderPath)
23
    {
24
        $realpath = realpath($baseFolderPath);
25
        \assert(false !== $realpath);
26
        $this->baseFolderRealPath = $realpath;
27
    }
28
 
29
    public function getBaseFolderRealPath(): string
30
    {
31
        return $this->baseFolderRealPath;
32
    }
33
 
34
    /**
35
     * Creates an empty folder with the given name under the given parent folder.
36
     *
37
     * @param string $parentFolderPath The parent folder path under which the folder is going to be created
38
     * @param string $folderName       The name of the folder to create
39
     *
40
     * @return string Path of the created folder
41
     *
42
     * @throws IOException If unable to create the folder or if the folder path is not inside of the base folder
43
     */
44
    public function createFolder(string $parentFolderPath, string $folderName): string
45
    {
46
        $this->throwIfOperationNotInBaseFolder($parentFolderPath);
47
 
48
        $folderPath = $parentFolderPath.\DIRECTORY_SEPARATOR.$folderName;
49
 
50
        $errorMessage = '';
51
        set_error_handler(static function ($nr, $message) use (&$errorMessage): bool {
52
            $errorMessage = $message;
53
 
54
            return true;
55
        });
56
        $wasCreationSuccessful = mkdir($folderPath, 0777, true);
57
        restore_error_handler();
58
 
59
        if (!$wasCreationSuccessful) {
60
            throw new IOException("Unable to create folder: {$folderPath} - {$errorMessage}");
61
        }
62
 
63
        return $folderPath;
64
    }
65
 
66
    /**
67
     * Creates a file with the given name and content in the given folder.
68
     * The parent folder must exist.
69
     *
70
     * @param string $parentFolderPath The parent folder path where the file is going to be created
71
     * @param string $fileName         The name of the file to create
72
     * @param string $fileContents     The contents of the file to create
73
     *
74
     * @return string Path of the created file
75
     *
76
     * @throws IOException If unable to create the file or if the file path is not inside of the base folder
77
     */
78
    public function createFileWithContents(string $parentFolderPath, string $fileName, string $fileContents): string
79
    {
80
        $this->throwIfOperationNotInBaseFolder($parentFolderPath);
81
 
82
        $filePath = $parentFolderPath.\DIRECTORY_SEPARATOR.$fileName;
83
 
84
        $errorMessage = '';
85
        set_error_handler(static function ($nr, $message) use (&$errorMessage): bool {
86
            $errorMessage = $message;
87
 
88
            return true;
89
        });
90
        $wasCreationSuccessful = file_put_contents($filePath, $fileContents);
91
        restore_error_handler();
92
 
93
        if (false === $wasCreationSuccessful) {
94
            throw new IOException("Unable to create file: {$filePath} - {$errorMessage}");
95
        }
96
 
97
        return $filePath;
98
    }
99
 
100
    /**
101
     * Delete the file at the given path.
102
     *
103
     * @param string $filePath Path of the file to delete
104
     *
105
     * @throws IOException If the file path is not inside of the base folder
106
     */
107
    public function deleteFile(string $filePath): void
108
    {
109
        $this->throwIfOperationNotInBaseFolder($filePath);
110
 
111
        if (file_exists($filePath) && is_file($filePath)) {
112
            unlink($filePath);
113
        }
114
    }
115
 
116
    /**
117
     * Delete the folder at the given path as well as all its contents.
118
     *
119
     * @param string $folderPath Path of the folder to delete
120
     *
121
     * @throws IOException If the folder path is not inside of the base folder
122
     */
123
    public function deleteFolderRecursively(string $folderPath): void
124
    {
125
        $this->throwIfOperationNotInBaseFolder($folderPath);
126
 
127
        $itemIterator = new RecursiveIteratorIterator(
128
            new RecursiveDirectoryIterator($folderPath, RecursiveDirectoryIterator::SKIP_DOTS),
129
            RecursiveIteratorIterator::CHILD_FIRST
130
        );
131
 
132
        foreach ($itemIterator as $item) {
133
            if ($item->isDir()) {
134
                rmdir($item->getPathname());
135
            } else {
136
                unlink($item->getPathname());
137
            }
138
        }
139
 
140
        rmdir($folderPath);
141
    }
142
 
143
    /**
144
     * All I/O operations must occur inside the base folder, for security reasons.
145
     * This function will throw an exception if the folder where the I/O operation
146
     * should occur is not inside the base folder.
147
     *
148
     * @param string $operationFolderPath The path of the folder where the I/O operation should occur
149
     *
150
     * @throws IOException If the folder where the I/O operation should occur
151
     *                     is not inside the base folder or the base folder does not exist
152
     */
153
    private function throwIfOperationNotInBaseFolder(string $operationFolderPath): void
154
    {
155
        $operationFolderRealPath = realpath($operationFolderPath);
156
        if (false === $operationFolderRealPath) {
157
            throw new IOException("Folder not found: {$operationFolderRealPath}");
158
        }
159
        $isInBaseFolder = str_starts_with($operationFolderRealPath, $this->baseFolderRealPath);
160
        if (!$isInBaseFolder) {
161
            throw new IOException("Cannot perform I/O operation outside of the base folder: {$this->baseFolderRealPath}");
162
        }
163
    }
164
}