Filesystem.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <?php
  2. namespace Illuminate\Filesystem;
  3. use ErrorException;
  4. use FilesystemIterator;
  5. use Symfony\Component\Finder\Finder;
  6. use Illuminate\Support\Traits\Macroable;
  7. use Illuminate\Contracts\Filesystem\FileNotFoundException;
  8. class Filesystem
  9. {
  10. use Macroable;
  11. /**
  12. * Determine if a file or directory exists.
  13. *
  14. * @param string $path
  15. * @return bool
  16. */
  17. public function exists($path)
  18. {
  19. return file_exists($path);
  20. }
  21. /**
  22. * Get the contents of a file.
  23. *
  24. * @param string $path
  25. * @param bool $lock
  26. * @return string
  27. *
  28. * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
  29. */
  30. public function get($path, $lock = false)
  31. {
  32. if ($this->isFile($path)) {
  33. return $lock ? $this->sharedGet($path) : file_get_contents($path);
  34. }
  35. throw new FileNotFoundException("File does not exist at path {$path}");
  36. }
  37. /**
  38. * Get contents of a file with shared access.
  39. *
  40. * @param string $path
  41. * @return string
  42. */
  43. public function sharedGet($path)
  44. {
  45. $contents = '';
  46. $handle = fopen($path, 'rb');
  47. if ($handle) {
  48. try {
  49. if (flock($handle, LOCK_SH)) {
  50. clearstatcache(true, $path);
  51. $contents = fread($handle, $this->size($path) ?: 1);
  52. flock($handle, LOCK_UN);
  53. }
  54. } finally {
  55. fclose($handle);
  56. }
  57. }
  58. return $contents;
  59. }
  60. /**
  61. * Get the returned value of a file.
  62. *
  63. * @param string $path
  64. * @return mixed
  65. *
  66. * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
  67. */
  68. public function getRequire($path)
  69. {
  70. if ($this->isFile($path)) {
  71. return require $path;
  72. }
  73. throw new FileNotFoundException("File does not exist at path {$path}");
  74. }
  75. /**
  76. * Require the given file once.
  77. *
  78. * @param string $file
  79. * @return mixed
  80. */
  81. public function requireOnce($file)
  82. {
  83. require_once $file;
  84. }
  85. /**
  86. * Get the MD5 hash of the file at the given path.
  87. *
  88. * @param string $path
  89. * @return string
  90. */
  91. public function hash($path)
  92. {
  93. return md5_file($path);
  94. }
  95. /**
  96. * Write the contents of a file.
  97. *
  98. * @param string $path
  99. * @param string $contents
  100. * @param bool $lock
  101. * @return int
  102. */
  103. public function put($path, $contents, $lock = false)
  104. {
  105. return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
  106. }
  107. /**
  108. * Prepend to a file.
  109. *
  110. * @param string $path
  111. * @param string $data
  112. * @return int
  113. */
  114. public function prepend($path, $data)
  115. {
  116. if ($this->exists($path)) {
  117. return $this->put($path, $data.$this->get($path));
  118. }
  119. return $this->put($path, $data);
  120. }
  121. /**
  122. * Append to a file.
  123. *
  124. * @param string $path
  125. * @param string $data
  126. * @return int
  127. */
  128. public function append($path, $data)
  129. {
  130. return file_put_contents($path, $data, FILE_APPEND);
  131. }
  132. /**
  133. * Get or set UNIX mode of a file or directory.
  134. *
  135. * @param string $path
  136. * @param int $mode
  137. * @return mixed
  138. */
  139. public function chmod($path, $mode = null)
  140. {
  141. if ($mode) {
  142. return chmod($path, $mode);
  143. }
  144. return substr(sprintf('%o', fileperms($path)), -4);
  145. }
  146. /**
  147. * Delete the file at a given path.
  148. *
  149. * @param string|array $paths
  150. * @return bool
  151. */
  152. public function delete($paths)
  153. {
  154. $paths = is_array($paths) ? $paths : func_get_args();
  155. $success = true;
  156. foreach ($paths as $path) {
  157. try {
  158. if (! @unlink($path)) {
  159. $success = false;
  160. }
  161. } catch (ErrorException $e) {
  162. $success = false;
  163. }
  164. }
  165. return $success;
  166. }
  167. /**
  168. * Move a file to a new location.
  169. *
  170. * @param string $path
  171. * @param string $target
  172. * @return bool
  173. */
  174. public function move($path, $target)
  175. {
  176. return rename($path, $target);
  177. }
  178. /**
  179. * Copy a file to a new location.
  180. *
  181. * @param string $path
  182. * @param string $target
  183. * @return bool
  184. */
  185. public function copy($path, $target)
  186. {
  187. return copy($path, $target);
  188. }
  189. /**
  190. * Create a hard link to the target file or directory.
  191. *
  192. * @param string $target
  193. * @param string $link
  194. * @return void
  195. */
  196. public function link($target, $link)
  197. {
  198. if (! windows_os()) {
  199. return symlink($target, $link);
  200. }
  201. $mode = $this->isDirectory($target) ? 'J' : 'H';
  202. exec("mklink /{$mode} \"{$link}\" \"{$target}\"");
  203. }
  204. /**
  205. * Extract the file name from a file path.
  206. *
  207. * @param string $path
  208. * @return string
  209. */
  210. public function name($path)
  211. {
  212. return pathinfo($path, PATHINFO_FILENAME);
  213. }
  214. /**
  215. * Extract the trailing name component from a file path.
  216. *
  217. * @param string $path
  218. * @return string
  219. */
  220. public function basename($path)
  221. {
  222. return pathinfo($path, PATHINFO_BASENAME);
  223. }
  224. /**
  225. * Extract the parent directory from a file path.
  226. *
  227. * @param string $path
  228. * @return string
  229. */
  230. public function dirname($path)
  231. {
  232. return pathinfo($path, PATHINFO_DIRNAME);
  233. }
  234. /**
  235. * Extract the file extension from a file path.
  236. *
  237. * @param string $path
  238. * @return string
  239. */
  240. public function extension($path)
  241. {
  242. return pathinfo($path, PATHINFO_EXTENSION);
  243. }
  244. /**
  245. * Get the file type of a given file.
  246. *
  247. * @param string $path
  248. * @return string
  249. */
  250. public function type($path)
  251. {
  252. return filetype($path);
  253. }
  254. /**
  255. * Get the mime-type of a given file.
  256. *
  257. * @param string $path
  258. * @return string|false
  259. */
  260. public function mimeType($path)
  261. {
  262. return finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
  263. }
  264. /**
  265. * Get the file size of a given file.
  266. *
  267. * @param string $path
  268. * @return int
  269. */
  270. public function size($path)
  271. {
  272. return filesize($path);
  273. }
  274. /**
  275. * Get the file's last modification time.
  276. *
  277. * @param string $path
  278. * @return int
  279. */
  280. public function lastModified($path)
  281. {
  282. return filemtime($path);
  283. }
  284. /**
  285. * Determine if the given path is a directory.
  286. *
  287. * @param string $directory
  288. * @return bool
  289. */
  290. public function isDirectory($directory)
  291. {
  292. return is_dir($directory);
  293. }
  294. /**
  295. * Determine if the given path is readable.
  296. *
  297. * @param string $path
  298. * @return bool
  299. */
  300. public function isReadable($path)
  301. {
  302. return is_readable($path);
  303. }
  304. /**
  305. * Determine if the given path is writable.
  306. *
  307. * @param string $path
  308. * @return bool
  309. */
  310. public function isWritable($path)
  311. {
  312. return is_writable($path);
  313. }
  314. /**
  315. * Determine if the given path is a file.
  316. *
  317. * @param string $file
  318. * @return bool
  319. */
  320. public function isFile($file)
  321. {
  322. return is_file($file);
  323. }
  324. /**
  325. * Find path names matching a given pattern.
  326. *
  327. * @param string $pattern
  328. * @param int $flags
  329. * @return array
  330. */
  331. public function glob($pattern, $flags = 0)
  332. {
  333. return glob($pattern, $flags);
  334. }
  335. /**
  336. * Get an array of all files in a directory.
  337. *
  338. * @param string $directory
  339. * @param bool $hidden
  340. * @return \Symfony\Component\Finder\SplFileInfo[]
  341. */
  342. public function files($directory, $hidden = false)
  343. {
  344. return iterator_to_array(
  345. Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->depth(0)->sortByName(),
  346. false
  347. );
  348. }
  349. /**
  350. * Get all of the files from the given directory (recursive).
  351. *
  352. * @param string $directory
  353. * @param bool $hidden
  354. * @return \Symfony\Component\Finder\SplFileInfo[]
  355. */
  356. public function allFiles($directory, $hidden = false)
  357. {
  358. return iterator_to_array(
  359. Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->sortByName(),
  360. false
  361. );
  362. }
  363. /**
  364. * Get all of the directories within a given directory.
  365. *
  366. * @param string $directory
  367. * @return array
  368. */
  369. public function directories($directory)
  370. {
  371. $directories = [];
  372. foreach (Finder::create()->in($directory)->directories()->depth(0)->sortByName() as $dir) {
  373. $directories[] = $dir->getPathname();
  374. }
  375. return $directories;
  376. }
  377. /**
  378. * Create a directory.
  379. *
  380. * @param string $path
  381. * @param int $mode
  382. * @param bool $recursive
  383. * @param bool $force
  384. * @return bool
  385. */
  386. public function makeDirectory($path, $mode = 0755, $recursive = false, $force = false)
  387. {
  388. if ($force) {
  389. return @mkdir($path, $mode, $recursive);
  390. }
  391. return mkdir($path, $mode, $recursive);
  392. }
  393. /**
  394. * Move a directory.
  395. *
  396. * @param string $from
  397. * @param string $to
  398. * @param bool $overwrite
  399. * @return bool
  400. */
  401. public function moveDirectory($from, $to, $overwrite = false)
  402. {
  403. if ($overwrite && $this->isDirectory($to)) {
  404. if (! $this->deleteDirectory($to)) {
  405. return false;
  406. }
  407. }
  408. return @rename($from, $to) === true;
  409. }
  410. /**
  411. * Copy a directory from one location to another.
  412. *
  413. * @param string $directory
  414. * @param string $destination
  415. * @param int $options
  416. * @return bool
  417. */
  418. public function copyDirectory($directory, $destination, $options = null)
  419. {
  420. if (! $this->isDirectory($directory)) {
  421. return false;
  422. }
  423. $options = $options ?: FilesystemIterator::SKIP_DOTS;
  424. // If the destination directory does not actually exist, we will go ahead and
  425. // create it recursively, which just gets the destination prepared to copy
  426. // the files over. Once we make the directory we'll proceed the copying.
  427. if (! $this->isDirectory($destination)) {
  428. $this->makeDirectory($destination, 0777, true);
  429. }
  430. $items = new FilesystemIterator($directory, $options);
  431. foreach ($items as $item) {
  432. // As we spin through items, we will check to see if the current file is actually
  433. // a directory or a file. When it is actually a directory we will need to call
  434. // back into this function recursively to keep copying these nested folders.
  435. $target = $destination.'/'.$item->getBasename();
  436. if ($item->isDir()) {
  437. $path = $item->getPathname();
  438. if (! $this->copyDirectory($path, $target, $options)) {
  439. return false;
  440. }
  441. }
  442. // If the current items is just a regular file, we will just copy this to the new
  443. // location and keep looping. If for some reason the copy fails we'll bail out
  444. // and return false, so the developer is aware that the copy process failed.
  445. else {
  446. if (! $this->copy($item->getPathname(), $target)) {
  447. return false;
  448. }
  449. }
  450. }
  451. return true;
  452. }
  453. /**
  454. * Recursively delete a directory.
  455. *
  456. * The directory itself may be optionally preserved.
  457. *
  458. * @param string $directory
  459. * @param bool $preserve
  460. * @return bool
  461. */
  462. public function deleteDirectory($directory, $preserve = false)
  463. {
  464. if (! $this->isDirectory($directory)) {
  465. return false;
  466. }
  467. $items = new FilesystemIterator($directory);
  468. foreach ($items as $item) {
  469. // If the item is a directory, we can just recurse into the function and
  470. // delete that sub-directory otherwise we'll just delete the file and
  471. // keep iterating through each file until the directory is cleaned.
  472. if ($item->isDir() && ! $item->isLink()) {
  473. $this->deleteDirectory($item->getPathname());
  474. }
  475. // If the item is just a file, we can go ahead and delete it since we're
  476. // just looping through and waxing all of the files in this directory
  477. // and calling directories recursively, so we delete the real path.
  478. else {
  479. $this->delete($item->getPathname());
  480. }
  481. }
  482. if (! $preserve) {
  483. @rmdir($directory);
  484. }
  485. return true;
  486. }
  487. /**
  488. * Empty the specified directory of all files and folders.
  489. *
  490. * @param string $directory
  491. * @return bool
  492. */
  493. public function cleanDirectory($directory)
  494. {
  495. return $this->deleteDirectory($directory, true);
  496. }
  497. }