Passed
Push — main ( a7294a...cb4e4e )
by Sammy
08:21 queued 01:31
created

FileSystem.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace HexMakina\LocalFS;
4
5
/**
6
 * FILEPATH: /var/www/dev.engine/krafto-cinergie/lib/LocalFS/FileSystem.php
7
 * 
8
 * The FileSystem class provides a set of methods to interact with a directory and subdirectories.
9
 * The root is the starting point of the represented file system.
10
 * It allows to work with relative paths and provides methods to get absolute paths, list directories and files,
11
 * and ensure that a path is writable.
12
 * 
13
 * @package HexMakina\LocalFS
14
 */
15
16
class FileSystem
17
{
18
19
    private $rootPath = null;
20
21
    /**
22
     * Constructs a new FileSystem object with the given root path.
23
     *
24
     * @param string $rootPath The starting point of the represented file system.
25
     * @throws \InvalidArgumentException If the root path is invalid.
26
     */
27
    function __construct(string $rootPath)
28
    {
29
        $rootPath = realpath($rootPath);
30
        if (!$rootPath)
31
            throw new \InvalidArgumentException('INVALID_ROOT_PATH');
32
33
        $this->rootPath = $rootPath;
34
    }
35
36
    /**
37
     * Returns the starting point of a filesystem's abstraction, not the machine's filesystem root. 
38
     * It can be a project's root, or any directory you may want to work with.
39
     *
40
     * @return string The root path of the file system.
41
     */
42
    public function root(): string
43
    {
44
        return $this->rootPath;
45
    }
46
47
    /**
48
     * Returns the absolute path for a given relative path.
49
     *
50
     * @param string $relativePath The relative path to get the absolute path for.
51
     * @param bool $checkExistence Whether to check if the file exists or not. Default is false.
52
     * @return string The absolute path for the given relative path.
53
     * @throws \InvalidArgumentException If the relative path is empty or invalid.
54
     */
55
    public function absolutePathFor(string $relativePath, bool $checkExistence = false): string
56
    {
57
        $absolute = sprintf('%s/%s', $this->root(), $relativePath);
58
59
        if ($checkExistence) {
60
            $absolute = realpath($absolute);
61
            if (!$absolute) {
62
                throw new \InvalidArgumentException('INVALID_PATH');
63
            }
64
        }
65
66
        return $absolute;
67
    }
68
69
    /**
70
     * Lists the contents of a directory.
71
     *
72
     * @param string $relativePath The relative path of the directory to list.
73
     * @return array An array of filenames in the directory.
74
     * @throws \InvalidArgumentException If the specified path is not a directory.
75
     */
76
    public function list(string $relativePath): array
77
    {
78
        $absolutePath = $this->absolutePathFor($relativePath);
79
80
        if (!is_dir($absolutePath)) {
81
            throw new \InvalidArgumentException('RELATIVE_PATH_NOT_A_DIRECTORY');
82
        }
83
84
        return array_diff(scandir($absolutePath), ['.', '..']);
85
    }
86
87
    /**
88
     * Returns an array of files in the specified directory.
89
     *
90
     * @param string $relativePath The relative path of the directory to search.
91
     * @return array An array of file names.
92
     */
93
    public function files(string $relativePath): array
94
    {
95
        $absolutePath = $this->absolutePathFor($relativePath);
96
        // Filter the list of files to include only files (not directories).
97
        $files = array_filter($this->list($relativePath), function ($filename) use ($absolutePath) {
98
            return is_file($absolutePath . DIRECTORY_SEPARATOR . $filename);
99
        });
100
101
        return $files;
102
    }
103
104
    /**
105
     * Returns an array of directories in the specified relative path.
106
     *
107
     * @param string $relativePath The relative path to search for directories.
108
     * @return array An array of directories in the specified relative path.
109
     */
110
    public function directories(string $relativePath): array
111
    {
112
        $absolutePath = $this->absolutePathFor($relativePath);
113
114
        // Filter the list of files to include only files (not directories).
115
        $files = array_filter($this->list($relativePath), function ($filename) use ($absolutePath) {
116
            return is_dir($absolutePath . DIRECTORY_SEPARATOR . $filename);
117
        });
118
119
        return $files;
120
    }
121
122
    /**
123
     * Ensures that the specified relative path is writable.
124
     *
125
     * @param string $relativePath The relative path to ensure is writable.
126
     * @throws \InvalidArgumentException If the path is not inside the root path, the target directory cannot be created, or the target directory is not writable.
127
     * @return bool True if the path is writable, false otherwise.
128
     */
129
130
    public function ensureWritablePath(string $absoluteDirectoryPath, string $filename=null): bool
131
    {
132
        if (strpos($absoluteDirectoryPath, $this->root()) !== 0) {
133
            throw new \InvalidArgumentException('PATH_NOT_INSIDE_ROOT_PATH');
134
        }
135
136
        $relativeDirectoryPath = substr($absoluteDirectoryPath, strlen($this->root()) + 1);
137
        $pathParts = explode('/', $relativeDirectoryPath);
138
        
139
140
        $targetDir = $this->root();
141
        foreach ($pathParts as $i => $part) {
142
            $targetDir .= '/' . $part;
143
144
            // Create the folder if it doesn't exist
145
            if (!file_exists($targetDir) && mkdir($targetDir, 0755, true) === false) {
146
                throw new \InvalidArgumentException('UNABLE_TO_CREATE_MISSING_TARGET_DIRECTORY');
147
            }
148
149
            if (!is_dir($targetDir)) {
150
                throw new \InvalidArgumentException('TARGET_DIRECTORY_NOT_A_DIRECTORY');
151
            }
152
            if (!is_writable($targetDir)) {
153
                throw new \InvalidArgumentException('TARGET_DIRECTORY_NOT_WRITABLE');
154
            }
155
        }
156
        return true;
157
    }
158
159
    public function filenames($regex = null): array
160
    {
161
        if (!file_exists($this->rootPath) && mkdir($this->rootPath) === false) {
162
            return [];
163
        }
164
165
        $filenames = self::preg_scandir($this->rootPath, $regex); // ID_SEQUENCENUMBER.ext
0 ignored issues
show
The method preg_scandir() does not exist on HexMakina\LocalFS\FileSystem. Did you maybe mean pregScandir()? ( Ignorable by Annotation )

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

165
        /** @scrutinizer ignore-call */ 
166
        $filenames = self::preg_scandir($this->rootPath, $regex); // ID_SEQUENCENUMBER.ext

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
166
        if (!is_null($filenames)) {
167
            sort($filenames);
168
        }
169
170
        return $filenames;
171
    }
172
173
    // previous implementation, to update
174
    public function filepathes($regex = null): array
175
    {
176
        $filenames = $this->filenames($regex);
177
        $filepathes = [];
178
        foreach ($filenames as $filename) {
179
            $filepathes[] = $this->absolutePathFor($filename);
180
        }
181
182
        return $filepathes;
183
    }
184
185
    /**
186
     * Resolves a symbolic link to its target path.
187
     *
188
     * @param string $path The path to resolve.
189
     * @throws \Exception If `readlink` fails to resolve the symbolic link.
190
     * @return string The resolved path.
191
     */
192
    public static function resolve_symlink($path)
193
    {
194
        if (is_link($path)) {
195
            if (($path = readlink($path)) === false) {
196
                throw new \Exception('Failed to resolve symbolic link');
197
            }
198
        }
199
        return $path;
200
    }
201
    /**
202
     * Copies a file from the source path to the destination path.
203
     *
204
     * @param string $sourcePath The path of the source file.
205
     * @param string $destinationPath The path of the destination file.
206
     * @return bool Returns TRUE on success or FALSE on failure.
207
     */
208
    public static function copy($sourcePath, $destinationPath)
209
    {
210
        if (file_exists($sourcePath) && is_file($sourcePath)) {
211
            $destination = new FilePath($destinationPath);
212
            if (file_exists($destination->dir()) && is_dir($destination->dir())) {
213
                return copy($sourcePath, $destinationPath);
214
            }
215
        }
216
        return false;
217
    }
218
219
    /**
220
     * Moves a file from the source path to the destination path.
221
     *
222
     * @param string $sourcePath The path of the source file.
223
     * @param string $destinationPath The path of the destination file.
224
     * @return bool Returns TRUE on success or FALSE on failure.
225
     */
226
    public static function move($sourcePath, $destinationPath)
227
    {
228
        if (file_exists($sourcePath) && is_file($sourcePath)) {
229
            $destination = new FilePath($destinationPath);
230
            if (file_exists($destination->dir()) && is_dir($destination->dir())) {
231
                return rename($sourcePath, $destinationPath);
232
            }
233
        }
234
235
        return false;
236
    }
237
238
    /**
239
     * Creates a directory with the specified path and permissions.
240
     *
241
     * @param string $directoryPath The path of the directory to create.
242
     * @param int $permission The permissions to set for the directory.
243
     * @param bool $recursive Whether to create parent directories if they don't exist.
244
     * @return bool Returns TRUE on success or FALSE on failure.
245
     */
246
    public static function makeDirectory($directoryPath, $permission = 0777, $recursive = true): bool
247
    {
248
        return mkdir($directoryPath, $permission, $recursive);
249
    }
250
251
    /**
252
     * Removes a file or directory with the specified path.
253
     *
254
     * @param string $sourcePath The path of the file or directory to remove.
255
     * @param bool $followLink Whether to follow symbolic links.
256
     * @return bool Returns TRUE on success or FALSE on failure.
257
     */
258
    public static function remove($sourcePath, $followLink = false): bool
259
    {
260
        $success = false;
261
        if (file_exists($sourcePath)) {
262
            if (is_link($sourcePath) && $followLink === true) {
263
                $success = self::remove(readlink($sourcePath));
264
            } elseif (is_file($sourcePath)) {
265
                $success = unlink($sourcePath);
266
            } elseif (is_dir($sourcePath)) {
267
                $success = rmdir($sourcePath);
268
            }
269
        }
270
        return $success;
271
    }
272
273
    /**
274
     * Scans a directory for files and directories that match a regular expression.
275
     *
276
     * @param string $directoryPath The path of the directory to scan.
277
     * @param string|null $regex The regular expression to match against file and directory names.
278
     * @return array|null Returns an array of file and directory names that match the regular expression, or NULL if the directory doesn't exist.
279
     * @throws \Exception If the directory cannot be scanned.
280
     */
281
    public static function pregScandir($directoryPath, $regex = null)
282
    {
283
        if (!file_exists($directoryPath) || !is_dir($directoryPath)) {
284
            return null;
285
        }
286
287
        if (($fileNames = scandir($directoryPath, SCANDIR_SORT_ASCENDING)) !== false) {
288
            return is_null($regex) ? $fileNames : preg_grep($regex, $fileNames);
289
        }
290
291
        throw new \Exception("directory path '$directoryPath' cannot be scanned");
292
    }
293
}
294