]>
Commit | Line | Data |
---|---|---|
2e28269b | 1 | <?php |
b2306b0c | 2 | |
c2cd15da | 3 | namespace Shaarli\Helper; |
f3d2f257 | 4 | |
a0c4dbd9 | 5 | use Shaarli\Exceptions\IOException; |
b2306b0c | 6 | |
2e28269b | 7 | /** |
b2306b0c A |
8 | * Class FileUtils |
9 | * | |
10 | * Utility class for file manipulation. | |
2e28269b | 11 | */ |
b2306b0c | 12 | class FileUtils |
2e28269b | 13 | { |
b2306b0c A |
14 | /** |
15 | * @var string | |
16 | */ | |
17 | protected static $phpPrefix = '<?php /* '; | |
18 | ||
19 | /** | |
20 | * @var string | |
21 | */ | |
22 | protected static $phpSuffix = ' */ ?>'; | |
2e28269b V |
23 | |
24 | /** | |
b2306b0c A |
25 | * Write data into a file (Shaarli database format). |
26 | * The data is stored in a PHP file, as a comment, in compressed base64 format. | |
27 | * | |
28 | * The file will be created if it doesn't exist. | |
29 | * | |
30 | * @param string $file File path. | |
813849e5 | 31 | * @param mixed $content Content to write. |
b2306b0c A |
32 | * |
33 | * @return int|bool Number of bytes written or false if it fails. | |
2e28269b | 34 | * |
b2306b0c | 35 | * @throws IOException The destination file can't be written. |
2e28269b | 36 | */ |
b2306b0c | 37 | public static function writeFlatDB($file, $content) |
2e28269b | 38 | { |
b2306b0c A |
39 | if (is_file($file) && !is_writeable($file)) { |
40 | // The datastore exists but is not writeable | |
41 | throw new IOException($file); | |
d2d4f993 | 42 | } elseif (!is_file($file) && !is_writeable(dirname($file))) { |
b2306b0c A |
43 | // The datastore does not exist and its parent directory is not writeable |
44 | throw new IOException(dirname($file)); | |
45 | } | |
46 | ||
47 | return file_put_contents( | |
48 | $file, | |
a0c4dbd9 | 49 | self::$phpPrefix . base64_encode(gzdeflate(serialize($content))) . self::$phpSuffix |
b2306b0c A |
50 | ); |
51 | } | |
52 | ||
53 | /** | |
54 | * Read data from a file containing Shaarli database format content. | |
8c322aab V |
55 | * |
56 | * If the file isn't readable or doesn't exist, default data will be returned. | |
b2306b0c A |
57 | * |
58 | * @param string $file File path. | |
59 | * @param mixed $default The default value to return if the file isn't readable. | |
60 | * | |
61 | * @return mixed The content unserialized, or default if the file isn't readable, or false if it fails. | |
62 | */ | |
63 | public static function readFlatDB($file, $default = null) | |
64 | { | |
65 | // Note that gzinflate is faster than gzuncompress. | |
66 | // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 | |
a0c4dbd9 | 67 | if (!is_readable($file)) { |
8c322aab V |
68 | return $default; |
69 | } | |
70 | ||
71 | $data = file_get_contents($file); | |
72 | if ($data == '') { | |
73 | return $default; | |
b2306b0c A |
74 | } |
75 | ||
8c322aab V |
76 | return unserialize( |
77 | gzinflate( | |
78 | base64_decode( | |
79 | substr($data, strlen(self::$phpPrefix), -strlen(self::$phpSuffix)) | |
80 | ) | |
81 | ) | |
82 | ); | |
2e28269b | 83 | } |
0cf76ccb A |
84 | |
85 | /** | |
86 | * Recursively deletes a folder content, and deletes itself optionally. | |
87 | * If an excluded file is found, folders won't be deleted. | |
88 | * | |
89 | * Additional security: raise an exception if it tries to delete a folder outside of Shaarli directory. | |
90 | * | |
91 | * @param string $path | |
92 | * @param bool $selfDelete Delete the provided folder if true, only its content if false. | |
93 | * @param array $exclude | |
94 | */ | |
95 | public static function clearFolder(string $path, bool $selfDelete, array $exclude = []): bool | |
96 | { | |
97 | $skipped = false; | |
98 | ||
99 | if (!is_dir($path)) { | |
100 | throw new IOException(t('Provided path is not a directory.')); | |
101 | } | |
102 | ||
103 | if (!static::isPathInShaarliFolder($path)) { | |
104 | throw new IOException(t('Trying to delete a folder outside of Shaarli path.')); | |
105 | } | |
106 | ||
107 | foreach (new \DirectoryIterator($path) as $file) { | |
108 | if($file->isDot()) { | |
109 | continue; | |
110 | } | |
111 | ||
112 | if (in_array($file->getBasename(), $exclude, true)) { | |
113 | $skipped = true; | |
114 | continue; | |
115 | } | |
116 | ||
117 | if ($file->isFile()) { | |
118 | unlink($file->getPathname()); | |
119 | } elseif($file->isDir()) { | |
120 | $skipped = static::clearFolder($file->getRealPath(), true, $exclude) || $skipped; | |
121 | } | |
122 | } | |
123 | ||
124 | if ($selfDelete && !$skipped) { | |
125 | rmdir($path); | |
126 | } | |
127 | ||
128 | return $skipped; | |
129 | } | |
130 | ||
131 | /** | |
132 | * Checks that the given path is inside Shaarli directory. | |
133 | */ | |
134 | public static function isPathInShaarliFolder(string $path): bool | |
135 | { | |
54afb1d6 | 136 | $rootDirectory = dirname(dirname(dirname(__FILE__))); |
0cf76ccb A |
137 | |
138 | return strpos(realpath($path), $rootDirectory) !== false; | |
139 | } | |
2e28269b | 140 | } |