Source for file folder.php

Documentation is available at folder.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  FileSystem
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM'or die;
  11.  
  12. jimport('joomla.filesystem.path');
  13.  
  14. /**
  15.  * A Folder handling class
  16.  *
  17.  * @package     Joomla.Platform
  18.  * @subpackage  FileSystem
  19.  * @since       11.1
  20.  */
  21. abstract class JFolder
  22. {
  23.     /**
  24.      * Copy a folder.
  25.      *
  26.      * @param   string   $src          The path to the source folder.
  27.      * @param   string   $dest         The path to the destination folder.
  28.      * @param   string   $path         An optional base path to prefix to the file names.
  29.      * @param   boolean  $force        Force copy.
  30.      * @param   boolean  $use_streams  Optionally force folder/file overwrites.
  31.      *
  32.      * @return  boolean  True on success.
  33.      *
  34.      * @since   11.1
  35.      * @throws  RuntimeException
  36.      */
  37.     public static function copy($src$dest$path ''$force false$use_streams false)
  38.     {
  39.         @set_time_limit(ini_get('max_execution_time'));
  40.  
  41.         $FTPOptions JClientHelper::getCredentials('ftp');
  42.  
  43.         if ($path)
  44.         {
  45.             $src JPath::clean($path '/' $src);
  46.             $dest JPath::clean($path '/' $dest);
  47.         }
  48.  
  49.         // Eliminate trailing directory separators, if any
  50.         $src rtrim($srcDIRECTORY_SEPARATOR);
  51.         $dest rtrim($destDIRECTORY_SEPARATOR);
  52.  
  53.         if (!self::exists($src))
  54.         {
  55.             throw new RuntimeException('Source folder not found'-1);
  56.         }
  57.         if (self::exists($dest&& !$force)
  58.         {
  59.             throw new RuntimeException('Destination folder not found'-1);
  60.         }
  61.  
  62.         // Make sure the destination exists
  63.         if (!self::create($dest))
  64.         {
  65.             throw new RuntimeException('Cannot create destination folder'-1);
  66.         }
  67.  
  68.         // If we're using ftp and don't have streams enabled
  69.         if ($FTPOptions['enabled'== && !$use_streams)
  70.         {
  71.             // Connect the FTP client
  72.             $ftp JClientFtp::getInstance($FTPOptions['host']$FTPOptions['port']array()$FTPOptions['user']$FTPOptions['pass']);
  73.  
  74.             if (!($dh @opendir($src)))
  75.             {
  76.                 throw new RuntimeException('Cannot open source folder'-1);
  77.             }
  78.             // Walk through the directory copying files and recursing into folders.
  79.             while (($file readdir($dh)) !== false)
  80.             {
  81.                 $sfid $src '/' $file;
  82.                 $dfid $dest '/' $file;
  83.  
  84.                 switch (filetype($sfid))
  85.                 {
  86.                     case 'dir':
  87.                         if ($file != '.' && $file != '..')
  88.                         {
  89.                             $ret self::copy($sfid$dfidnull$force);
  90.  
  91.                             if ($ret !== true)
  92.                             {
  93.                                 return $ret;
  94.                             }
  95.                         }
  96.                         break;
  97.  
  98.                     case 'file':
  99.                         // Translate path for the FTP account
  100.                         $dfid JPath::clean(str_replace(JPATH_ROOT$FTPOptions['root']$dfid)'/');
  101.  
  102.                         if (!$ftp->store($sfid$dfid))
  103.                         {
  104.                             throw new RuntimeException('Copy file failed'-1);
  105.                         }
  106.                         break;
  107.                 }
  108.             }
  109.         }
  110.         else
  111.         {
  112.             if (!($dh @opendir($src)))
  113.             {
  114.                 throw new RuntimeException('Cannot open source folder'-1);
  115.             }
  116.             // Walk through the directory copying files and recursing into folders.
  117.             while (($file readdir($dh)) !== false)
  118.             {
  119.                 $sfid $src '/' $file;
  120.                 $dfid $dest '/' $file;
  121.  
  122.                 switch (filetype($sfid))
  123.                 {
  124.                     case 'dir':
  125.                         if ($file != '.' && $file != '..')
  126.                         {
  127.                             $ret self::copy($sfid$dfidnull$force$use_streams);
  128.  
  129.                             if ($ret !== true)
  130.                             {
  131.                                 return $ret;
  132.                             }
  133.                         }
  134.                         break;
  135.  
  136.                     case 'file':
  137.                         if ($use_streams)
  138.                         {
  139.                             $stream JFactory::getStream();
  140.  
  141.                             if (!$stream->copy($sfid$dfid))
  142.                             {
  143.                                 throw new RuntimeException('Cannot copy file: ' $stream->getError()-1);
  144.                             }
  145.                         }
  146.                         else
  147.                         {
  148.                             if (!@copy($sfid$dfid))
  149.                             {
  150.                                 throw new RuntimeException('Copy file failed'-1);
  151.                             }
  152.                         }
  153.                         break;
  154.                 }
  155.             }
  156.         }
  157.         return true;
  158.     }
  159.  
  160.     /**
  161.      * Create a folder -- and all necessary parent folders.
  162.      *
  163.      * @param   string   $path  A path to create from the base path.
  164.      * @param   integer  $mode  Directory permissions to set for folders created. 0755 by default.
  165.      *
  166.      * @return  boolean  True if successful.
  167.      *
  168.      * @since   11.1
  169.      */
  170.     public static function create($path ''$mode 0755)
  171.     {
  172.         $FTPOptions JClientHelper::getCredentials('ftp');
  173.         static $nested 0;
  174.  
  175.         // Check to make sure the path valid and clean
  176.         $path JPath::clean($path);
  177.  
  178.         // Check if parent dir exists
  179.         $parent dirname($path);
  180.  
  181.         if (!self::exists($parent))
  182.         {
  183.             // Prevent infinite loops!
  184.             $nested++;
  185.  
  186.             if (($nested 20|| ($parent == $path))
  187.             {
  188.                 JLog::add(__METHOD__ . ': ' JText::_('JLIB_FILESYSTEM_ERROR_FOLDER_LOOP')JLog::WARNING'jerror');
  189.                 $nested--;
  190.  
  191.                 return false;
  192.             }
  193.  
  194.             // Create the parent directory
  195.             if (self::create($parent$mode!== true)
  196.             {
  197.                 // JFolder::create throws an error
  198.                 $nested--;
  199.  
  200.                 return false;
  201.             }
  202.  
  203.             // OK, parent directory has been created
  204.             $nested--;
  205.         }
  206.  
  207.         // Check if dir already exists
  208.         if (self::exists($path))
  209.         {
  210.             return true;
  211.         }
  212.  
  213.         // Check for safe mode
  214.         if ($FTPOptions['enabled'== 1)
  215.         {
  216.             // Connect the FTP client
  217.             $ftp JClientFtp::getInstance($FTPOptions['host']$FTPOptions['port']array()$FTPOptions['user']$FTPOptions['pass']);
  218.  
  219.             // Translate path to FTP path
  220.             $path JPath::clean(str_replace(JPATH_ROOT$FTPOptions['root']$path)'/');
  221.             $ret $ftp->mkdir($path);
  222.             $ftp->chmod($path$mode);
  223.         }
  224.         else
  225.         {
  226.             // We need to get and explode the open_basedir paths
  227.             $obd ini_get('open_basedir');
  228.  
  229.             // If open_basedir is set we need to get the open_basedir that the path is in
  230.             if ($obd != null)
  231.             {
  232.                 if (IS_WIN)
  233.                 {
  234.                     $obdSeparator ";";
  235.                 }
  236.                 else
  237.                 {
  238.                     $obdSeparator ":";
  239.                 }
  240.  
  241.                 // Create the array of open_basedir paths
  242.                 $obdArray explode($obdSeparator$obd);
  243.                 $inBaseDir false;
  244.  
  245.                 // Iterate through open_basedir paths looking for a match
  246.                 foreach ($obdArray as $test)
  247.                 {
  248.                     $test JPath::clean($test);
  249.  
  250.                     if (strpos($path$test=== 0)
  251.                     {
  252.                         $inBaseDir true;
  253.                         break;
  254.                     }
  255.                 }
  256.                 if ($inBaseDir == false)
  257.                 {
  258.                     // Return false for JFolder::create because the path to be created is not in open_basedir
  259.                     JLog::add(__METHOD__ . ': ' JText::_('JLIB_FILESYSTEM_ERROR_FOLDER_PATH')JLog::WARNING'jerror');
  260.  
  261.                     return false;
  262.                 }
  263.             }
  264.  
  265.             // First set umask
  266.             $origmask @umask(0);
  267.  
  268.             // Create the path
  269.             if (!$ret @mkdir($path$mode))
  270.             {
  271.                 @umask($origmask);
  272.                 JLog::add(
  273.                     __METHOD__ . ': ' JText::_('JLIB_FILESYSTEM_ERROR_COULD_NOT_CREATE_DIRECTORY''Path: ' $pathJLog::WARNING'jerror'
  274.                 );
  275.  
  276.                 return false;
  277.             }
  278.  
  279.             // Reset umask
  280.             @umask($origmask);
  281.         }
  282.         return $ret;
  283.     }
  284.  
  285.     /**
  286.      * Delete a folder.
  287.      *
  288.      * @param   string  $path  The path to the folder to delete.
  289.      *
  290.      * @return  boolean  True on success.
  291.      *
  292.      * @since   11.1
  293.      * @throws  UnexpectedValueException
  294.      */
  295.     public static function delete($path)
  296.     {
  297.         @set_time_limit(ini_get('max_execution_time'));
  298.  
  299.         // Sanity check
  300.         if (!$path)
  301.         {
  302.             // Bad programmer! Bad Bad programmer!
  303.             JLog::add(__METHOD__ . ': ' JText::_('JLIB_FILESYSTEM_ERROR_DELETE_BASE_DIRECTORY')JLog::WARNING'jerror');
  304.  
  305.             return false;
  306.         }
  307.  
  308.         $FTPOptions JClientHelper::getCredentials('ftp');
  309.  
  310.         try
  311.         {
  312.             // Check to make sure the path valid and clean
  313.             $path JPath::clean($path);
  314.         }
  315.         catch (UnexpectedValueException $e)
  316.         {
  317.             throw $e;
  318.         }
  319.  
  320.         // Is this really a folder?
  321.         if (!is_dir($path))
  322.         {
  323.             JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER'$path)JLog::WARNING'jerror');
  324.  
  325.             return false;
  326.         }
  327.  
  328.         // Remove all the files in folder if they exist; disable all filtering
  329.         $files self::files($path'.'falsetruearray()array());
  330.  
  331.         if (!empty($files))
  332.         {
  333.             jimport('joomla.filesystem.file');
  334.  
  335.             if (JFile::delete($files!== true)
  336.             {
  337.                 // JFile::delete throws an error
  338.                 return false;
  339.             }
  340.         }
  341.  
  342.         // Remove sub-folders of folder; disable all filtering
  343.         $folders self::folders($path'.'falsetruearray()array());
  344.  
  345.         foreach ($folders as $folder)
  346.         {
  347.             if (is_link($folder))
  348.             {
  349.                 // Don't descend into linked directories, just delete the link.
  350.                 jimport('joomla.filesystem.file');
  351.  
  352.                 if (JFile::delete($folder!== true)
  353.                 {
  354.                     // JFile::delete throws an error
  355.                     return false;
  356.                 }
  357.             }
  358.             elseif (self::delete($folder!== true)
  359.             {
  360.                 // JFolder::delete throws an error
  361.                 return false;
  362.             }
  363.         }
  364.  
  365.         if ($FTPOptions['enabled'== 1)
  366.         {
  367.             // Connect the FTP client
  368.             $ftp JClientFtp::getInstance($FTPOptions['host']$FTPOptions['port']array()$FTPOptions['user']$FTPOptions['pass']);
  369.         }
  370.  
  371.         // In case of restricted permissions we zap it one way or the other
  372.         // as long as the owner is either the webserver or the ftp.
  373.         if (@rmdir($path))
  374.         {
  375.             $ret true;
  376.         }
  377.         elseif ($FTPOptions['enabled'== 1)
  378.         {
  379.             // Translate path and delete
  380.             $path JPath::clean(str_replace(JPATH_ROOT$FTPOptions['root']$path)'/');
  381.  
  382.             // FTP connector throws an error
  383.             $ret $ftp->delete($path);
  384.         }
  385.         else
  386.         {
  387.             JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_DELETE'$path)JLog::WARNING'jerror');
  388.             $ret false;
  389.         }
  390.  
  391.         return $ret;
  392.     }
  393.  
  394.     /**
  395.      * Moves a folder.
  396.      *
  397.      * @param   string   $src          The path to the source folder.
  398.      * @param   string   $dest         The path to the destination folder.
  399.      * @param   string   $path         An optional base path to prefix to the file names.
  400.      * @param   boolean  $use_streams  Optionally use streams.
  401.      *
  402.      * @return  mixed  Error message on false or boolean true on success.
  403.      *
  404.      * @since   11.1
  405.      */
  406.     public static function move($src$dest$path ''$use_streams false)
  407.     {
  408.         $FTPOptions JClientHelper::getCredentials('ftp');
  409.  
  410.         if ($path)
  411.         {
  412.             $src JPath::clean($path '/' $src);
  413.             $dest JPath::clean($path '/' $dest);
  414.         }
  415.  
  416.         if (!self::exists($src))
  417.         {
  418.             return JText::_('JLIB_FILESYSTEM_ERROR_FIND_SOURCE_FOLDER');
  419.         }
  420.  
  421.         if (self::exists($dest))
  422.         {
  423.             return JText::_('JLIB_FILESYSTEM_ERROR_FOLDER_EXISTS');
  424.         }
  425.  
  426.         if ($use_streams)
  427.         {
  428.             $stream JFactory::getStream();
  429.  
  430.             if (!$stream->move($src$dest))
  431.             {
  432.                 return JText::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_RENAME'$stream->getError());
  433.             }
  434.  
  435.             $ret true;
  436.         }
  437.         else
  438.         {
  439.             if ($FTPOptions['enabled'== 1)
  440.             {
  441.                 // Connect the FTP client
  442.                 $ftp JClientFtp::getInstance($FTPOptions['host']$FTPOptions['port']array()$FTPOptions['user']$FTPOptions['pass']);
  443.  
  444.                 // Translate path for the FTP account
  445.                 $src JPath::clean(str_replace(JPATH_ROOT$FTPOptions['root']$src)'/');
  446.                 $dest JPath::clean(str_replace(JPATH_ROOT$FTPOptions['root']$dest)'/');
  447.  
  448.                 // Use FTP rename to simulate move
  449.                 if (!$ftp->rename($src$dest))
  450.                 {
  451.                     return JText::_('Rename failed');
  452.                 }
  453.  
  454.                 $ret true;
  455.             }
  456.             else
  457.             {
  458.                 if (!@rename($src$dest))
  459.                 {
  460.                     return JText::_('Rename failed');
  461.                 }
  462.  
  463.                 $ret true;
  464.             }
  465.         }
  466.  
  467.         return $ret;
  468.     }
  469.  
  470.     /**
  471.      * Wrapper for the standard file_exists function
  472.      *
  473.      * @param   string  $path  Folder name relative to installation dir
  474.      *
  475.      * @return  boolean  True if path is a folder
  476.      *
  477.      * @since   11.1
  478.      */
  479.     public static function exists($path)
  480.     {
  481.         return is_dir(JPath::clean($path));
  482.     }
  483.  
  484.     /**
  485.      * Utility function to read the files in a folder.
  486.      *
  487.      * @param   string   $path           The path of the folder to read.
  488.      * @param   string   $filter         A filter for file names.
  489.      * @param   mixed    $recurse        True to recursively search into sub-folders, or an integer to specify the maximum depth.
  490.      * @param   boolean  $full           True to return the full path to the file.
  491.      * @param   array    $exclude        Array with names of files which should not be shown in the result.
  492.      * @param   array    $excludefilter  Array of filter to exclude
  493.      * @param   boolean  $naturalSort    False for asort, true for natsort
  494.      *
  495.      * @return  array  Files in the given folder.
  496.      *
  497.      * @since   11.1
  498.      */
  499.     public static function files($path$filter '.'$recurse false$full false$exclude array('.svn''CVS''.DS_Store''__MACOSX'),
  500.         $excludefilter array('^\..*''.*~')$naturalSort false)
  501.     {
  502.         // Check to make sure the path valid and clean
  503.         $path JPath::clean($path);
  504.  
  505.         // Is the path a folder?
  506.         if (!is_dir($path))
  507.         {
  508.             JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER_FILES'$path)JLog::WARNING'jerror');
  509.  
  510.             return false;
  511.         }
  512.  
  513.         // Compute the excludefilter string
  514.         if (count($excludefilter))
  515.         {
  516.             $excludefilter_string '/(' implode('|'$excludefilter')/';
  517.         }
  518.         else
  519.         {
  520.             $excludefilter_string '';
  521.         }
  522.  
  523.         // Get the files
  524.         $arr self::_items($path$filter$recurse$full$exclude$excludefilter_stringtrue);
  525.  
  526.         // Sort the files based on either natural or alpha method
  527.         if ($naturalSort)
  528.         {
  529.             natsort($arr);
  530.         }
  531.         else
  532.         {
  533.             asort($arr);
  534.         }
  535.  
  536.         return array_values($arr);
  537.     }
  538.  
  539.     /**
  540.      * Utility function to read the folders in a folder.
  541.      *
  542.      * @param   string   $path           The path of the folder to read.
  543.      * @param   string   $filter         A filter for folder names.
  544.      * @param   mixed    $recurse        True to recursively search into sub-folders, or an integer to specify the maximum depth.
  545.      * @param   boolean  $full           True to return the full path to the folders.
  546.      * @param   array    $exclude        Array with names of folders which should not be shown in the result.
  547.      * @param   array    $excludefilter  Array with regular expressions matching folders which should not be shown in the result.
  548.      *
  549.      * @return  array  Folders in the given folder.
  550.      *
  551.      * @since   11.1
  552.      */
  553.     public static function folders($path$filter '.'$recurse false$full false$exclude array('.svn''CVS''.DS_Store''__MACOSX'),
  554.         $excludefilter array('^\..*'))
  555.     {
  556.         // Check to make sure the path valid and clean
  557.         $path JPath::clean($path);
  558.  
  559.         // Is the path a folder?
  560.         if (!is_dir($path))
  561.         {
  562.             JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER_FOLDER'$path)JLog::WARNING'jerror');
  563.  
  564.             return false;
  565.         }
  566.  
  567.         // Compute the excludefilter string
  568.         if (count($excludefilter))
  569.         {
  570.             $excludefilter_string '/(' implode('|'$excludefilter')/';
  571.         }
  572.         else
  573.         {
  574.             $excludefilter_string '';
  575.         }
  576.  
  577.         // Get the folders
  578.         $arr self::_items($path$filter$recurse$full$exclude$excludefilter_stringfalse);
  579.  
  580.         // Sort the folders
  581.         asort($arr);
  582.  
  583.         return array_values($arr);
  584.     }
  585.  
  586.     /**
  587.      * Function to read the files/folders in a folder.
  588.      *
  589.      * @param   string   $path                  The path of the folder to read.
  590.      * @param   string   $filter                A filter for file names.
  591.      * @param   mixed    $recurse               True to recursively search into sub-folders, or an integer to specify the maximum depth.
  592.      * @param   boolean  $full                  True to return the full path to the file.
  593.      * @param   array    $exclude               Array with names of files which should not be shown in the result.
  594.      * @param   string   $excludefilter_string  Regexp of files to exclude
  595.      * @param   boolean  $findfiles             True to read the files, false to read the folders
  596.      *
  597.      * @return  array  Files.
  598.      *
  599.      * @since   11.1
  600.      */
  601.     protected static function _items($path$filter$recurse$full$exclude$excludefilter_string$findfiles)
  602.     {
  603.         @set_time_limit(ini_get('max_execution_time'));
  604.  
  605.         $arr array();
  606.  
  607.         // Read the source directory
  608.         if (!($handle @opendir($path)))
  609.         {
  610.             return $arr;
  611.         }
  612.  
  613.         while (($file readdir($handle)) !== false)
  614.         {
  615.             if ($file != '.' && $file != '..' && !in_array($file$exclude)
  616.                 && (empty($excludefilter_string|| !preg_match($excludefilter_string$file)))
  617.             {
  618.                 // Compute the fullpath
  619.                 $fullpath $path '/' $file;
  620.  
  621.                 // Compute the isDir flag
  622.                 $isDir is_dir($fullpath);
  623.  
  624.                 if (($isDir xor $findfiles&& preg_match("/$filter/"$file))
  625.                 {
  626.                     // (fullpath is dir and folders are searched or fullpath is not dir and files are searched) and file matches the filter
  627.                     if ($full)
  628.                     {
  629.                         // Full path is requested
  630.                         $arr[$fullpath;
  631.                     }
  632.                     else
  633.                     {
  634.                         // Filename is requested
  635.                         $arr[$file;
  636.                     }
  637.                 }
  638.  
  639.                 if ($isDir && $recurse)
  640.                 {
  641.                     // Search recursively
  642.                     if (is_int($recurse))
  643.                     {
  644.                         // Until depth 0 is reached
  645.                         $arr array_merge($arrself::_items($fullpath$filter$recurse 1$full$exclude$excludefilter_string$findfiles));
  646.                     }
  647.                     else
  648.                     {
  649.                         $arr array_merge($arrself::_items($fullpath$filter$recurse$full$exclude$excludefilter_string$findfiles));
  650.                     }
  651.                 }
  652.             }
  653.         }
  654.  
  655.         closedir($handle);
  656.  
  657.         return $arr;
  658.     }
  659.  
  660.     /**
  661.      * Lists folder in format suitable for tree display.
  662.      *
  663.      * @param   string   $path      The path of the folder to read.
  664.      * @param   string   $filter    A filter for folder names.
  665.      * @param   integer  $maxLevel  The maximum number of levels to recursively read, defaults to three.
  666.      * @param   integer  $level     The current level, optional.
  667.      * @param   integer  $parent    Unique identifier of the parent folder, if any.
  668.      *
  669.      * @return  array  Folders in the given folder.
  670.      *
  671.      * @since   11.1
  672.      */
  673.     public static function listFolderTree($path$filter$maxLevel 3$level 0$parent 0)
  674.     {
  675.         $dirs array();
  676.  
  677.         if ($level == 0)
  678.         {
  679.             $GLOBALS['_JFolder_folder_tree_index'0;
  680.         }
  681.  
  682.         if ($level $maxLevel)
  683.         {
  684.             $folders self::folders($path$filter);
  685.  
  686.             // First path, index foldernames
  687.             foreach ($folders as $name)
  688.             {
  689.                 $id = ++$GLOBALS['_JFolder_folder_tree_index'];
  690.                 $fullName JPath::clean($path '/' $name);
  691.                 $dirs[array('id' => $id'parent' => $parent'name' => $name'fullname' => $fullName,
  692.                     'relname' => str_replace(JPATH_ROOT''$fullName));
  693.                 $dirs2 self::listFolderTree($fullName$filter$maxLevel$level 1$id);
  694.                 $dirs array_merge($dirs$dirs2);
  695.             }
  696.         }
  697.  
  698.         return $dirs;
  699.     }
  700.  
  701.     /**
  702.      * Makes path name safe to use.
  703.      *
  704.      * @param   string  $path  The full path to sanitise.
  705.      *
  706.      * @return  string  The sanitised string.
  707.      *
  708.      * @since   11.1
  709.      */
  710.     public static function makeSafe($path)
  711.     {
  712.         $regex array('#[^A-Za-z0-9_\\\/\(\)\[\]\{\}\#\$\^\+\.\'~`!@&=;,-]#');
  713.  
  714.         return preg_replace($regex''$path);
  715.     }
  716. }

Documentation generated on Tue, 19 Nov 2013 15:03:26 +0100 by phpDocumentor 1.4.3