fileManage.lib.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Symfony\Component\Filesystem\Filesystem;
  4. /**
  5. * This is the file manage library for Chamilo.
  6. * Include/require it in your code to use its functionality.
  7. *
  8. * @package chamilo.library
  9. */
  10. /**
  11. * Cheks a file or a directory actually exist at this location.
  12. *
  13. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  14. *
  15. * @param string $file_path Path of the presume existing file or dir
  16. *
  17. * @return bool TRUE if the file or the directory exists or FALSE otherwise
  18. */
  19. function check_name_exist($file_path)
  20. {
  21. clearstatcache();
  22. $save_dir = getcwd();
  23. if (!is_dir(dirname($file_path))) {
  24. return false;
  25. }
  26. chdir(dirname($file_path));
  27. $file_name = basename($file_path);
  28. if (file_exists($file_name)) {
  29. chdir($save_dir);
  30. return true;
  31. } else {
  32. chdir($save_dir);
  33. return false;
  34. }
  35. }
  36. /**
  37. * Deletes a file or a directory.
  38. *
  39. * @author - Hugues Peeters
  40. *
  41. * @param $file (String) - the path of file or directory to delete
  42. *
  43. * @return bool - true if the delete succeed, false otherwise
  44. *
  45. * @see - delete() uses check_name_exist() and removeDir() functions
  46. */
  47. function my_delete($file)
  48. {
  49. if (check_name_exist($file)) {
  50. if (is_file($file)) { // FILE CASE
  51. unlink($file);
  52. return true;
  53. } elseif (is_dir($file)) { // DIRECTORY CASE
  54. removeDir($file);
  55. return true;
  56. }
  57. }
  58. return false; // no file or directory to delete
  59. }
  60. /**
  61. * Removes a directory recursively.
  62. *
  63. * @returns true if OK, otherwise false
  64. *
  65. * @author Amary <MasterNES@aol.com> (from Nexen.net)
  66. * @author Olivier Brouckaert <oli.brouckaert@skynet.be>
  67. *
  68. * @param string $dir directory to remove
  69. */
  70. function removeDir($dir)
  71. {
  72. if (!@$opendir = opendir($dir)) {
  73. return false;
  74. }
  75. while ($readdir = readdir($opendir)) {
  76. if ($readdir != '..' && $readdir != '.') {
  77. if (is_file($dir.'/'.$readdir)) {
  78. if (!@unlink($dir.'/'.$readdir)) {
  79. return false;
  80. }
  81. } elseif (is_dir($dir.'/'.$readdir)) {
  82. if (!removeDir($dir.'/'.$readdir)) {
  83. return false;
  84. }
  85. }
  86. }
  87. }
  88. closedir($opendir);
  89. if (!@rmdir($dir)) {
  90. return false;
  91. }
  92. return true;
  93. }
  94. /**
  95. * Return true if folder is empty.
  96. *
  97. * @author hubert.borderiou@grenet.fr
  98. *
  99. * @param string $in_folder folder path on disk
  100. *
  101. * @return int 1 if folder is empty, 0 otherwise
  102. */
  103. function folder_is_empty($in_folder)
  104. {
  105. $folder_is_empty = 0;
  106. if (is_dir($in_folder)) {
  107. $tab_folder_content = scandir($in_folder);
  108. if ((
  109. count($tab_folder_content) == 2 &&
  110. in_array(".", $tab_folder_content) &&
  111. in_array("..", $tab_folder_content)
  112. ) ||
  113. (count($tab_folder_content) < 2)
  114. ) {
  115. $folder_is_empty = 1;
  116. }
  117. }
  118. return $folder_is_empty;
  119. }
  120. /**
  121. * Renames a file or a directory.
  122. *
  123. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  124. *
  125. * @param string $file_path complete path of the file or the directory
  126. * @param string $new_file_name new name for the file or the directory
  127. *
  128. * @return bool true if succeed, false otherwise
  129. *
  130. * @see rename() uses the check_name_exist() and php2phps() functions
  131. */
  132. function my_rename($file_path, $new_file_name)
  133. {
  134. $save_dir = getcwd();
  135. $path = dirname($file_path);
  136. $old_file_name = basename($file_path);
  137. $new_file_name = api_replace_dangerous_char($new_file_name);
  138. // If no extension, take the old one
  139. if ((strpos($new_file_name, '.') === false) && ($dotpos = strrpos($old_file_name, '.'))) {
  140. $new_file_name .= substr($old_file_name, $dotpos);
  141. }
  142. // Note: still possible: 'xx.yy' -rename-> '.yy' -rename-> 'zz'
  143. // This is useful for folder names, where otherwise '.' would be sticky
  144. // Extension PHP is not allowed, change to PHPS
  145. $new_file_name = php2phps($new_file_name);
  146. if ($new_file_name == $old_file_name) {
  147. return $old_file_name;
  148. }
  149. if (strtolower($new_file_name) != strtolower($old_file_name) && check_name_exist($path.'/'.$new_file_name)) {
  150. return false;
  151. }
  152. // On a Windows server, it would be better not to do the above check
  153. // because it succeeds for some new names resembling the old name.
  154. // But on Unix/Linux the check must be done because rename overwrites.
  155. chdir($path);
  156. $res = rename($old_file_name, $new_file_name) ? $new_file_name : false;
  157. chdir($save_dir);
  158. return $res;
  159. }
  160. /**
  161. * Moves a file or a directory to an other area.
  162. *
  163. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  164. *
  165. * @param string $source the path of file or directory to move
  166. * @param string $target the path of the new area
  167. * @param bool $forceMove Whether to force a move or to make a copy (safer but slower) and then delete the original
  168. * @param bool $moveContent In some cases (including migrations), we need to move the *content* and not the folder itself
  169. *
  170. * @return bool true if the move succeed, false otherwise
  171. *
  172. * @see move() uses check_name_exist() and copyDirTo() functions
  173. */
  174. function move($source, $target, $forceMove = true, $moveContent = false)
  175. {
  176. $target = realpath($target); // remove trailing slash
  177. $source = realpath($source);
  178. if (check_name_exist($source)) {
  179. $file_name = basename($source);
  180. // move onto self illegal: mv a/b/c a/b/c or mv a/b/c a/b
  181. if (strcasecmp($target, dirname($source)) === 0) {
  182. return false;
  183. }
  184. $isWindowsOS = api_is_windows_os();
  185. $canExec = function_exists('exec');
  186. /* File case */
  187. if (is_file($source)) {
  188. if ($forceMove) {
  189. if (!$isWindowsOS && $canExec) {
  190. exec('mv '.$source.' '.$target.'/'.$file_name);
  191. } else {
  192. // Try copying
  193. copy($source, $target.'/'.$file_name);
  194. unlink($source);
  195. }
  196. } else {
  197. copy($source, $target.'/'.$file_name);
  198. unlink($source);
  199. }
  200. return true;
  201. } elseif (is_dir($source)) {
  202. // move dir down will cause loop: mv a/b/ a/b/c/ not legal
  203. if (strncasecmp($target, $source, strlen($source)) == 0) {
  204. return false;
  205. }
  206. /* Directory */
  207. if ($forceMove && !$isWindowsOS && $canExec) {
  208. if ($moveContent) {
  209. $base = basename($source);
  210. $out = [];
  211. $retVal = -1;
  212. exec('mv '.$source.'/* '.$target.'/'.$base, $out, $retVal);
  213. if ($retVal !== 0) {
  214. return false; // mv should return 0 on success
  215. }
  216. exec('rm -rf '.$source);
  217. } else {
  218. $out = [];
  219. $retVal = -1;
  220. exec("mv $source $target", $out, $retVal);
  221. if ($retVal !== 0) {
  222. error_log("Chamilo error fileManage.lib.php: mv $source $target\n");
  223. return false; // mv should return 0 on success
  224. }
  225. }
  226. } else {
  227. return copyDirTo($source, $target);
  228. }
  229. return true;
  230. }
  231. }
  232. return false;
  233. }
  234. /**
  235. * Moves a directory and its content to an other area.
  236. *
  237. * @author Hugues Peeters <peeters@ipm.ucl.ac.be>
  238. *
  239. * @param string $source the path of the directory to move
  240. * @param string $destination the path of the new area
  241. * @param bool $move Whether we want to remove the source at the end
  242. *
  243. * @return bool false on error
  244. */
  245. function copyDirTo($source, $destination, $move = true)
  246. {
  247. $fs = new Filesystem();
  248. if (is_dir($source)) {
  249. $fs->mkdir($destination);
  250. if (!is_dir($destination)) {
  251. error_log("Chamilo copyDirTo cannot mkdir $destination\n");
  252. return false; // could not create destination dir
  253. }
  254. $fs->mirror($source, $destination);
  255. if ($move) {
  256. $fs->remove($source);
  257. }
  258. }
  259. return true;
  260. }
  261. /**
  262. * Copy a directory and its directories (not files) to an other area.
  263. *
  264. * @param string $source the path of the directory to move
  265. * @param string $destination the path of the new area
  266. *
  267. * @return bool false on error
  268. */
  269. function copyDirWithoutFilesTo($source, $destination)
  270. {
  271. $fs = new Filesystem();
  272. if (!is_dir($source)) {
  273. return false;
  274. }
  275. if (!$fs->exists($destination)) {
  276. $fs->mkdir($destination);
  277. }
  278. $dirIterator = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
  279. $iterator = new RecursiveIteratorIterator($dirIterator, RecursiveIteratorIterator::SELF_FIRST);
  280. /** @var \SplFileInfo $item */
  281. foreach ($iterator as $item) {
  282. if ($item->isFile()) {
  283. continue;
  284. }
  285. $newDir = $destination.'/'.$item->getFilename();
  286. if (!$fs->exists($newDir)) {
  287. $fs->mkdir($destination.'/'.$item->getFilename());
  288. }
  289. }
  290. return true;
  291. }
  292. /**
  293. * Extracting extension of a filename.
  294. *
  295. * @returns array
  296. *
  297. * @param string $filename filename
  298. */
  299. function getextension($filename)
  300. {
  301. $bouts = explode('.', $filename);
  302. return [array_pop($bouts), implode('.', $bouts)];
  303. }
  304. /**
  305. * Get a list of all PHP (.php) files in a given directory. Includes .tpl files.
  306. *
  307. * @param string $base_path The base path in which to find the corresponding files
  308. * @param bool $includeStatic Include static .html, .htm and .css files
  309. *
  310. * @return array
  311. */
  312. function getAllPhpFiles($base_path, $includeStatic = false)
  313. {
  314. $list = scandir($base_path);
  315. $files = [];
  316. $extensionsArray = ['.php', '.tpl'];
  317. if ($includeStatic) {
  318. $extensionsArray[] = 'html';
  319. $extensionsArray[] = '.htm';
  320. $extensionsArray[] = '.css';
  321. }
  322. foreach ($list as $item) {
  323. if (substr($item, 0, 1) == '.') {
  324. continue;
  325. }
  326. $special_dirs = [api_get_path(SYS_TEST_PATH), api_get_path(SYS_COURSE_PATH), api_get_path(SYS_LANG_PATH), api_get_path(SYS_ARCHIVE_PATH)];
  327. if (in_array($base_path.$item.'/', $special_dirs)) {
  328. continue;
  329. }
  330. if (is_dir($base_path.$item)) {
  331. $files = array_merge($files, getAllPhpFiles($base_path.$item.'/', $includeStatic));
  332. } else {
  333. //only analyse php files
  334. $sub = substr($item, -4);
  335. if (in_array($sub, $extensionsArray)) {
  336. $files[] = $base_path.$item;
  337. }
  338. }
  339. }
  340. $list = null;
  341. return $files;
  342. }