PageRenderTime 24ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/server/vpn.51sync.com/library/helper/filesys.php

https://github.com/qibinghua/vpn.51sync
PHP | 351 lines | 227 code | 12 blank | 112 comment | 23 complexity | c2b30413444049914184f04e11a7bbc6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. // $Id: filesys.php 2212 2009-02-06 00:51:32Z dualface $
  3. /**
  4. * 定义 Helper_Filesys 类
  5. *
  6. * @link http://qeephp.com/
  7. * @copyright Copyright (c) 2006-2009 Qeeyuan Inc. {@link http://www.qeeyuan.com}
  8. * @license New BSD License {@link http://qeephp.com/license/}
  9. * @version $Id: filesys.php 2212 2009-02-06 00:51:32Z dualface $
  10. * @package helper
  11. */
  12. /**
  13. * Helper_Filesys 类提供了一组简化文件系统操作的方法
  14. *
  15. * 部分方法来自 Yii Framework 框架的 CFileHelper 类,并作了修改。
  16. *
  17. * @author YuLei Liao <liaoyulei@qeeyuan.com>
  18. * @version $Id: filesys.php 2212 2009-02-06 00:51:32Z dualface $
  19. * @package helper
  20. */
  21. abstract class Helper_Filesys
  22. {
  23. /**
  24. * 遍历指定目录及子目录下的文件,返回所有与匹配模式符合的文件名
  25. *
  26. * @param string $dir
  27. * @param string $pattern
  28. *
  29. * @return array
  30. */
  31. static function recursionGlob($dir, $pattern)
  32. {
  33. $dir = rtrim($dir, '/\\') . DS;
  34. $files = array();
  35. // 遍历目录,删除所有文件和子目录
  36. $dh = opendir($dir);
  37. if (!$dh) return $files;
  38. $items = (array)glob($dir . $pattern);
  39. foreach ($items as $item)
  40. {
  41. if (is_file($item)) $files[] = $item;
  42. }
  43. while (($file = readdir($dh)))
  44. {
  45. if ($file == '.' || $file == '..') continue;
  46. $path = $dir . $file;
  47. if (is_dir($path))
  48. {
  49. $files = array_merge($files, self::recursionGlob($path, $pattern));
  50. }
  51. }
  52. closedir($dh);
  53. return $files;
  54. }
  55. /**
  56. * 创建一个目录树,失败抛出异常
  57. *
  58. * 用法:
  59. * @code php
  60. * Helper_Filesys::mkdirs('/top/second/3rd');
  61. * @endcode
  62. *
  63. * @param string $dir 要创建的目录
  64. * @param int $mode 新建目录的权限
  65. *
  66. * @throw Q_CreateDirFailedException
  67. */
  68. static function mkdirs($dir, $mode = 0777)
  69. {
  70. if (!is_dir($dir))
  71. {
  72. $oldumask = umask(0);
  73. $ret = @mkdir($dir, $mode, true);
  74. umask($oldumask);
  75. if (!$ret)
  76. {
  77. throw new Q_CreateDirFailedException($dir);
  78. }
  79. }
  80. return true;
  81. }
  82. /**
  83. * 删除指定目录及其下的所有文件和子目录,失败抛出异常
  84. *
  85. * 用法:
  86. * @code php
  87. * // 删除 my_dir 目录及其下的所有文件和子目录
  88. * Helper_Filesys::rmdirs('/path/to/my_dir');
  89. * @endcode
  90. *
  91. * 注意:使用该函数要非常非常小心,避免意外删除重要文件。
  92. *
  93. * @param string $dir 要删除的目录
  94. *
  95. * @throw Q_RemoveDirFailedException
  96. */
  97. static function rmdirs($dir)
  98. {
  99. $dir = realpath($dir);
  100. if ($dir == '' || $dir == '/' || (strlen($dir) == 3 && substr($dir, 1) == ':\\'))
  101. {
  102. // 禁止删除根目录
  103. throw new Q_RemoveDirFailedException($dir);
  104. }
  105. // 遍历目录,删除所有文件和子目录
  106. if(false !== ($dh = opendir($dir)))
  107. {
  108. while(false !== ($file = readdir($dh)))
  109. {
  110. if($file == '.' || $file == '..')
  111. {
  112. continue;
  113. }
  114. $path = $dir . DIRECTORY_SEPARATOR . $file;
  115. if (is_dir($path))
  116. {
  117. self::rmdirs($path);
  118. }
  119. else
  120. {
  121. unlink($path);
  122. }
  123. }
  124. closedir($dh);
  125. if (@rmdir($dir) == false)
  126. {
  127. throw new Q_RemoveDirFailedException($dir);
  128. }
  129. }
  130. else
  131. {
  132. throw new Q_RemoveDirFailedException($dir);
  133. }
  134. }
  135. /**
  136. * 复制一个目录及其子目录的文件到目的地
  137. *
  138. * 用法:
  139. * @code
  140. * Helper_Filesys::copyDir($src, $dst);
  141. * @endcode
  142. *
  143. * 如果目的地目录不存在,则会创建需要的目录。
  144. *
  145. * 可以通过 $options 参数控制要复制的文件,以及复制的深度。
  146. *
  147. * $options 参数可用的选项有:
  148. *
  149. * - extnames: 只有指定扩展名的文件被复制
  150. * 如果不指定该参数,则查找所有扩展名的文件。
  151. *
  152. * - excludes: 排除指定的目录或文件
  153. * "upload/avatars" 表示排除 "$dir/upload/avatars" 目录。
  154. *
  155. * - levels: 整数,指定查找的目录深度,默认为 -1。
  156. * 如果为 0 表示不查找子目录。
  157. *
  158. * 注意:copyDirs() 总是会排除所有以“.”开头的目录和文件。
  159. */
  160. static function copyDir($src, $dst, $options=array())
  161. {
  162. $extnames = !empty($options['extnames'])
  163. ? Q::normalize($options['extnames'])
  164. : array();
  165. foreach ($extnames as $offset => $extname)
  166. {
  167. if ($extname[0] == '.')
  168. {
  169. $extnames[$offset] = substr($extname, 1);
  170. }
  171. }
  172. $excludes = !empty($options['excludes'])
  173. ? Q::normalize($options['excludes'])
  174. : array();
  175. $level = isset($options['level'])
  176. ? intval($options['level'])
  177. : -1;
  178. self::_copyDirectoryRecursive($src, $dst, '', $extnames, $excludes, $level);
  179. }
  180. /**
  181. * 在指定目录及其子目录中查找文件
  182. *
  183. * 用法:
  184. * @code php
  185. * $files = Helper_FileSys::findFiles($dir, array(
  186. * // 只查找扩展名为 .jpg, .jpeg, .png 和 .gif 的文件
  187. * 'extnames' => 'jpg, jpeg, png, gif',
  188. * // 排除 .svn 目录和 upload/avatars 目录
  189. * 'excludes' => '.svn, upload/avatars',
  190. * // 只查找 2 层子目录
  191. * 'level' => 2,
  192. * ));
  193. * @endcode
  194. *
  195. * findFiles() 的 $options 参数支持下列选项:
  196. *
  197. * - extnames: 字符串或数组,指定查找文件时有效的文件扩展名。
  198. * 如果不指定该参数,则查找所有扩展名的文件。
  199. *
  200. * - excludes: 字符串或数组,指定查找文件时要排除的目录或文件。
  201. * "upload/avatars" 表示排除 "$dir/upload/avatars" 目录。
  202. *
  203. * - levels: 整数,指定查找的目录深度,默认为 -1。
  204. * 如果为 0 表示不查找子目录。
  205. *
  206. * findFiles() 返回一个数组,包含了排序后的文件完整路径。
  207. *
  208. * @param string|array $dir 要查找文件的目录
  209. * @param array $options 查找选项
  210. *
  211. * @return array 包含有效文件名的数组
  212. */
  213. static function findFiles($dir, $options=array())
  214. {
  215. $extnames = !empty($options['extnames'])
  216. ? Q::normalize($options['extnames'])
  217. : array();
  218. foreach ($extnames as $offset => $extname)
  219. {
  220. if ($extname[0] == '.')
  221. {
  222. $extnames[$offset] = substr($extname, 1);
  223. }
  224. }
  225. $excludes = !empty($options['excludes'])
  226. ? Q::normalize($options['excludes'])
  227. : array();
  228. $level = isset($options['level'])
  229. ? intval($options['level'])
  230. : -1;
  231. $list = self::_findFilesRecursive($dir, '', $extnames, $excludes, $level);
  232. sort($list);
  233. return $list;
  234. }
  235. /**
  236. * 内部使用
  237. */
  238. private static function _copyDirectoryRecursive($src, $dst, $base, $extnames, $excludes, $level)
  239. {
  240. @mkdir($dst);
  241. @chmod($dst,0777);
  242. $folder = opendir($src);
  243. while (($file = readdir($folder)))
  244. {
  245. if ($file{0} == '.') continue;
  246. $path = $src . DIRECTORY_SEPARATOR . $file;
  247. $is_file = is_file($path);
  248. if(self::_validatePath($base, $file, $is_file, $extnames, $excludes))
  249. {
  250. if($is_file)
  251. {
  252. copy($path, $dst . DIRECTORY_SEPARATOR . $file);
  253. }
  254. elseif($level)
  255. {
  256. self::_copyDirectoryRecursive($path, $dst . DIRECTORY_SEPARATOR . $file,
  257. $base . '/' . $file, $extnames, $excludes, $level - 1);
  258. }
  259. }
  260. }
  261. closedir($folder);
  262. }
  263. /**
  264. * 递归查找文件,用于 {@link Helper_FileSys::findFiles()}
  265. *
  266. * @param string $dir 要查找的源目录
  267. * @param string $base 与源目录的相对路径
  268. * @param array $extnames 有效的扩展名
  269. * @param array $excludes 要排除的文件或目录
  270. * @param integer $level 要查找的目录深度
  271. *
  272. * @return 包含有效文件名的数组
  273. */
  274. private static function _findFilesRecursive($dir, $base, $extnames,
  275. $excludes, $level)
  276. {
  277. $list = array();
  278. $handle = opendir($dir);
  279. while(($file = readdir($handle)))
  280. {
  281. if($file == '.' || $file == '..') continue;
  282. $path = $dir . DIRECTORY_SEPARATOR . $file;
  283. $is_file = is_file($path);
  284. if (self::_validatePath($base, $file, $is_file, $extnames, $excludes))
  285. {
  286. if ($is_file)
  287. {
  288. $list[] = $path;
  289. }
  290. elseif ($level)
  291. {
  292. $list = array_merge($list, self::_findFilesRecursive($path,
  293. $base . '/' . $file, $extnames, $excludes, $level - 1));
  294. }
  295. }
  296. }
  297. closedir($handle);
  298. return $list;
  299. }
  300. /**
  301. * 验证文件或目录,返回验证结果
  302. *
  303. * @param string $base 与源目录相对路径
  304. * @param string $file 文件名或目录名
  305. * @param boolean $is_file 是否是文件
  306. * @param array $extnames 有效的文件扩展名
  307. * @param array $excludes 要排除的文件名或目录
  308. *
  309. * @return boolean 该文件是否通过验证
  310. */
  311. private static function _validatePath($base, $file, $is_file,
  312. array $extnames, array $excludes)
  313. {
  314. $test = ltrim(str_replace('\\', '/', "/{$base}/{$file}"), '/');
  315. foreach($excludes as $e)
  316. {
  317. if ($file == $e || $test == $e) return false;
  318. }
  319. if(!$is_file || empty($extnames)) return true;
  320. if(($pos = strrpos($file, '.')) !==false)
  321. {
  322. $type = substr($file, $pos + 1);
  323. return in_array($type, $extnames);
  324. }
  325. else
  326. {
  327. return false;
  328. }
  329. }
  330. }