PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/silex_private/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php

https://bitbucket.org/ortic/deploy
PHP | 407 lines | 221 code | 42 blank | 144 comment | 69 complexity | 03b5093b215659c3be54a8c43c8399e3 MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause, BSD-2-Clause, LGPL-2.1
  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\Filesystem;
  11. use Symfony\Component\Filesystem\Exception\IOException;
  12. /**
  13. * Provides basic utility to manipulate the file system.
  14. *
  15. * @author Fabien Potencier <fabien@symfony.com>
  16. */
  17. class Filesystem
  18. {
  19. /**
  20. * Copies a file.
  21. *
  22. * This method only copies the file if the origin file is newer than the target file.
  23. *
  24. * By default, if the target already exists, it is not overridden.
  25. *
  26. * @param string $originFile The original filename
  27. * @param string $targetFile The target filename
  28. * @param boolean $override Whether to override an existing file or not
  29. *
  30. * @throws IOException When copy fails
  31. */
  32. public function copy($originFile, $targetFile, $override = false)
  33. {
  34. $this->mkdir(dirname($targetFile));
  35. if (!$override && is_file($targetFile)) {
  36. $doCopy = filemtime($originFile) > filemtime($targetFile);
  37. } else {
  38. $doCopy = true;
  39. }
  40. if ($doCopy) {
  41. if (true !== @copy($originFile, $targetFile)) {
  42. throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile));
  43. }
  44. }
  45. }
  46. /**
  47. * Creates a directory recursively.
  48. *
  49. * @param string|array|\Traversable $dirs The directory path
  50. * @param integer $mode The directory mode
  51. *
  52. * @throws IOException On any directory creation failure
  53. */
  54. public function mkdir($dirs, $mode = 0777)
  55. {
  56. foreach ($this->toIterator($dirs) as $dir) {
  57. if (is_dir($dir)) {
  58. continue;
  59. }
  60. if (true !== @mkdir($dir, $mode, true)) {
  61. throw new IOException(sprintf('Failed to create %s', $dir));
  62. }
  63. }
  64. }
  65. /**
  66. * Checks the existence of files or directories.
  67. *
  68. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check
  69. *
  70. * @return Boolean true if the file exists, false otherwise
  71. */
  72. public function exists($files)
  73. {
  74. foreach ($this->toIterator($files) as $file) {
  75. if (!file_exists($file)) {
  76. return false;
  77. }
  78. }
  79. return true;
  80. }
  81. /**
  82. * Sets access and modification time of file.
  83. *
  84. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
  85. * @param integer $time The touch time as a unix timestamp
  86. * @param integer $atime The access time as a unix timestamp
  87. *
  88. * @throws IOException When touch fails
  89. */
  90. public function touch($files, $time = null, $atime = null)
  91. {
  92. if (null === $time) {
  93. $time = time();
  94. }
  95. foreach ($this->toIterator($files) as $file) {
  96. if (true !== @touch($file, $time, $atime)) {
  97. throw new IOException(sprintf('Failed to touch %s', $file));
  98. }
  99. }
  100. }
  101. /**
  102. * Removes files or directories.
  103. *
  104. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
  105. *
  106. * @throws IOException When removal fails
  107. */
  108. public function remove($files)
  109. {
  110. $files = iterator_to_array($this->toIterator($files));
  111. $files = array_reverse($files);
  112. foreach ($files as $file) {
  113. if (!file_exists($file) && !is_link($file)) {
  114. continue;
  115. }
  116. if (is_dir($file) && !is_link($file)) {
  117. $this->remove(new \FilesystemIterator($file));
  118. if (true !== @rmdir($file)) {
  119. throw new IOException(sprintf('Failed to remove directory %s', $file));
  120. }
  121. } else {
  122. // https://bugs.php.net/bug.php?id=52176
  123. if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) {
  124. if (true !== @rmdir($file)) {
  125. throw new IOException(sprintf('Failed to remove file %s', $file));
  126. }
  127. } else {
  128. if (true !== @unlink($file)) {
  129. throw new IOException(sprintf('Failed to remove file %s', $file));
  130. }
  131. }
  132. }
  133. }
  134. }
  135. /**
  136. * Change mode for an array of files or directories.
  137. *
  138. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode
  139. * @param integer $mode The new mode (octal)
  140. * @param integer $umask The mode mask (octal)
  141. * @param Boolean $recursive Whether change the mod recursively or not
  142. *
  143. * @throws IOException When the change fail
  144. */
  145. public function chmod($files, $mode, $umask = 0000, $recursive = false)
  146. {
  147. foreach ($this->toIterator($files) as $file) {
  148. if ($recursive && is_dir($file) && !is_link($file)) {
  149. $this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
  150. }
  151. if (true !== @chmod($file, $mode & ~$umask)) {
  152. throw new IOException(sprintf('Failed to chmod file %s', $file));
  153. }
  154. }
  155. }
  156. /**
  157. * Change the owner of an array of files or directories
  158. *
  159. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner
  160. * @param string $user The new owner user name
  161. * @param Boolean $recursive Whether change the owner recursively or not
  162. *
  163. * @throws IOException When the change fail
  164. */
  165. public function chown($files, $user, $recursive = false)
  166. {
  167. foreach ($this->toIterator($files) as $file) {
  168. if ($recursive && is_dir($file) && !is_link($file)) {
  169. $this->chown(new \FilesystemIterator($file), $user, true);
  170. }
  171. if (is_link($file) && function_exists('lchown')) {
  172. if (true !== @lchown($file, $user)) {
  173. throw new IOException(sprintf('Failed to chown file %s', $file));
  174. }
  175. } else {
  176. if (true !== @chown($file, $user)) {
  177. throw new IOException(sprintf('Failed to chown file %s', $file));
  178. }
  179. }
  180. }
  181. }
  182. /**
  183. * Change the group of an array of files or directories
  184. *
  185. * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group
  186. * @param string $group The group name
  187. * @param Boolean $recursive Whether change the group recursively or not
  188. *
  189. * @throws IOException When the change fail
  190. */
  191. public function chgrp($files, $group, $recursive = false)
  192. {
  193. foreach ($this->toIterator($files) as $file) {
  194. if ($recursive && is_dir($file) && !is_link($file)) {
  195. $this->chgrp(new \FilesystemIterator($file), $group, true);
  196. }
  197. if (is_link($file) && function_exists('lchgrp')) {
  198. if (true !== @lchgrp($file, $group)) {
  199. throw new IOException(sprintf('Failed to chgrp file %s', $file));
  200. }
  201. } else {
  202. if (true !== @chgrp($file, $group)) {
  203. throw new IOException(sprintf('Failed to chgrp file %s', $file));
  204. }
  205. }
  206. }
  207. }
  208. /**
  209. * Renames a file.
  210. *
  211. * @param string $origin The origin filename
  212. * @param string $target The new filename
  213. *
  214. * @throws IOException When target file already exists
  215. * @throws IOException When origin cannot be renamed
  216. */
  217. public function rename($origin, $target)
  218. {
  219. // we check that target does not exist
  220. if (is_readable($target)) {
  221. throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target));
  222. }
  223. if (true !== @rename($origin, $target)) {
  224. throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target));
  225. }
  226. }
  227. /**
  228. * Creates a symbolic link or copy a directory.
  229. *
  230. * @param string $originDir The origin directory path
  231. * @param string $targetDir The symbolic link name
  232. * @param Boolean $copyOnWindows Whether to copy files if on Windows
  233. *
  234. * @throws IOException When symlink fails
  235. */
  236. public function symlink($originDir, $targetDir, $copyOnWindows = false)
  237. {
  238. if (!function_exists('symlink') && $copyOnWindows) {
  239. $this->mirror($originDir, $targetDir);
  240. return;
  241. }
  242. $this->mkdir(dirname($targetDir));
  243. $ok = false;
  244. if (is_link($targetDir)) {
  245. if (readlink($targetDir) != $originDir) {
  246. $this->remove($targetDir);
  247. } else {
  248. $ok = true;
  249. }
  250. }
  251. if (!$ok) {
  252. if (true !== @symlink($originDir, $targetDir)) {
  253. $report = error_get_last();
  254. if (is_array($report)) {
  255. if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) {
  256. throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?');
  257. }
  258. }
  259. throw new IOException(sprintf('Failed to create symbolic link from %s to %s', $originDir, $targetDir));
  260. }
  261. }
  262. }
  263. /**
  264. * Given an existing path, convert it to a path relative to a given starting path
  265. *
  266. * @param string $endPath Absolute path of target
  267. * @param string $startPath Absolute path where traversal begins
  268. *
  269. * @return string Path of target relative to starting path
  270. */
  271. public function makePathRelative($endPath, $startPath)
  272. {
  273. // Normalize separators on windows
  274. if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
  275. $endPath = strtr($endPath, '\\', '/');
  276. $startPath = strtr($startPath, '\\', '/');
  277. }
  278. // Split the paths into arrays
  279. $startPathArr = explode('/', trim($startPath, '/'));
  280. $endPathArr = explode('/', trim($endPath, '/'));
  281. // Find for which directory the common path stops
  282. $index = 0;
  283. while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
  284. $index++;
  285. }
  286. // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
  287. $depth = count($startPathArr) - $index;
  288. // Repeated "../" for each level need to reach the common path
  289. $traverser = str_repeat('../', $depth);
  290. $endPathRemainder = implode('/', array_slice($endPathArr, $index));
  291. // Construct $endPath from traversing to the common path, then to the remaining $endPath
  292. $relativePath = $traverser . (strlen($endPathRemainder) > 0 ? $endPathRemainder . '/' : '');
  293. return (strlen($relativePath) === 0) ? './' : $relativePath;
  294. }
  295. /**
  296. * Mirrors a directory to another.
  297. *
  298. * @param string $originDir The origin directory
  299. * @param string $targetDir The target directory
  300. * @param \Traversable $iterator A Traversable instance
  301. * @param array $options An array of boolean options
  302. * Valid options are:
  303. * - $options['override'] Whether to override an existing file on copy or not (see copy())
  304. * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
  305. *
  306. * @throws IOException When file type is unknown
  307. */
  308. public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
  309. {
  310. $copyOnWindows = false;
  311. if (isset($options['copy_on_windows']) && !function_exists('symlink')) {
  312. $copyOnWindows = $options['copy_on_windows'];
  313. }
  314. if (null === $iterator) {
  315. $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
  316. $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
  317. }
  318. $targetDir = rtrim($targetDir, '/\\');
  319. $originDir = rtrim($originDir, '/\\');
  320. foreach ($iterator as $file) {
  321. $target = str_replace($originDir, $targetDir, $file->getPathname());
  322. if (is_dir($file)) {
  323. $this->mkdir($target);
  324. } elseif (!$copyOnWindows && is_link($file)) {
  325. $this->symlink($file, $target);
  326. } elseif (is_file($file) || ($copyOnWindows && is_link($file))) {
  327. $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
  328. } else {
  329. throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
  330. }
  331. }
  332. }
  333. /**
  334. * Returns whether the file path is an absolute path.
  335. *
  336. * @param string $file A file path
  337. *
  338. * @return Boolean
  339. */
  340. public function isAbsolutePath($file)
  341. {
  342. if (strspn($file, '/\\', 0, 1)
  343. || (strlen($file) > 3 && ctype_alpha($file[0])
  344. && substr($file, 1, 1) === ':'
  345. && (strspn($file, '/\\', 2, 1))
  346. )
  347. || null !== parse_url($file, PHP_URL_SCHEME)
  348. ) {
  349. return true;
  350. }
  351. return false;
  352. }
  353. /**
  354. * @param mixed $files
  355. *
  356. * @return \Traversable
  357. */
  358. private function toIterator($files)
  359. {
  360. if (!$files instanceof \Traversable) {
  361. $files = new \ArrayObject(is_array($files) ? $files : array($files));
  362. }
  363. return $files;
  364. }
  365. }