blitz-php /
framework
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * This file is part of Blitz PHP framework. |
||
| 5 | * |
||
| 6 | * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]> |
||
| 7 | * |
||
| 8 | * For the full copyright and license information, please view |
||
| 9 | * the LICENSE file that was distributed with this source code. |
||
| 10 | */ |
||
| 11 | |||
| 12 | if (! function_exists('directory_map')) { |
||
| 13 | /** |
||
| 14 | * Créer une carte de répertoire |
||
| 15 | * |
||
| 16 | * Lit le répertoire spécifié et construit un tableau |
||
| 17 | * représentation de celui-ci. Les sous-dossiers contenus dans le répertoire seront également mappés. |
||
| 18 | * |
||
| 19 | * @param string $sourceDir Chemin d'accès à la source |
||
| 20 | * @param int $directoryDepth Profondeur des répertoires à parcourir |
||
| 21 | * (0 = entièrement récursif, 1 = répertoire actuel, etc.) |
||
| 22 | * @param bool $hidden Afficher ou non les fichiers cachés |
||
| 23 | */ |
||
| 24 | function directory_map(string $sourceDir, int $directoryDepth = 0, bool $hidden = false): array |
||
| 25 | { |
||
| 26 | try { |
||
| 27 | 6 | $fp = opendir($sourceDir); |
|
| 28 | |||
| 29 | 6 | $fileData = []; |
|
| 30 | 6 | $newDepth = $directoryDepth - 1; |
|
| 31 | 6 | $sourceDir = rtrim($sourceDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
|
| 32 | |||
| 33 | while (false !== ($file = readdir($fp))) { |
||
| 34 | // Remove '.', '..', and hidden files [optional] |
||
| 35 | if ($file === '.' || $file === '..' || ($hidden === false && $file[0] === '.')) { |
||
| 36 | 6 | continue; |
|
| 37 | } |
||
| 38 | |||
| 39 | if (is_dir($sourceDir . $file)) { |
||
| 40 | 6 | $file .= DIRECTORY_SEPARATOR; |
|
| 41 | } |
||
| 42 | |||
| 43 | if (($directoryDepth < 1 || $newDepth > 0) && is_dir($sourceDir . $file)) { |
||
| 44 | 6 | $fileData[$file] = directory_map($sourceDir . $file, $newDepth, $hidden); |
|
| 45 | } else { |
||
| 46 | 6 | $fileData[] = $file; |
|
| 47 | } |
||
| 48 | } |
||
| 49 | |||
| 50 | 6 | closedir($fp); |
|
| 51 | |||
| 52 | 6 | return $fileData; |
|
| 53 | } catch (Throwable) { |
||
| 54 | return []; |
||
| 55 | } |
||
| 56 | } |
||
| 57 | } |
||
| 58 | |||
| 59 | if (! function_exists('directory_mirror')) { |
||
| 60 | /** |
||
| 61 | * Copie récursivement les fichiers et répertoires du répertoire d'origine |
||
| 62 | * dans le répertoire cible, c'est-à-dire "miroir" son contenu. |
||
| 63 | * |
||
| 64 | * @param bool $overwrite Si les fichiers individuels sont écrasés en cas de collision |
||
| 65 | */ |
||
| 66 | function directory_mirror(string $originDir, string $targetDir, bool $overwrite = true): bool |
||
| 67 | { |
||
| 68 | return service('fs')->copyDirectory($originDir, $targetDir, $overwrite); |
||
| 69 | } |
||
| 70 | } |
||
| 71 | |||
| 72 | if (! function_exists('write_file')) { |
||
| 73 | /** |
||
| 74 | * Write File |
||
| 75 | * |
||
| 76 | * Writes data to the file specified in the path. |
||
| 77 | * Creates a new file if non-existent. |
||
| 78 | * |
||
| 79 | * @param string $path File path |
||
| 80 | * @param string $data Data to write |
||
| 81 | * @param string $mode fopen() mode (default: 'wb') |
||
| 82 | */ |
||
| 83 | function write_file(string $path, string $data, string $mode = 'wb'): bool |
||
| 84 | { |
||
| 85 | try { |
||
| 86 | 8 | $fp = fopen($path, $mode); |
|
| 87 | |||
| 88 | 8 | flock($fp, LOCK_EX); |
|
| 89 | |||
| 90 | 8 | for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result) { |
|
| 91 | if (($result = fwrite($fp, substr($data, $written))) === false) { |
||
| 92 | break; |
||
| 93 | } |
||
| 94 | } |
||
| 95 | |||
| 96 | 8 | flock($fp, LOCK_UN); |
|
| 97 | 8 | fclose($fp); |
|
| 98 | |||
| 99 | 8 | return is_int($result); |
|
| 100 | } catch (Throwable) { |
||
| 101 | return false; |
||
| 102 | } |
||
| 103 | } |
||
| 104 | } |
||
| 105 | |||
| 106 | if (! function_exists('delete_files')) { |
||
| 107 | /** |
||
| 108 | * Delete Files |
||
| 109 | * |
||
| 110 | * Deletes all files contained in the supplied directory path. |
||
| 111 | * Files must be writable or owned by the system in order to be deleted. |
||
| 112 | * If the second parameter is set to true, any directories contained |
||
| 113 | * within the supplied base directory will be nuked as well. |
||
| 114 | * |
||
| 115 | * @param string $path File path |
||
| 116 | * @param bool $delDir Whether to delete any directories found in the path |
||
| 117 | * @param bool $htdocs Whether to skip deleting .htaccess and index page files |
||
| 118 | * @param bool $hidden Whether to include hidden files (files beginning with a period) |
||
| 119 | */ |
||
| 120 | function delete_files(string $path, bool $delDir = false, bool $htdocs = false, bool $hidden = false): bool |
||
| 121 | { |
||
| 122 | 10 | $path = realpath($path) ?: $path; |
|
| 123 | 10 | $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
|
| 124 | |||
| 125 | try { |
||
| 126 | foreach (new RecursiveIteratorIterator( |
||
| 127 | new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), |
||
| 128 | RecursiveIteratorIterator::CHILD_FIRST |
||
| 129 | ) as $object) { |
||
| 130 | 10 | $filename = $object->getFilename(); |
|
| 131 | if (! $hidden && $filename[0] === '.') { |
||
| 132 | continue; |
||
| 133 | } |
||
| 134 | |||
| 135 | if (! $htdocs || ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) { |
||
| 136 | 10 | $isDir = $object->isDir(); |
|
| 137 | if ($isDir && $delDir) { |
||
| 138 | 10 | rmdir($object->getPathname()); |
|
| 139 | |||
| 140 | continue; |
||
| 141 | } |
||
| 142 | if (! $isDir) { |
||
| 143 | 10 | unlink($object->getPathname()); |
|
| 144 | } |
||
| 145 | } |
||
| 146 | } |
||
| 147 | |||
| 148 | 10 | return true; |
|
| 149 | } catch (Throwable) { |
||
| 150 | return false; |
||
| 151 | } |
||
| 152 | } |
||
| 153 | } |
||
| 154 | |||
| 155 | if (! function_exists('get_filenames')) { |
||
| 156 | /** |
||
| 157 | * Get Filenames |
||
| 158 | * |
||
| 159 | * Reads the specified directory and builds an array containing the filenames. |
||
| 160 | * Any sub-folders contained within the specified path are read as well. |
||
| 161 | * |
||
| 162 | * @param string $sourceDir Path to source |
||
| 163 | * @param bool|null $includePath Whether to include the path as part of the filename; false for no path, null for a relative path, true for full path |
||
| 164 | * @param bool $hidden Whether to include hidden files (files beginning with a period) |
||
| 165 | * @param bool $includeDir Whether to include directories |
||
| 166 | */ |
||
| 167 | function get_filenames( |
||
| 168 | string $sourceDir, |
||
| 169 | ?bool $includePath = false, |
||
| 170 | bool $hidden = false, |
||
| 171 | bool $includeDir = true |
||
| 172 | ): array { |
||
| 173 | $files = []; |
||
| 174 | |||
| 175 | $sourceDir = realpath($sourceDir) ?: $sourceDir; |
||
| 176 | $sourceDir = rtrim($sourceDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
||
| 177 | |||
| 178 | try { |
||
| 179 | foreach (new RecursiveIteratorIterator( |
||
| 180 | new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS), |
||
| 181 | RecursiveIteratorIterator::SELF_FIRST |
||
| 182 | ) as $name => $object) { |
||
| 183 | $basename = pathinfo($name, PATHINFO_BASENAME); |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 184 | if (! $hidden && $basename[0] === '.') { |
||
| 185 | continue; |
||
| 186 | } |
||
| 187 | |||
| 188 | if ($includeDir || ! $object->isDir()) { |
||
| 189 | if ($includePath === false) { |
||
| 190 | $files[] = $basename; |
||
| 191 | } elseif ($includePath === null) { |
||
| 192 | $files[] = str_replace($sourceDir, '', $name); |
||
| 193 | } else { |
||
| 194 | $files[] = $name; |
||
| 195 | } |
||
| 196 | } |
||
| 197 | } |
||
| 198 | } catch (Throwable) { |
||
| 199 | return []; |
||
| 200 | } |
||
| 201 | |||
| 202 | sort($files); |
||
| 203 | |||
| 204 | return $files; |
||
| 205 | } |
||
| 206 | } |
||
| 207 | |||
| 208 | if (! function_exists('get_dir_file_info')) { |
||
| 209 | /** |
||
| 210 | * Get Directory File Information |
||
| 211 | * |
||
| 212 | * Reads the specified directory and builds an array containing the filenames, |
||
| 213 | * filesize, dates, and permissions |
||
| 214 | * |
||
| 215 | * Any sub-folders contained within the specified path are read as well. |
||
| 216 | * |
||
| 217 | * @param string $sourceDir Path to source |
||
| 218 | * @param bool $topLevelOnly Look only at the top level directory specified? |
||
| 219 | * @param bool $recursion Internal variable to determine recursion status - do not use in calls |
||
| 220 | */ |
||
| 221 | function get_dir_file_info(string $sourceDir, bool $topLevelOnly = true, bool $recursion = false): array |
||
| 222 | { |
||
| 223 | static $fileData = []; |
||
| 224 | $relativePath = $sourceDir; |
||
| 225 | |||
| 226 | try { |
||
| 227 | $fp = opendir($sourceDir); |
||
| 228 | |||
| 229 | // reset the array and make sure $source_dir has a trailing slash on the initial call |
||
| 230 | if ($recursion === false) { |
||
| 231 | $fileData = []; |
||
| 232 | $sourceDir = rtrim(realpath($sourceDir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
||
| 233 | } |
||
| 234 | |||
| 235 | // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast |
||
| 236 | while (false !== ($file = readdir($fp))) { |
||
| 237 | if (is_dir($sourceDir . $file) && $file[0] !== '.' && $topLevelOnly === false) { |
||
| 238 | get_dir_file_info($sourceDir . $file . DIRECTORY_SEPARATOR, $topLevelOnly, true); |
||
| 239 | } elseif ($file[0] !== '.') { |
||
| 240 | $fileData[$file] = get_file_info($sourceDir . $file); |
||
| 241 | $fileData[$file]['relative_path'] = $relativePath; |
||
| 242 | } |
||
| 243 | } |
||
| 244 | |||
| 245 | closedir($fp); |
||
| 246 | |||
| 247 | return $fileData; |
||
| 248 | } catch (Throwable) { |
||
| 249 | return []; |
||
| 250 | } |
||
| 251 | } |
||
| 252 | } |
||
| 253 | |||
| 254 | if (! function_exists('get_file_info')) { |
||
| 255 | /** |
||
| 256 | * Get File Info |
||
| 257 | * |
||
| 258 | * Given a file and path, returns the name, path, size, date modified |
||
| 259 | * Second parameter allows you to explicitly declare what information you want returned |
||
| 260 | * Options are: name, server_path, size, date, readable, writable, executable, fileperms |
||
| 261 | * Returns false if the file cannot be found. |
||
| 262 | * |
||
| 263 | * @param string $file Path to file |
||
| 264 | * @param mixed $returnedValues Array or comma separated string of information returned |
||
| 265 | * |
||
| 266 | * @return array|null |
||
| 267 | */ |
||
| 268 | function get_file_info(string $file, $returnedValues = ['name', 'server_path', 'size', 'date']) |
||
| 269 | { |
||
| 270 | if (! is_file($file)) { |
||
| 271 | return null; |
||
| 272 | } |
||
| 273 | |||
| 274 | $fileInfo = []; |
||
| 275 | |||
| 276 | if (is_string($returnedValues)) { |
||
| 277 | $returnedValues = explode(',', $returnedValues); |
||
| 278 | } |
||
| 279 | |||
| 280 | foreach ($returnedValues as $key) { |
||
| 281 | switch ($key) { |
||
| 282 | case 'name': |
||
| 283 | $fileInfo['name'] = basename($file); |
||
| 284 | break; |
||
| 285 | |||
| 286 | case 'server_path': |
||
| 287 | $fileInfo['server_path'] = $file; |
||
| 288 | break; |
||
| 289 | |||
| 290 | case 'size': |
||
| 291 | $fileInfo['size'] = filesize($file); |
||
| 292 | break; |
||
| 293 | |||
| 294 | case 'date': |
||
| 295 | $fileInfo['date'] = filemtime($file); |
||
| 296 | break; |
||
| 297 | |||
| 298 | case 'readable': |
||
| 299 | $fileInfo['readable'] = is_readable($file); |
||
| 300 | break; |
||
| 301 | |||
| 302 | case 'writable': |
||
| 303 | $fileInfo['writable'] = is_really_writable($file); |
||
| 304 | break; |
||
| 305 | |||
| 306 | case 'executable': |
||
| 307 | $fileInfo['executable'] = is_executable($file); |
||
| 308 | break; |
||
| 309 | |||
| 310 | case 'fileperms': |
||
| 311 | $fileInfo['fileperms'] = fileperms($file); |
||
| 312 | break; |
||
| 313 | } |
||
| 314 | } |
||
| 315 | |||
| 316 | return $fileInfo; |
||
| 317 | } |
||
| 318 | } |
||
| 319 | |||
| 320 | if (! function_exists('symbolic_permissions')) { |
||
| 321 | /** |
||
| 322 | * Symbolic Permissions |
||
| 323 | * |
||
| 324 | * Takes a numeric value representing a file's permissions and returns |
||
| 325 | * standard symbolic notation representing that value |
||
| 326 | * |
||
| 327 | * @param int $perms Permissions |
||
| 328 | */ |
||
| 329 | function symbolic_permissions(int $perms): string |
||
| 330 | { |
||
| 331 | if (($perms & 0xC000) === 0xC000) { |
||
| 332 | $symbolic = 's'; // Socket |
||
| 333 | } elseif (($perms & 0xA000) === 0xA000) { |
||
| 334 | $symbolic = 'l'; // Symbolic Link |
||
| 335 | } elseif (($perms & 0x8000) === 0x8000) { |
||
| 336 | $symbolic = '-'; // Regular |
||
| 337 | } elseif (($perms & 0x6000) === 0x6000) { |
||
| 338 | $symbolic = 'b'; // Block special |
||
| 339 | } elseif (($perms & 0x4000) === 0x4000) { |
||
| 340 | $symbolic = 'd'; // Directory |
||
| 341 | } elseif (($perms & 0x2000) === 0x2000) { |
||
| 342 | $symbolic = 'c'; // Character special |
||
| 343 | } elseif (($perms & 0x1000) === 0x1000) { |
||
| 344 | $symbolic = 'p'; // FIFO pipe |
||
| 345 | } else { |
||
| 346 | $symbolic = 'u'; // Unknown |
||
| 347 | } |
||
| 348 | |||
| 349 | // Owner |
||
| 350 | $symbolic .= ((($perms & 0x0100) !== 0) ? 'r' : '-') |
||
| 351 | . ((($perms & 0x0080) !== 0) ? 'w' : '-') |
||
| 352 | . ((($perms & 0x0040) !== 0) ? ((($perms & 0x0800) !== 0) ? 's' : 'x') : ((($perms & 0x0800) !== 0) ? 'S' : '-')); |
||
| 353 | |||
| 354 | // Group |
||
| 355 | $symbolic .= ((($perms & 0x0020) !== 0) ? 'r' : '-') |
||
| 356 | . ((($perms & 0x0010) !== 0) ? 'w' : '-') |
||
| 357 | . ((($perms & 0x0008) !== 0) ? ((($perms & 0x0400) !== 0) ? 's' : 'x') : ((($perms & 0x0400) !== 0) ? 'S' : '-')); |
||
| 358 | |||
| 359 | // World |
||
| 360 | $symbolic .= ((($perms & 0x0004) !== 0) ? 'r' : '-') |
||
| 361 | . ((($perms & 0x0002) !== 0) ? 'w' : '-') |
||
| 362 | . ((($perms & 0x0001) !== 0) ? ((($perms & 0x0200) !== 0) ? 't' : 'x') : ((($perms & 0x0200) !== 0) ? 'T' : '-')); |
||
| 363 | |||
| 364 | return $symbolic; |
||
| 365 | } |
||
| 366 | } |
||
| 367 | |||
| 368 | if (! function_exists('octal_permissions')) { |
||
| 369 | /** |
||
| 370 | * Octal Permissions |
||
| 371 | * |
||
| 372 | * Takes a numeric value representing a file's permissions and returns |
||
| 373 | * a three character string representing the file's octal permissions |
||
| 374 | * |
||
| 375 | * @param int $perms Permissions |
||
| 376 | */ |
||
| 377 | function octal_permissions(int $perms): string |
||
| 378 | { |
||
| 379 | return substr(sprintf('%o', $perms), -3); |
||
| 380 | } |
||
| 381 | } |
||
| 382 | |||
| 383 | if (! function_exists('same_file')) { |
||
| 384 | /** |
||
| 385 | * Checks if two files both exist and have identical hashes |
||
| 386 | * |
||
| 387 | * @return bool Same or not |
||
| 388 | */ |
||
| 389 | function same_file(string $file1, string $file2): bool |
||
| 390 | { |
||
| 391 | 2 | return is_file($file1) && is_file($file2) && md5_file($file1) === md5_file($file2); |
|
| 392 | } |
||
| 393 | } |
||
| 394 | |||
| 395 | if (! function_exists('set_realpath')) { |
||
| 396 | /** |
||
| 397 | * Set Realpath |
||
| 398 | * |
||
| 399 | * @param bool $checkExistence Checks to see if the path exists |
||
| 400 | */ |
||
| 401 | function set_realpath(string $path, bool $checkExistence = false): string |
||
| 402 | { |
||
| 403 | // Security check to make sure the path is NOT a URL. No remote file inclusion! |
||
| 404 | if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp)#i', $path) || filter_var($path, FILTER_VALIDATE_IP) === $path) { |
||
| 405 | 12 | throw new InvalidArgumentException('The path you submitted must be a local server path, not a URL'); |
|
| 406 | } |
||
| 407 | |||
| 408 | // Resolve the path |
||
| 409 | if (realpath($path) !== false) { |
||
| 410 | 12 | $path = realpath($path); |
|
| 411 | } elseif ($checkExistence && ! is_dir($path) && ! is_file($path)) { |
||
| 412 | 2 | throw new InvalidArgumentException('Not a valid path: ' . $path); |
|
| 413 | } |
||
| 414 | |||
| 415 | // Add a trailing slash, if this is a directory |
||
| 416 | 12 | return is_dir($path) ? rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $path; |
|
| 417 | } |
||
| 418 | } |
||
| 419 |