Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

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