/src/Symfony/Component/HttpKernel/Util/Filesystem.php

https://github.com/outlawscumbag/symfony · PHP · 248 lines · 132 code · 31 blank · 85 comment · 46 complexity · c81e56cfb4541d3f45f70c2a5b157cc4 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpKernel\Util;
  11. /**
  12. * Provides basic utility to manipulate the file system.
  13. *
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. */
  16. class Filesystem
  17. {
  18. /**
  19. * Copies a file.
  20. *
  21. * This method only copies the file if the origin file is newer than the target file.
  22. *
  23. * By default, if the target already exists, it is not overridden.
  24. *
  25. * @param string $originFile The original filename
  26. * @param string $targetFile The target filename
  27. * @param array $override Whether to override an existing file or not
  28. */
  29. public function copy($originFile, $targetFile, $override = false)
  30. {
  31. $this->mkdir(dirname($targetFile));
  32. if (!$override && is_file($targetFile)) {
  33. $doCopy = filemtime($originFile) > filemtime($targetFile);
  34. } else {
  35. $doCopy = true;
  36. }
  37. if ($doCopy) {
  38. copy($originFile, $targetFile);
  39. }
  40. }
  41. /**
  42. * Creates a directory recursively.
  43. *
  44. * @param string|array|\Traversable $dirs The directory path
  45. * @param int $mode The directory mode
  46. *
  47. * @return Boolean true if the directory has been created, false otherwise
  48. */
  49. public function mkdir($dirs, $mode = 0777)
  50. {
  51. $ret = true;
  52. foreach ($this->toIterator($dirs) as $dir) {
  53. if (is_dir($dir)) {
  54. continue;
  55. }
  56. $ret = @mkdir($dir, $mode, true) && $ret;
  57. }
  58. return $ret;
  59. }
  60. /**
  61. * Creates empty files.
  62. *
  63. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
  64. */
  65. public function touch($files)
  66. {
  67. foreach ($this->toIterator($files) as $file) {
  68. touch($file);
  69. }
  70. }
  71. /**
  72. * Removes files or directories.
  73. *
  74. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
  75. */
  76. public function remove($files)
  77. {
  78. $files = iterator_to_array($this->toIterator($files));
  79. $files = array_reverse($files);
  80. foreach ($files as $file) {
  81. if (!file_exists($file)) {
  82. continue;
  83. }
  84. if (is_dir($file) && !is_link($file)) {
  85. $this->remove(new \FilesystemIterator($file));
  86. rmdir($file);
  87. } else {
  88. unlink($file);
  89. }
  90. }
  91. }
  92. /**
  93. * Change mode for an array of files or directories.
  94. *
  95. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
  96. * @param integer $mode The new mode
  97. * @param integer $umask The mode mask (octal)
  98. */
  99. public function chmod($files, $mode, $umask = 0000)
  100. {
  101. $currentUmask = umask();
  102. umask($umask);
  103. foreach ($this->toIterator($files) as $file) {
  104. chmod($file, $mode);
  105. }
  106. umask($currentUmask);
  107. }
  108. /**
  109. * Renames a file.
  110. *
  111. * @param string $origin The origin filename
  112. * @param string $target The new filename
  113. *
  114. * @throws \RuntimeException When target file already exists
  115. */
  116. public function rename($origin, $target)
  117. {
  118. // we check that target does not exist
  119. if (is_readable($target)) {
  120. throw new \RuntimeException(sprintf('Cannot rename because the target "%" already exist.', $target));
  121. }
  122. rename($origin, $target);
  123. }
  124. /**
  125. * Creates a symbolic link or copy a directory.
  126. *
  127. * @param string $originDir The origin directory path
  128. * @param string $targetDir The symbolic link name
  129. * @param Boolean $copyOnWindows Whether to copy files if on Windows
  130. */
  131. public function symlink($originDir, $targetDir, $copyOnWindows = false)
  132. {
  133. if (!function_exists('symlink') && $copyOnWindows) {
  134. $this->mirror($originDir, $targetDir);
  135. return;
  136. }
  137. $ok = false;
  138. if (is_link($targetDir)) {
  139. if (readlink($targetDir) != $originDir) {
  140. unlink($targetDir);
  141. } else {
  142. $ok = true;
  143. }
  144. }
  145. if (!$ok) {
  146. symlink($originDir, $targetDir);
  147. }
  148. }
  149. /**
  150. * Mirrors a directory to another.
  151. *
  152. * @param string $originDir The origin directory
  153. * @param string $targetDir The target directory
  154. * @param \Traversable $iterator A Traversable instance
  155. * @param array $options An array of boolean options
  156. * Valid options are:
  157. * - $options['override'] Whether to override an existing file on copy or not (see copy())
  158. * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
  159. *
  160. * @throws \RuntimeException When file type is unknown
  161. */
  162. public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
  163. {
  164. $copyOnWindows = false;
  165. if (isset($options['copy_on_windows']) && !function_exists('symlink')) {
  166. $copyOnWindows = $options['copy_on_windows'];
  167. }
  168. if (null === $iterator) {
  169. $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
  170. $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
  171. }
  172. if ('/' === substr($targetDir, -1) || '\\' === substr($targetDir, -1)) {
  173. $targetDir = substr($targetDir, 0, -1);
  174. }
  175. if ('/' === substr($originDir, -1) || '\\' === substr($originDir, -1)) {
  176. $originDir = substr($originDir, 0, -1);
  177. }
  178. foreach ($iterator as $file) {
  179. $target = $targetDir.'/'.str_replace($originDir.DIRECTORY_SEPARATOR, '', $file->getPathname());
  180. if (is_dir($file)) {
  181. $this->mkdir($target);
  182. } else if (is_file($file) || ($copyOnWindows && is_link($file))) {
  183. $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
  184. } else if (is_link($file)) {
  185. $this->symlink($file, $target);
  186. } else {
  187. throw new \RuntimeException(sprintf('Unable to guess "%s" file type.', $file));
  188. }
  189. }
  190. }
  191. /**
  192. * Returns whether the file path is an absolute path.
  193. *
  194. * @param string $file A file path
  195. *
  196. * @return Boolean
  197. */
  198. public function isAbsolutePath($file)
  199. {
  200. if ($file[0] == '/' || $file[0] == '\\'
  201. || (strlen($file) > 3 && ctype_alpha($file[0])
  202. && $file[1] == ':'
  203. && ($file[2] == '\\' || $file[2] == '/')
  204. )
  205. ) {
  206. return true;
  207. }
  208. return false;
  209. }
  210. private function toIterator($files)
  211. {
  212. if (!$files instanceof \Traversable) {
  213. $files = new \ArrayObject(is_array($files) ? $files : array($files));
  214. }
  215. return $files;
  216. }
  217. }