[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/filesystem/src/ -> Folder.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer