PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/framework/utils/CFileHelper.php

https://github.com/LosYear/FluentCMS
PHP | 311 lines | 143 code | 16 blank | 152 comment | 35 complexity | 7867e7e5c84ebc1573f2a95e45d98655 MD5 | raw file
  1. <?php
  2. /**
  3. * CFileHelper class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CFileHelper provides a set of helper methods for common file system operations.
  12. *
  13. * @author Qiang Xue <qiang.xue@gmail.com>
  14. * @package system.utils
  15. * @since 1.0
  16. */
  17. class CFileHelper
  18. {
  19. /**
  20. * Returns the extension name of a file path.
  21. * For example, the path "path/to/something.php" would return "php".
  22. * @param string $path the file path
  23. * @return string the extension name without the dot character.
  24. * @since 1.1.2
  25. */
  26. public static function getExtension($path)
  27. {
  28. return pathinfo($path,PATHINFO_EXTENSION);
  29. }
  30. /**
  31. * Copies a directory recursively as another.
  32. * If the destination directory does not exist, it will be created recursively.
  33. * @param string $src the source directory
  34. * @param string $dst the destination directory
  35. * @param array $options options for directory copy. Valid options are:
  36. * <ul>
  37. * <li>fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be copied.</li>
  38. * <li>exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path.
  39. * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of
  40. * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude
  41. * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant.
  42. * </li>
  43. * <li>level: integer, recursion depth, default=-1.
  44. * Level -1 means copying all directories and files under the directory;
  45. * Level 0 means copying only the files DIRECTLY under the directory;
  46. * level N means copying those directories that are within N levels.
  47. * </li>
  48. * <li>newDirMode - the permission to be set for newly copied directories (defaults to 0777);</li>
  49. * <li>newFileMode - the permission to be set for newly copied files (defaults to the current environment setting).</li>
  50. * </ul>
  51. */
  52. public static function copyDirectory($src,$dst,$options=array())
  53. {
  54. $fileTypes=array();
  55. $exclude=array();
  56. $level=-1;
  57. extract($options);
  58. if(!is_dir($dst))
  59. self::mkdir($dst,$options,true);
  60. self::copyDirectoryRecursive($src,$dst,'',$fileTypes,$exclude,$level,$options);
  61. }
  62. /**
  63. * Removes a directory recursively.
  64. * @param string $directory to be deleted recursively.
  65. * @since 1.1.14
  66. */
  67. public static function removeDirectory($directory)
  68. {
  69. $items=glob($directory.DIRECTORY_SEPARATOR.'{,.}*',GLOB_MARK | GLOB_BRACE);
  70. foreach($items as $item)
  71. {
  72. if(basename($item)=='.' || basename($item)=='..')
  73. continue;
  74. if(substr($item,-1)==DIRECTORY_SEPARATOR)
  75. self::removeDirectory($item);
  76. else
  77. unlink($item);
  78. }
  79. if(is_dir($directory))
  80. rmdir($directory);
  81. }
  82. /**
  83. * Returns the files found under the specified directory and subdirectories.
  84. * @param string $dir the directory under which the files will be looked for
  85. * @param array $options options for file searching. Valid options are:
  86. * <ul>
  87. * <li>fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned.</li>
  88. * <li>exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path.
  89. * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of
  90. * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude
  91. * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant.
  92. * </li>
  93. * <li>level: integer, recursion depth, default=-1.
  94. * Level -1 means searching for all directories and files under the directory;
  95. * Level 0 means searching for only the files DIRECTLY under the directory;
  96. * level N means searching for those directories that are within N levels.
  97. * </li>
  98. * </ul>
  99. * @return array files found under the directory. The file list is sorted.
  100. */
  101. public static function findFiles($dir,$options=array())
  102. {
  103. $fileTypes=array();
  104. $exclude=array();
  105. $level=-1;
  106. extract($options);
  107. $list=self::findFilesRecursive($dir,'',$fileTypes,$exclude,$level);
  108. sort($list);
  109. return $list;
  110. }
  111. /**
  112. * Copies a directory.
  113. * This method is mainly used by {@link copyDirectory}.
  114. * @param string $src the source directory
  115. * @param string $dst the destination directory
  116. * @param string $base the path relative to the original source directory
  117. * @param array $fileTypes list of file name suffix (without dot). Only files with these suffixes will be copied.
  118. * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path.
  119. * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of
  120. * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude
  121. * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant.
  122. * @param integer $level recursion depth. It defaults to -1.
  123. * Level -1 means copying all directories and files under the directory;
  124. * Level 0 means copying only the files DIRECTLY under the directory;
  125. * level N means copying those directories that are within N levels.
  126. * @param array $options additional options. The following options are supported:
  127. * newDirMode - the permission to be set for newly copied directories (defaults to 0777);
  128. * newFileMode - the permission to be set for newly copied files (defaults to the current environment setting).
  129. */
  130. protected static function copyDirectoryRecursive($src,$dst,$base,$fileTypes,$exclude,$level,$options)
  131. {
  132. if(!is_dir($dst))
  133. self::mkdir($dst,$options,false);
  134. $folder=opendir($src);
  135. while(($file=readdir($folder))!==false)
  136. {
  137. if($file==='.' || $file==='..')
  138. continue;
  139. $path=$src.DIRECTORY_SEPARATOR.$file;
  140. $isFile=is_file($path);
  141. if(self::validatePath($base,$file,$isFile,$fileTypes,$exclude))
  142. {
  143. if($isFile)
  144. {
  145. copy($path,$dst.DIRECTORY_SEPARATOR.$file);
  146. if(isset($options['newFileMode']))
  147. @chmod($dst.DIRECTORY_SEPARATOR.$file,$options['newFileMode']);
  148. }
  149. elseif($level)
  150. self::copyDirectoryRecursive($path,$dst.DIRECTORY_SEPARATOR.$file,$base.'/'.$file,$fileTypes,$exclude,$level-1,$options);
  151. }
  152. }
  153. closedir($folder);
  154. }
  155. /**
  156. * Returns the files found under the specified directory and subdirectories.
  157. * This method is mainly used by {@link findFiles}.
  158. * @param string $dir the source directory
  159. * @param string $base the path relative to the original source directory
  160. * @param array $fileTypes list of file name suffix (without dot). Only files with these suffixes will be returned.
  161. * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path.
  162. * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of
  163. * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude
  164. * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant.
  165. * @param integer $level recursion depth. It defaults to -1.
  166. * Level -1 means searching for all directories and files under the directory;
  167. * Level 0 means searching for only the files DIRECTLY under the directory;
  168. * level N means searching for those directories that are within N levels.
  169. * @return array files found under the directory.
  170. */
  171. protected static function findFilesRecursive($dir,$base,$fileTypes,$exclude,$level)
  172. {
  173. $list=array();
  174. $handle=opendir($dir);
  175. while(($file=readdir($handle))!==false)
  176. {
  177. if($file==='.' || $file==='..')
  178. continue;
  179. $path=$dir.DIRECTORY_SEPARATOR.$file;
  180. $isFile=is_file($path);
  181. if(self::validatePath($base,$file,$isFile,$fileTypes,$exclude))
  182. {
  183. if($isFile)
  184. $list[]=$path;
  185. elseif($level)
  186. $list=array_merge($list,self::findFilesRecursive($path,$base.'/'.$file,$fileTypes,$exclude,$level-1));
  187. }
  188. }
  189. closedir($handle);
  190. return $list;
  191. }
  192. /**
  193. * Validates a file or directory.
  194. * @param string $base the path relative to the original source directory
  195. * @param string $file the file or directory name
  196. * @param boolean $isFile whether this is a file
  197. * @param array $fileTypes list of valid file name suffixes (without dot).
  198. * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path.
  199. * If a file or directory name or path matches the exclusion, false will be returned. For example, an exclusion of
  200. * '.svn' will return false for all files and directories whose name is '.svn'. And an exclusion of '/a/b' will return false for
  201. * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant.
  202. * @return boolean whether the file or directory is valid
  203. */
  204. protected static function validatePath($base,$file,$isFile,$fileTypes,$exclude)
  205. {
  206. foreach($exclude as $e)
  207. {
  208. if($file===$e || strpos($base.'/'.$file,$e)===0)
  209. return false;
  210. }
  211. if(!$isFile || empty($fileTypes))
  212. return true;
  213. if(($type=pathinfo($file,PATHINFO_EXTENSION))!=='')
  214. return in_array($type,$fileTypes);
  215. else
  216. return false;
  217. }
  218. /**
  219. * Determines the MIME type of the specified file.
  220. * This method will attempt the following approaches in order:
  221. * <ol>
  222. * <li>finfo</li>
  223. * <li>mime_content_type</li>
  224. * <li>{@link getMimeTypeByExtension}, when $checkExtension is set true.</li>
  225. * </ol>
  226. * @param string $file the file name.
  227. * @param string $magicFile name of a magic database file, usually something like /path/to/magic.mime.
  228. * This will be passed as the second parameter to {@link http://php.net/manual/en/function.finfo-open.php finfo_open}.
  229. * Magic file format described in {@link http://linux.die.net/man/5/magic man 5 magic}, note that this file does not
  230. * contain a standard PHP array as you might suppose. Specified magic file will be used only when fileinfo
  231. * PHP extension is available. This parameter has been available since version 1.1.3.
  232. * @param boolean $checkExtension whether to check the file extension in case the MIME type cannot be determined
  233. * based on finfo and mime_content_type. Defaults to true. This parameter has been available since version 1.1.4.
  234. * @return string the MIME type. Null is returned if the MIME type cannot be determined.
  235. */
  236. public static function getMimeType($file,$magicFile=null,$checkExtension=true)
  237. {
  238. if(function_exists('finfo_open'))
  239. {
  240. $options=defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME;
  241. $info=$magicFile===null ? finfo_open($options) : finfo_open($options,$magicFile);
  242. if($info && ($result=finfo_file($info,$file))!==false)
  243. return $result;
  244. }
  245. if(function_exists('mime_content_type') && ($result=mime_content_type($file))!==false)
  246. return $result;
  247. return $checkExtension ? self::getMimeTypeByExtension($file) : null;
  248. }
  249. /**
  250. * Determines the MIME type based on the extension name of the specified file.
  251. * This method will use a local map between extension name and MIME type.
  252. * @param string $file the file name.
  253. * @param string $magicFile the path of the file that contains all available MIME type information.
  254. * If this is not set, the default 'system.utils.mimeTypes' file will be used.
  255. * This parameter has been available since version 1.1.3.
  256. * @return string the MIME type. Null is returned if the MIME type cannot be determined.
  257. */
  258. public static function getMimeTypeByExtension($file,$magicFile=null)
  259. {
  260. static $extensions,$customExtensions=array();
  261. if($magicFile===null && $extensions===null)
  262. $extensions=require(Yii::getPathOfAlias('system.utils.mimeTypes').'.php');
  263. elseif($magicFile!==null && !isset($customExtensions[$magicFile]))
  264. $customExtensions[$magicFile]=require($magicFile);
  265. if(($ext=pathinfo($file,PATHINFO_EXTENSION))!=='')
  266. {
  267. $ext=strtolower($ext);
  268. if($magicFile===null && isset($extensions[$ext]))
  269. return $extensions[$ext];
  270. elseif($magicFile!==null && isset($customExtensions[$magicFile][$ext]))
  271. return $customExtensions[$magicFile][$ext];
  272. }
  273. return null;
  274. }
  275. /**
  276. * Shared environment safe version of mkdir. Supports recursive creation.
  277. * For avoidance of umask side-effects chmod is used.
  278. *
  279. * @param string $dst path to be created
  280. * @param array $options newDirMode element used, must contain access bitmask
  281. * @param boolean $recursive whether to create directory structure recursive if parent dirs do not exist
  282. * @return boolean result of mkdir
  283. * @see mkdir
  284. */
  285. private static function mkdir($dst,array $options,$recursive)
  286. {
  287. $prevDir=dirname($dst);
  288. if($recursive && !is_dir($dst) && !is_dir($prevDir))
  289. self::mkdir(dirname($dst),$options,true);
  290. $mode=isset($options['newDirMode']) ? $options['newDirMode'] : 0777;
  291. $res=mkdir($dst, $mode);
  292. @chmod($dst,$mode);
  293. return $res;
  294. }
  295. }