Folder.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. <?php
  2. /**
  3. * Convenience class for handling directories.
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package Cake.Utility
  16. * @since CakePHP(tm) v 0.2.9
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. /**
  20. * Folder structure browser, lists folders and files.
  21. * Provides an Object interface for Common directory related tasks.
  22. *
  23. * @package Cake.Utility
  24. */
  25. class Folder {
  26. /**
  27. * Path to Folder.
  28. *
  29. * @var string
  30. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$path
  31. */
  32. public $path = null;
  33. /**
  34. * Sortedness. Whether or not list results
  35. * should be sorted by name.
  36. *
  37. * @var boolean
  38. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$sort
  39. */
  40. public $sort = false;
  41. /**
  42. * Mode to be used on create. Does nothing on windows platforms.
  43. *
  44. * @var integer
  45. * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$mode
  46. */
  47. public $mode = 0755;
  48. /**
  49. * Holds messages from last method.
  50. *
  51. * @var array
  52. */
  53. protected $_messages = array();
  54. /**
  55. * Holds errors from last method.
  56. *
  57. * @var array
  58. */
  59. protected $_errors = array();
  60. /**
  61. * Holds array of complete directory paths.
  62. *
  63. * @var array
  64. */
  65. protected $_directories;
  66. /**
  67. * Holds array of complete file paths.
  68. *
  69. * @var array
  70. */
  71. protected $_files;
  72. /**
  73. * Constructor.
  74. *
  75. * @param string $path Path to folder
  76. * @param boolean $create Create folder if not found
  77. * @param mixed $mode Mode (CHMOD) to apply to created folder, false to ignore
  78. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder
  79. */
  80. public function __construct($path = false, $create = false, $mode = false) {
  81. if (empty($path)) {
  82. $path = TMP;
  83. }
  84. if ($mode) {
  85. $this->mode = $mode;
  86. }
  87. if (!file_exists($path) && $create === true) {
  88. $this->create($path, $this->mode);
  89. }
  90. if (!Folder::isAbsolute($path)) {
  91. $path = realpath($path);
  92. }
  93. if (!empty($path)) {
  94. $this->cd($path);
  95. }
  96. }
  97. /**
  98. * Return current path.
  99. *
  100. * @return string Current path
  101. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::pwd
  102. */
  103. public function pwd() {
  104. return $this->path;
  105. }
  106. /**
  107. * Change directory to $path.
  108. *
  109. * @param string $path Path to the directory to change to
  110. * @return string The new path. Returns false on failure
  111. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::cd
  112. */
  113. public function cd($path) {
  114. $path = $this->realpath($path);
  115. if (is_dir($path)) {
  116. return $this->path = $path;
  117. }
  118. return false;
  119. }
  120. /**
  121. * Returns an array of the contents of the current directory.
  122. * The returned array holds two arrays: One of directories and one of files.
  123. *
  124. * @param boolean $sort Whether you want the results sorted, set this and the sort property
  125. * to false to get unsorted results.
  126. * @param mixed $exceptions Either an array or boolean true will not grab dot files
  127. * @param boolean $fullPath True returns the full path
  128. * @return mixed Contents of current directory as an array, an empty array on failure
  129. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::read
  130. */
  131. public function read($sort = true, $exceptions = false, $fullPath = false) {
  132. $dirs = $files = array();
  133. if (!$this->pwd()) {
  134. return array($dirs, $files);
  135. }
  136. if (is_array($exceptions)) {
  137. $exceptions = array_flip($exceptions);
  138. }
  139. $skipHidden = isset($exceptions['.']) || $exceptions === true;
  140. try {
  141. $iterator = new DirectoryIterator($this->path);
  142. } catch (UnexpectedValueException $e) {
  143. return array($dirs, $files);
  144. }
  145. foreach ($iterator as $item) {
  146. if ($item->isDot()) {
  147. continue;
  148. }
  149. $name = $item->getFileName();
  150. if ($skipHidden && $name[0] === '.' || isset($exceptions[$name])) {
  151. continue;
  152. }
  153. if ($fullPath) {
  154. $name = $item->getPathName();
  155. }
  156. if ($item->isDir()) {
  157. $dirs[] = $name;
  158. } else {
  159. $files[] = $name;
  160. }
  161. }
  162. if ($sort || $this->sort) {
  163. sort($dirs);
  164. sort($files);
  165. }
  166. return array($dirs, $files);
  167. }
  168. /**
  169. * Returns an array of all matching files in current directory.
  170. *
  171. * @param string $regexpPattern Preg_match pattern (Defaults to: .*)
  172. * @param boolean $sort Whether results should be sorted.
  173. * @return array Files that match given pattern
  174. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::find
  175. */
  176. public function find($regexpPattern = '.*', $sort = false) {
  177. list($dirs, $files) = $this->read($sort);
  178. return array_values(preg_grep('/^' . $regexpPattern . '$/i', $files)); ;
  179. }
  180. /**
  181. * Returns an array of all matching files in and below current directory.
  182. *
  183. * @param string $pattern Preg_match pattern (Defaults to: .*)
  184. * @param boolean $sort Whether results should be sorted.
  185. * @return array Files matching $pattern
  186. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::findRecursive
  187. */
  188. public function findRecursive($pattern = '.*', $sort = false) {
  189. if (!$this->pwd()) {
  190. return array();
  191. }
  192. $startsOn = $this->path;
  193. $out = $this->_findRecursive($pattern, $sort);
  194. $this->cd($startsOn);
  195. return $out;
  196. }
  197. /**
  198. * Private helper function for findRecursive.
  199. *
  200. * @param string $pattern Pattern to match against
  201. * @param boolean $sort Whether results should be sorted.
  202. * @return array Files matching pattern
  203. */
  204. protected function _findRecursive($pattern, $sort = false) {
  205. list($dirs, $files) = $this->read($sort);
  206. $found = array();
  207. foreach ($files as $file) {
  208. if (preg_match('/^' . $pattern . '$/i', $file)) {
  209. $found[] = Folder::addPathElement($this->path, $file);
  210. }
  211. }
  212. $start = $this->path;
  213. foreach ($dirs as $dir) {
  214. $this->cd(Folder::addPathElement($start, $dir));
  215. $found = array_merge($found, $this->findRecursive($pattern, $sort));
  216. }
  217. return $found;
  218. }
  219. /**
  220. * Returns true if given $path is a Windows path.
  221. *
  222. * @param string $path Path to check
  223. * @return boolean true if windows path, false otherwise
  224. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isWindowsPath
  225. */
  226. public static function isWindowsPath($path) {
  227. return (preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
  228. }
  229. /**
  230. * Returns true if given $path is an absolute path.
  231. *
  232. * @param string $path Path to check
  233. * @return boolean true if path is absolute.
  234. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isAbsolute
  235. */
  236. public static function isAbsolute($path) {
  237. return !empty($path) && ($path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
  238. }
  239. /**
  240. * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
  241. *
  242. * @param string $path Path to check
  243. * @return string Set of slashes ("\\" or "/")
  244. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::normalizePath
  245. */
  246. public static function normalizePath($path) {
  247. return Folder::correctSlashFor($path);
  248. }
  249. /**
  250. * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
  251. *
  252. * @param string $path Path to check
  253. * @return string Set of slashes ("\\" or "/")
  254. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::correctSlashFor
  255. */
  256. public static function correctSlashFor($path) {
  257. return (Folder::isWindowsPath($path)) ? '\\' : '/';
  258. }
  259. /**
  260. * Returns $path with added terminating slash (corrected for Windows or other OS).
  261. *
  262. * @param string $path Path to check
  263. * @return string Path with ending slash
  264. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::slashTerm
  265. */
  266. public static function slashTerm($path) {
  267. if (Folder::isSlashTerm($path)) {
  268. return $path;
  269. }
  270. return $path . Folder::correctSlashFor($path);
  271. }
  272. /**
  273. * Returns $path with $element added, with correct slash in-between.
  274. *
  275. * @param string $path Path
  276. * @param string $element Element to and at end of path
  277. * @return string Combined path
  278. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::addPathElement
  279. */
  280. public static function addPathElement($path, $element) {
  281. return rtrim($path, DS) . DS . $element;
  282. }
  283. /**
  284. * Returns true if the File is in a given CakePath.
  285. *
  286. * @param string $path The path to check.
  287. * @return boolean
  288. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inCakePath
  289. */
  290. public function inCakePath($path = '') {
  291. $dir = substr(Folder::slashTerm(ROOT), 0, -1);
  292. $newdir = $dir . $path;
  293. return $this->inPath($newdir);
  294. }
  295. /**
  296. * Returns true if the File is in given path.
  297. *
  298. * @param string $path The path to check that the current pwd() resides with in.
  299. * @param boolean $reverse
  300. * @return boolean
  301. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inPath
  302. */
  303. public function inPath($path = '', $reverse = false) {
  304. $dir = Folder::slashTerm($path);
  305. $current = Folder::slashTerm($this->pwd());
  306. if (!$reverse) {
  307. $return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
  308. } else {
  309. $return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
  310. }
  311. return (bool)$return;
  312. }
  313. /**
  314. * Change the mode on a directory structure recursively. This includes changing the mode on files as well.
  315. *
  316. * @param string $path The path to chmod
  317. * @param integer $mode octal value 0755
  318. * @param boolean $recursive chmod recursively, set to false to only change the current directory.
  319. * @param array $exceptions array of files, directories to skip
  320. * @return boolean Returns TRUE on success, FALSE on failure
  321. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::chmod
  322. */
  323. public function chmod($path, $mode = false, $recursive = true, $exceptions = array()) {
  324. if (!$mode) {
  325. $mode = $this->mode;
  326. }
  327. if ($recursive === false && is_dir($path)) {
  328. if (@chmod($path, intval($mode, 8))) {
  329. $this->_messages[] = __d('cake_dev', '%s changed to %s', $path, $mode);
  330. return true;
  331. }
  332. $this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $path, $mode);
  333. return false;
  334. }
  335. if (is_dir($path)) {
  336. $paths = $this->tree($path);
  337. foreach ($paths as $type) {
  338. foreach ($type as $key => $fullpath) {
  339. $check = explode(DS, $fullpath);
  340. $count = count($check);
  341. if (in_array($check[$count - 1], $exceptions)) {
  342. continue;
  343. }
  344. if (@chmod($fullpath, intval($mode, 8))) {
  345. $this->_messages[] = __d('cake_dev', '%s changed to %s', $fullpath, $mode);
  346. } else {
  347. $this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $fullpath, $mode);
  348. }
  349. }
  350. }
  351. if (empty($this->_errors)) {
  352. return true;
  353. }
  354. }
  355. return false;
  356. }
  357. /**
  358. * Returns an array of nested directories and files in each directory
  359. *
  360. * @param string $path the directory path to build the tree from
  361. * @param mixed $exceptions Either an array of files/folder to exclude
  362. * or boolean true to not grab dot files/folders
  363. * @param string $type either 'file' or 'dir'. null returns both files and directories
  364. * @return mixed array of nested directories and files in each directory
  365. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::tree
  366. */
  367. public function tree($path = null, $exceptions = false, $type = null) {
  368. if ($path == null) {
  369. $path = $this->path;
  370. }
  371. $files = array();
  372. $directories = array($path);
  373. if (is_array($exceptions)) {
  374. $exceptions = array_flip($exceptions);
  375. if (isset($exceptions['.'])) {
  376. $skipHidden = true;
  377. unset($exceptions['.']);
  378. }
  379. }
  380. $skipHidden = false;
  381. if ($exceptions === true) {
  382. $skipHidden = true;
  383. } elseif (isset($exceptions['.'])) {
  384. $skipHidden = true;
  385. unset($exceptions['.']);
  386. }
  387. try {
  388. $directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME | RecursiveDirectoryIterator::CURRENT_AS_SELF | RecursiveDirectoryIterator::SKIP_DOTS);
  389. $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST);
  390. } catch (UnexpectedValueException $e) {
  391. if ($type === null) {
  392. return array(array(), array());
  393. }
  394. return array();
  395. }
  396. $pathLength = strlen($path);
  397. foreach ($iterator as $itemPath => $fsIterator) {
  398. if ($skipHidden) {
  399. $subPathName = $fsIterator->getSubPathname();
  400. if ($subPathName{0} == '.' || strpos($subPathName, DS . '.') !== false) {
  401. continue;
  402. }
  403. }
  404. $item = $fsIterator->current();
  405. if (!empty($exceptions) && isset($exceptions[$item->getFilename()])) {
  406. continue;
  407. }
  408. if ($item->isFile()) {
  409. $files[] = $itemPath;
  410. } elseif ($item->isDir()) {
  411. $directories[] = $itemPath;
  412. }
  413. }
  414. if ($type === null) {
  415. return array($directories, $files);
  416. }
  417. if ($type === 'dir') {
  418. return $directories;
  419. }
  420. return $files;
  421. }
  422. /**
  423. * Create a directory structure recursively. Can be used to create
  424. * deep path structures like `/foo/bar/baz/shoe/horn`
  425. *
  426. * @param string $pathname The directory structure to create
  427. * @param integer $mode octal value 0755
  428. * @return boolean Returns TRUE on success, FALSE on failure
  429. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::create
  430. */
  431. public function create($pathname, $mode = false) {
  432. if (is_dir($pathname) || empty($pathname)) {
  433. return true;
  434. }
  435. if (!$mode) {
  436. $mode = $this->mode;
  437. }
  438. if (is_file($pathname)) {
  439. $this->_errors[] = __d('cake_dev', '%s is a file', $pathname);
  440. return false;
  441. }
  442. $pathname = rtrim($pathname, DS);
  443. $nextPathname = substr($pathname, 0, strrpos($pathname, DS));
  444. if ($this->create($nextPathname, $mode)) {
  445. if (!file_exists($pathname)) {
  446. $old = umask(0);
  447. if (mkdir($pathname, $mode)) {
  448. umask($old);
  449. $this->_messages[] = __d('cake_dev', '%s created', $pathname);
  450. return true;
  451. } else {
  452. umask($old);
  453. $this->_errors[] = __d('cake_dev', '%s NOT created', $pathname);
  454. return false;
  455. }
  456. }
  457. }
  458. return false;
  459. }
  460. /**
  461. * Returns the size in bytes of this Folder and its contents.
  462. *
  463. * @return integer size in bytes of current folder
  464. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::dirsize
  465. */
  466. public function dirsize() {
  467. $size = 0;
  468. $directory = Folder::slashTerm($this->path);
  469. $stack = array($directory);
  470. $count = count($stack);
  471. for ($i = 0, $j = $count; $i < $j; ++$i) {
  472. if (is_file($stack[$i])) {
  473. $size += filesize($stack[$i]);
  474. } elseif (is_dir($stack[$i])) {
  475. $dir = dir($stack[$i]);
  476. if ($dir) {
  477. while (false !== ($entry = $dir->read())) {
  478. if ($entry === '.' || $entry === '..') {
  479. continue;
  480. }
  481. $add = $stack[$i] . $entry;
  482. if (is_dir($stack[$i] . $entry)) {
  483. $add = Folder::slashTerm($add);
  484. }
  485. $stack[] = $add;
  486. }
  487. $dir->close();
  488. }
  489. }
  490. $j = count($stack);
  491. }
  492. return $size;
  493. }
  494. /**
  495. * Recursively Remove directories if the system allows.
  496. *
  497. * @param string $path Path of directory to delete
  498. * @return boolean Success
  499. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::delete
  500. */
  501. public function delete($path = null) {
  502. if (!$path) {
  503. $path = $this->pwd();
  504. }
  505. if (!$path) {
  506. return null;
  507. }
  508. $path = Folder::slashTerm($path);
  509. if (is_dir($path)) {
  510. try {
  511. $directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
  512. $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::CHILD_FIRST);
  513. } catch (UnexpectedValueException $e) {
  514. return false;
  515. }
  516. foreach ($iterator as $item) {
  517. $filePath = $item->getPathname();
  518. if ($item->isFile() || $item->isLink()) {
  519. if (@unlink($filePath)) {
  520. $this->_messages[] = __d('cake_dev', '%s removed', $filePath);
  521. } else {
  522. $this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath);
  523. }
  524. } elseif ($item->isDir()) {
  525. if (@rmdir($filePath)) {
  526. $this->_messages[] = __d('cake_dev', '%s removed', $filePath);
  527. } else {
  528. $this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath);
  529. return false;
  530. }
  531. }
  532. }
  533. $path = rtrim($path, DS);
  534. if (@rmdir($path)) {
  535. $this->_messages[] = __d('cake_dev', '%s removed', $path);
  536. } else {
  537. $this->_errors[] = __d('cake_dev', '%s NOT removed', $path);
  538. return false;
  539. }
  540. }
  541. return true;
  542. }
  543. /**
  544. * Recursive directory copy.
  545. *
  546. * ### Options
  547. *
  548. * - `to` The directory to copy to.
  549. * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
  550. * - `mode` The mode to copy the files/directories with.
  551. * - `skip` Files/directories to skip.
  552. *
  553. * @param mixed $options Either an array of options (see above) or a string of the destination directory.
  554. * @return boolean Success
  555. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::copy
  556. */
  557. public function copy($options = array()) {
  558. if (!$this->pwd()) {
  559. return false;
  560. }
  561. $to = null;
  562. if (is_string($options)) {
  563. $to = $options;
  564. $options = array();
  565. }
  566. $options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
  567. $fromDir = $options['from'];
  568. $toDir = $options['to'];
  569. $mode = $options['mode'];
  570. if (!$this->cd($fromDir)) {
  571. $this->_errors[] = __d('cake_dev', '%s not found', $fromDir);
  572. return false;
  573. }
  574. if (!is_dir($toDir)) {
  575. $this->create($toDir, $mode);
  576. }
  577. if (!is_writable($toDir)) {
  578. $this->_errors[] = __d('cake_dev', '%s not writable', $toDir);
  579. return false;
  580. }
  581. $exceptions = array_merge(array('.', '..', '.svn'), $options['skip']);
  582. if ($handle = @opendir($fromDir)) {
  583. while (false !== ($item = readdir($handle))) {
  584. if (!in_array($item, $exceptions)) {
  585. $from = Folder::addPathElement($fromDir, $item);
  586. $to = Folder::addPathElement($toDir, $item);
  587. if (is_file($from)) {
  588. if (copy($from, $to)) {
  589. chmod($to, intval($mode, 8));
  590. touch($to, filemtime($from));
  591. $this->_messages[] = __d('cake_dev', '%s copied to %s', $from, $to);
  592. } else {
  593. $this->_errors[] = __d('cake_dev', '%s NOT copied to %s', $from, $to);
  594. }
  595. }
  596. if (is_dir($from) && !file_exists($to)) {
  597. $old = umask(0);
  598. if (mkdir($to, $mode)) {
  599. umask($old);
  600. $old = umask(0);
  601. chmod($to, $mode);
  602. umask($old);
  603. $this->_messages[] = __d('cake_dev', '%s created', $to);
  604. $options = array_merge($options, array('to' => $to, 'from' => $from));
  605. $this->copy($options);
  606. } else {
  607. $this->_errors[] = __d('cake_dev', '%s not created', $to);
  608. }
  609. }
  610. }
  611. }
  612. closedir($handle);
  613. } else {
  614. return false;
  615. }
  616. if (!empty($this->_errors)) {
  617. return false;
  618. }
  619. return true;
  620. }
  621. /**
  622. * Recursive directory move.
  623. *
  624. * ### Options
  625. *
  626. * - `to` The directory to copy to.
  627. * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
  628. * - `chmod` The mode to copy the files/directories with.
  629. * - `skip` Files/directories to skip.
  630. *
  631. * @param array $options (to, from, chmod, skip)
  632. * @return boolean Success
  633. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::move
  634. */
  635. public function move($options) {
  636. $to = null;
  637. if (is_string($options)) {
  638. $to = $options;
  639. $options = (array)$options;
  640. }
  641. $options = array_merge(
  642. array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()),
  643. $options
  644. );
  645. if ($this->copy($options)) {
  646. if ($this->delete($options['from'])) {
  647. return (bool)$this->cd($options['to']);
  648. }
  649. }
  650. return false;
  651. }
  652. /**
  653. * get messages from latest method
  654. *
  655. * @return array
  656. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::messages
  657. */
  658. public function messages() {
  659. return $this->_messages;
  660. }
  661. /**
  662. * get error from latest method
  663. *
  664. * @return array
  665. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::errors
  666. */
  667. public function errors() {
  668. return $this->_errors;
  669. }
  670. /**
  671. * Get the real path (taking ".." and such into account)
  672. *
  673. * @param string $path Path to resolve
  674. * @return string The resolved path
  675. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::realpath
  676. */
  677. public function realpath($path) {
  678. $path = str_replace('/', DS, trim($path));
  679. if (strpos($path, '..') === false) {
  680. if (!Folder::isAbsolute($path)) {
  681. $path = Folder::addPathElement($this->path, $path);
  682. }
  683. return $path;
  684. }
  685. $parts = explode(DS, $path);
  686. $newparts = array();
  687. $newpath = '';
  688. if ($path[0] === DS) {
  689. $newpath = DS;
  690. }
  691. while (($part = array_shift($parts)) !== NULL) {
  692. if ($part === '.' || $part === '') {
  693. continue;
  694. }
  695. if ($part === '..') {
  696. if (!empty($newparts)) {
  697. array_pop($newparts);
  698. continue;
  699. } else {
  700. return false;
  701. }
  702. }
  703. $newparts[] = $part;
  704. }
  705. $newpath .= implode(DS, $newparts);
  706. return Folder::slashTerm($newpath);
  707. }
  708. /**
  709. * Returns true if given $path ends in a slash (i.e. is slash-terminated).
  710. *
  711. * @param string $path Path to check
  712. * @return boolean true if path ends with slash, false otherwise
  713. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isSlashTerm
  714. */
  715. public static function isSlashTerm($path) {
  716. $lastChar = $path[strlen($path) - 1];
  717. return $lastChar === '/' || $lastChar === '\\';
  718. }
  719. }