[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Part of the Joomla Framework Filesystem Package 4 * 5 * @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved. 6 * @license GNU General Public License version 2 or later; see LICENSE 7 */ 8 9 namespace Joomla\Filesystem; 10 11 use Joomla\Filesystem\Exception\FilesystemException; 12 13 /** 14 * A Folder handling class 15 * 16 * @since 1.0 17 */ 18 abstract class Folder 19 { 20 /** 21 * Copy a folder. 22 * 23 * @param string $src The path to the source folder. 24 * @param string $dest The path to the destination folder. 25 * @param string $path An optional base path to prefix to the file names. 26 * @param boolean $force Force copy. 27 * @param boolean $useStreams Optionally force folder/file overwrites. 28 * 29 * @return boolean True on success. 30 * 31 * @since 1.0 32 * @throws FilesystemException 33 */ 34 public static function copy($src, $dest, $path = '', $force = false, $useStreams = false) 35 { 36 @set_time_limit(ini_get('max_execution_time')); 37 38 if ($path) 39 { 40 $src = Path::clean($path . '/' . $src); 41 $dest = Path::clean($path . '/' . $dest); 42 } 43 44 // Eliminate trailing directory separators, if any 45 $src = rtrim($src, \DIRECTORY_SEPARATOR); 46 $dest = rtrim($dest, \DIRECTORY_SEPARATOR); 47 48 if (!is_dir(Path::clean($src))) 49 { 50 throw new FilesystemException('Source folder not found', -1); 51 } 52 53 if (is_dir(Path::clean($dest)) && !$force) 54 { 55 throw new FilesystemException('Destination folder not found', -1); 56 } 57 58 // Make sure the destination exists 59 if (!self::create($dest)) 60 { 61 throw new FilesystemException('Cannot create destination folder', -1); 62 } 63 64 if (!($dh = @opendir($src))) 65 { 66 throw new FilesystemException('Cannot open source folder', -1); 67 } 68 69 // Walk through the directory copying files and recursing into folders. 70 while (($file = readdir($dh)) !== false) 71 { 72 $sfid = $src . '/' . $file; 73 $dfid = $dest . '/' . $file; 74 75 switch (filetype($sfid)) 76 { 77 case 'dir': 78 if ($file != '.' && $file != '..') 79 { 80 $ret = self::copy($sfid, $dfid, null, $force, $useStreams); 81 82 if ($ret !== true) 83 { 84 return $ret; 85 } 86 } 87 88 break; 89 90 case 'file': 91 if ($useStreams) 92 { 93 Stream::getStream()->copy($sfid, $dfid); 94 } 95 else 96 { 97 if (!@copy($sfid, $dfid)) 98 { 99 throw new FilesystemException('Copy file failed', -1); 100 } 101 } 102 103 break; 104 } 105 } 106 107 return true; 108 } 109 110 /** 111 * Create a folder -- and all necessary parent folders. 112 * 113 * @param string $path A path to create from the base path. 114 * @param integer $mode Directory permissions to set for folders created. 0755 by default. 115 * 116 * @return boolean True if successful. 117 * 118 * @since 1.0 119 * @throws FilesystemException 120 */ 121 public static function create($path = '', $mode = 0755) 122 { 123 static $nested = 0; 124 125 // Check to make sure the path valid and clean 126 $path = Path::clean($path); 127 128 // Check if parent dir exists 129 $parent = \dirname($path); 130 131 if (!is_dir(Path::clean($parent))) 132 { 133 // Prevent infinite loops! 134 $nested++; 135 136 if (($nested > 20) || ($parent == $path)) 137 { 138 throw new FilesystemException(__METHOD__ . ': Infinite loop detected'); 139 } 140 141 try 142 { 143 // Create the parent directory 144 if (self::create($parent, $mode) !== true) 145 { 146 // Folder::create throws an error 147 $nested--; 148 149 return false; 150 } 151 } 152 catch (FilesystemException $exception) 153 { 154 $nested--; 155 156 throw $exception; 157 } 158 159 // OK, parent directory has been created 160 $nested--; 161 } 162 163 // Check if dir already exists 164 if (is_dir(Path::clean($path))) 165 { 166 return true; 167 } 168 169 // We need to get and explode the open_basedir paths 170 $obd = ini_get('open_basedir'); 171 172 // If open_basedir is set we need to get the open_basedir that the path is in 173 if ($obd != null) 174 { 175 if (\defined('PHP_WINDOWS_VERSION_MAJOR')) 176 { 177 $obdSeparator = ';'; 178 } 179 else 180 { 181 $obdSeparator = ':'; 182 } 183 184 // Create the array of open_basedir paths 185 $obdArray = explode($obdSeparator, $obd); 186 $inBaseDir = false; 187 188 // Iterate through open_basedir paths looking for a match 189 foreach ($obdArray as $test) 190 { 191 $test = Path::clean($test); 192 193 if (strpos($path, $test) === 0 || strpos($path, realpath($test)) === 0) 194 { 195 $inBaseDir = true; 196 197 break; 198 } 199 } 200 201 if ($inBaseDir == false) 202 { 203 // Throw a FilesystemException because the path to be created is not in open_basedir 204 throw new FilesystemException(__METHOD__ . ': Path not in open_basedir paths'); 205 } 206 } 207 208 // First set umask 209 $origmask = @umask(0); 210 211 // Create the path 212 if (!$ret = @mkdir($path, $mode)) 213 { 214 @umask($origmask); 215 216 throw new FilesystemException(__METHOD__ . ': Could not create directory. Path: ' . $path); 217 } 218 219 // Reset umask 220 @umask($origmask); 221 222 return $ret; 223 } 224 225 /** 226 * Delete a folder. 227 * 228 * @param string $path The path to the folder to delete. 229 * 230 * @return boolean True on success. 231 * 232 * @since 1.0 233 * @throws FilesystemException 234 * @throws \UnexpectedValueException 235 */ 236 public static function delete($path) 237 { 238 @set_time_limit(ini_get('max_execution_time')); 239 240 // Sanity check 241 if (!$path) 242 { 243 // Bad programmer! Bad Bad programmer! 244 throw new FilesystemException(__METHOD__ . ': You can not delete a base directory.'); 245 } 246 247 // Check to make sure the path valid and clean 248 $path = Path::clean($path); 249 250 // Is this really a folder? 251 if (!is_dir($path)) 252 { 253 throw new \UnexpectedValueException( 254 sprintf( 255 '%1$s: Path is not a folder. Path: %2$s', 256 __METHOD__, 257 Path::removeRoot($path) 258 ) 259 ); 260 } 261 262 // Remove all the files in folder if they exist; disable all filtering 263 $files = self::files($path, '.', false, true, [], []); 264 265 if (!empty($files)) 266 { 267 if (File::delete($files) !== true) 268 { 269 // File::delete throws an error 270 return false; 271 } 272 } 273 274 // Remove sub-folders of folder; disable all filtering 275 $folders = self::folders($path, '.', false, true, [], []); 276 277 foreach ($folders as $folder) 278 { 279 if (is_link($folder)) 280 { 281 // Don't descend into linked directories, just delete the link. 282 if (File::delete($folder) !== true) 283 { 284 // File::delete throws an error 285 return false; 286 } 287 } 288 elseif (self::delete($folder) !== true) 289 { 290 // Folder::delete throws an error 291 return false; 292 } 293 } 294 295 // In case of restricted permissions we zap it one way or the other as long as the owner is either the webserver or the ftp. 296 if (@rmdir($path)) 297 { 298 return true; 299 } 300 301 throw new FilesystemException(sprintf('%1$s: Could not delete folder. Path: %2$s', __METHOD__, $path)); 302 } 303 304 /** 305 * Moves a folder. 306 * 307 * @param string $src The path to the source folder. 308 * @param string $dest The path to the destination folder. 309 * @param string $path An optional base path to prefix to the file names. 310 * @param boolean $useStreams Optionally use streams. 311 * 312 * @return string|boolean Error message on false or boolean true on success. 313 * 314 * @since 1.0 315 */ 316 public static function move($src, $dest, $path = '', $useStreams = false) 317 { 318 if ($path) 319 { 320 $src = Path::clean($path . '/' . $src); 321 $dest = Path::clean($path . '/' . $dest); 322 } 323 324 if (!is_dir(Path::clean($src))) 325 { 326 return 'Cannot find source folder'; 327 } 328 329 if (is_dir(Path::clean($dest))) 330 { 331 return 'Folder already exists'; 332 } 333 334 if ($useStreams) 335 { 336 Stream::getStream()->move($src, $dest); 337 338 return true; 339 } 340 341 if (!@rename($src, $dest)) 342 { 343 return 'Rename failed'; 344 } 345 346 return true; 347 } 348 349 /** 350 * Utility function to read the files in a folder. 351 * 352 * @param string $path The path of the folder to read. 353 * @param string $filter A filter for file names. 354 * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. 355 * @param boolean $full True to return the full path to the file. 356 * @param array $exclude Array with names of files which should not be shown in the result. 357 * @param array $excludeFilter Array of filter to exclude 358 * 359 * @return array Files in the given folder. 360 * 361 * @since 1.0 362 * @throws \UnexpectedValueException 363 */ 364 public static function files($path, $filter = '.', $recurse = false, $full = false, $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], 365 $excludeFilter = ['^\..*', '.*~'] 366 ) 367 { 368 // Check to make sure the path valid and clean 369 $path = Path::clean($path); 370 371 // Is the path a folder? 372 if (!is_dir($path)) 373 { 374 throw new \UnexpectedValueException( 375 sprintf( 376 '%1$s: Path is not a folder. Path: %2$s', 377 __METHOD__, 378 Path::removeRoot($path) 379 ) 380 ); 381 } 382 383 // Compute the excludefilter string 384 if (\count($excludeFilter)) 385 { 386 $excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/'; 387 } 388 else 389 { 390 $excludeFilterString = ''; 391 } 392 393 // Get the files 394 $arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, true); 395 396 // Sort the files 397 asort($arr); 398 399 return array_values($arr); 400 } 401 402 /** 403 * Utility function to read the folders in a folder. 404 * 405 * @param string $path The path of the folder to read. 406 * @param string $filter A filter for folder names. 407 * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. 408 * @param boolean $full True to return the full path to the folders. 409 * @param array $exclude Array with names of folders which should not be shown in the result. 410 * @param array $excludeFilter Array with regular expressions matching folders which should not be shown in the result. 411 * 412 * @return array Folders in the given folder. 413 * 414 * @since 1.0 415 * @throws \UnexpectedValueException 416 */ 417 public static function folders($path, $filter = '.', $recurse = false, $full = false, $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], 418 $excludeFilter = ['^\..*'] 419 ) 420 { 421 // Check to make sure the path valid and clean 422 $path = Path::clean($path); 423 424 // Is the path a folder? 425 if (!is_dir($path)) 426 { 427 throw new \UnexpectedValueException( 428 sprintf( 429 '%1$s: Path is not a folder. Path: %2$s', 430 __METHOD__, 431 Path::removeRoot($path) 432 ) 433 ); 434 } 435 436 // Compute the excludefilter string 437 if (\count($excludeFilter)) 438 { 439 $excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/'; 440 } 441 else 442 { 443 $excludeFilterString = ''; 444 } 445 446 // Get the folders 447 $arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, false); 448 449 // Sort the folders 450 asort($arr); 451 452 return array_values($arr); 453 } 454 455 /** 456 * Function to read the files/folders in a folder. 457 * 458 * @param string $path The path of the folder to read. 459 * @param string $filter A filter for file names. 460 * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. 461 * @param boolean $full True to return the full path to the file. 462 * @param array $exclude Array with names of files which should not be shown in the result. 463 * @param string $excludeFilterString Regexp of files to exclude 464 * @param boolean $findfiles True to read the files, false to read the folders 465 * 466 * @return array Files. 467 * 468 * @since 1.0 469 */ 470 protected static function _items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, $findfiles) 471 { 472 @set_time_limit(ini_get('max_execution_time')); 473 474 $arr = []; 475 476 // Read the source directory 477 if (!($handle = @opendir($path))) 478 { 479 return $arr; 480 } 481 482 while (($file = readdir($handle)) !== false) 483 { 484 if ($file != '.' && $file != '..' && !\in_array($file, $exclude) 485 && (empty($excludeFilterString) || !preg_match($excludeFilterString, $file))) 486 { 487 // Compute the fullpath 488 $fullpath = Path::clean($path . '/' . $file); 489 490 // Compute the isDir flag 491 $isDir = is_dir($fullpath); 492 493 if (($isDir xor $findfiles) && preg_match("/$filter/", $file)) 494 { 495 // (fullpath is dir and folders are searched or fullpath is not dir and files are searched) and file matches the filter 496 if ($full) 497 { 498 // Full path is requested 499 $arr[] = $fullpath; 500 } 501 else 502 { 503 // Filename is requested 504 $arr[] = $file; 505 } 506 } 507 508 if ($isDir && $recurse) 509 { 510 // Search recursively 511 if (\is_int($recurse)) 512 { 513 // Until depth 0 is reached 514 $arr = array_merge($arr, self::_items($fullpath, $filter, $recurse - 1, $full, $exclude, $excludeFilterString, $findfiles)); 515 } 516 else 517 { 518 $arr = array_merge($arr, self::_items($fullpath, $filter, $recurse, $full, $exclude, $excludeFilterString, $findfiles)); 519 } 520 } 521 } 522 } 523 524 closedir($handle); 525 526 return $arr; 527 } 528 529 /** 530 * Lists folder in format suitable for tree display. 531 * 532 * @param string $path The path of the folder to read. 533 * @param string $filter A filter for folder names. 534 * @param integer $maxLevel The maximum number of levels to recursively read, defaults to three. 535 * @param integer $level The current level, optional. 536 * @param integer $parent Unique identifier of the parent folder, if any. 537 * 538 * @return array Folders in the given folder. 539 * 540 * @since 1.0 541 */ 542 public static function listFolderTree($path, $filter, $maxLevel = 3, $level = 0, $parent = 0) 543 { 544 $dirs = []; 545 546 if ($level == 0) 547 { 548 $GLOBALS['_JFolder_folder_tree_index'] = 0; 549 } 550 551 if ($level < $maxLevel) 552 { 553 $folders = self::folders($path, $filter); 554 555 // First path, index foldernames 556 foreach ($folders as $name) 557 { 558 $id = ++$GLOBALS['_JFolder_folder_tree_index']; 559 $fullName = Path::clean($path . '/' . $name); 560 $dirs[] = [ 561 'id' => $id, 562 'parent' => $parent, 563 'name' => $name, 564 'fullname' => $fullName, 565 'relname' => str_replace(JPATH_ROOT, '', $fullName), 566 ]; 567 $dirs2 = self::listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id); 568 $dirs = array_merge($dirs, $dirs2); 569 } 570 } 571 572 return $dirs; 573 } 574 575 /** 576 * Makes path name safe to use. 577 * 578 * @param string $path The full path to sanitise. 579 * 580 * @return string The sanitised string. 581 * 582 * @since 1.0 583 */ 584 public static function makeSafe($path) 585 { 586 $regex = ['#[^A-Za-z0-9_\\\/\(\)\[\]\{\}\#\$\^\+\.\'~`!@&=;,-]#']; 587 588 return preg_replace($regex, '', $path); 589 } 590 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |