Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
13532 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
 * @constant MERGE
30
 */
31
	const MERGE = 'merge';
32
 
33
/**
34
 * Overwrite scheme for Folder::copy
35
 * subfolders with the same name will be replaced
36
 *
37
 * @constant OVERWRITE
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
 * @constant SKIP
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 boolean
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 integer
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 boolean $create Create folder if not found
107
 * @param string|boolean $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 boolean $sort Whether you want the results sorted, set this and the sort property
159
 *   to false to get unsorted results.
160
 * @param array|boolean $exceptions Either an array or boolean true will not grab dot files
161
 * @param boolean $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 boolean $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 boolean $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 boolean $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 boolean 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 boolean 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 $element Element to and 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
		return rtrim($path, DS) . DS . $element;
330
	}
331
 
332
/**
333
 * Returns true if the File is in a given CakePath.
334
 *
335
 * @param string $path The path to check.
336
 * @return boolean
337
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inCakePath
338
 */
339
	public function inCakePath($path = '') {
340
		$dir = substr(Folder::slashTerm(ROOT), 0, -1);
341
		$newdir = $dir . $path;
342
 
343
		return $this->inPath($newdir);
344
	}
345
 
346
/**
347
 * Returns true if the File is in given path.
348
 *
349
 * @param string $path The path to check that the current pwd() resides with in.
350
 * @param boolean $reverse Reverse the search, check that pwd() resides within $path.
351
 * @return boolean
352
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inPath
353
 */
354
	public function inPath($path = '', $reverse = false) {
355
		$dir = Folder::slashTerm($path);
356
		$current = Folder::slashTerm($this->pwd());
357
 
358
		if (!$reverse) {
359
			$return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
360
		} else {
361
			$return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
362
		}
363
		return (bool)$return;
364
	}
365
 
366
/**
367
 * Change the mode on a directory structure recursively. This includes changing the mode on files as well.
368
 *
369
 * @param string $path The path to chmod
370
 * @param integer $mode octal value 0755
371
 * @param boolean $recursive chmod recursively, set to false to only change the current directory.
372
 * @param array $exceptions array of files, directories to skip
373
 * @return boolean Returns TRUE on success, FALSE on failure
374
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::chmod
375
 */
376
	public function chmod($path, $mode = false, $recursive = true, $exceptions = array()) {
377
		if (!$mode) {
378
			$mode = $this->mode;
379
		}
380
 
381
		if ($recursive === false && is_dir($path)) {
382
			//@codingStandardsIgnoreStart
383
			if (@chmod($path, intval($mode, 8))) {
384
				//@codingStandardsIgnoreEnd
385
				$this->_messages[] = __d('cake_dev', '%s changed to %s', $path, $mode);
386
				return true;
387
			}
388
 
389
			$this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $path, $mode);
390
			return false;
391
		}
392
 
393
		if (is_dir($path)) {
394
			$paths = $this->tree($path);
395
 
396
			foreach ($paths as $type) {
397
				foreach ($type as $fullpath) {
398
					$check = explode(DS, $fullpath);
399
					$count = count($check);
400
 
401
					if (in_array($check[$count - 1], $exceptions)) {
402
						continue;
403
					}
404
 
405
					//@codingStandardsIgnoreStart
406
					if (@chmod($fullpath, intval($mode, 8))) {
407
						//@codingStandardsIgnoreEnd
408
						$this->_messages[] = __d('cake_dev', '%s changed to %s', $fullpath, $mode);
409
					} else {
410
						$this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $fullpath, $mode);
411
					}
412
				}
413
			}
414
 
415
			if (empty($this->_errors)) {
416
				return true;
417
			}
418
		}
419
		return false;
420
	}
421
 
422
/**
423
 * Returns an array of nested directories and files in each directory
424
 *
425
 * @param string $path the directory path to build the tree from
426
 * @param array|boolean $exceptions Either an array of files/folder to exclude
427
 *   or boolean true to not grab dot files/folders
428
 * @param string $type either 'file' or 'dir'. null returns both files and directories
429
 * @return mixed array of nested directories and files in each directory
430
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::tree
431
 */
432
	public function tree($path = null, $exceptions = false, $type = null) {
433
		if (!$path) {
434
			$path = $this->path;
435
		}
436
		$files = array();
437
		$directories = array($path);
438
 
439
		if (is_array($exceptions)) {
440
			$exceptions = array_flip($exceptions);
441
		}
442
		$skipHidden = false;
443
		if ($exceptions === true) {
444
			$skipHidden = true;
445
		} elseif (isset($exceptions['.'])) {
446
			$skipHidden = true;
447
			unset($exceptions['.']);
448
		}
449
 
450
		try {
451
			$directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME | RecursiveDirectoryIterator::CURRENT_AS_SELF);
452
			$iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST);
453
		} catch (Exception $e) {
454
			if ($type === null) {
455
				return array(array(), array());
456
			}
457
			return array();
458
		}
459
 
460
		foreach ($iterator as $itemPath => $fsIterator) {
461
			if ($skipHidden) {
462
				$subPathName = $fsIterator->getSubPathname();
463
				if ($subPathName{0} === '.' || strpos($subPathName, DS . '.') !== false) {
464
					continue;
465
				}
466
			}
467
			$item = $fsIterator->current();
468
			if (!empty($exceptions) && isset($exceptions[$item->getFilename()])) {
469
				continue;
470
			}
471
 
472
			if ($item->isFile()) {
473
				$files[] = $itemPath;
474
			} elseif ($item->isDir() && !$item->isDot()) {
475
				$directories[] = $itemPath;
476
			}
477
		}
478
		if ($type === null) {
479
			return array($directories, $files);
480
		}
481
		if ($type === 'dir') {
482
			return $directories;
483
		}
484
		return $files;
485
	}
486
 
487
/**
488
 * Create a directory structure recursively. Can be used to create
489
 * deep path structures like `/foo/bar/baz/shoe/horn`
490
 *
491
 * @param string $pathname The directory structure to create
492
 * @param integer $mode octal value 0755
493
 * @return boolean Returns TRUE on success, FALSE on failure
494
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::create
495
 */
496
	public function create($pathname, $mode = false) {
497
		if (is_dir($pathname) || empty($pathname)) {
498
			return true;
499
		}
500
 
501
		if (!$mode) {
502
			$mode = $this->mode;
503
		}
504
 
505
		if (is_file($pathname)) {
506
			$this->_errors[] = __d('cake_dev', '%s is a file', $pathname);
507
			return false;
508
		}
509
		$pathname = rtrim($pathname, DS);
510
		$nextPathname = substr($pathname, 0, strrpos($pathname, DS));
511
 
512
		if ($this->create($nextPathname, $mode)) {
513
			if (!file_exists($pathname)) {
514
				$old = umask(0);
515
				if (mkdir($pathname, $mode)) {
516
					umask($old);
517
					$this->_messages[] = __d('cake_dev', '%s created', $pathname);
518
					return true;
519
				}
520
				umask($old);
521
				$this->_errors[] = __d('cake_dev', '%s NOT created', $pathname);
522
				return false;
523
			}
524
		}
525
		return false;
526
	}
527
 
528
/**
529
 * Returns the size in bytes of this Folder and its contents.
530
 *
531
 * @return integer size in bytes of current folder
532
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::dirsize
533
 */
534
	public function dirsize() {
535
		$size = 0;
536
		$directory = Folder::slashTerm($this->path);
537
		$stack = array($directory);
538
		$count = count($stack);
539
		for ($i = 0, $j = $count; $i < $j; ++$i) {
540
			if (is_file($stack[$i])) {
541
				$size += filesize($stack[$i]);
542
			} elseif (is_dir($stack[$i])) {
543
				$dir = dir($stack[$i]);
544
				if ($dir) {
545
					while (false !== ($entry = $dir->read())) {
546
						if ($entry === '.' || $entry === '..') {
547
							continue;
548
						}
549
						$add = $stack[$i] . $entry;
550
 
551
						if (is_dir($stack[$i] . $entry)) {
552
							$add = Folder::slashTerm($add);
553
						}
554
						$stack[] = $add;
555
					}
556
					$dir->close();
557
				}
558
			}
559
			$j = count($stack);
560
		}
561
		return $size;
562
	}
563
 
564
/**
565
 * Recursively Remove directories if the system allows.
566
 *
567
 * @param string $path Path of directory to delete
568
 * @return boolean Success
569
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::delete
570
 */
571
	public function delete($path = null) {
572
		if (!$path) {
573
			$path = $this->pwd();
574
		}
575
		if (!$path) {
576
			return null;
577
		}
578
		$path = Folder::slashTerm($path);
579
		if (is_dir($path)) {
580
			try {
581
				$directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::CURRENT_AS_SELF);
582
				$iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::CHILD_FIRST);
583
			} catch (Exception $e) {
584
				return false;
585
			}
586
 
587
			foreach ($iterator as $item) {
588
				$filePath = $item->getPathname();
589
				if ($item->isFile() || $item->isLink()) {
590
					//@codingStandardsIgnoreStart
591
					if (@unlink($filePath)) {
592
						//@codingStandardsIgnoreEnd
593
						$this->_messages[] = __d('cake_dev', '%s removed', $filePath);
594
					} else {
595
						$this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath);
596
					}
597
				} elseif ($item->isDir() && !$item->isDot()) {
598
					//@codingStandardsIgnoreStart
599
					if (@rmdir($filePath)) {
600
						//@codingStandardsIgnoreEnd
601
						$this->_messages[] = __d('cake_dev', '%s removed', $filePath);
602
					} else {
603
						$this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath);
604
						return false;
605
					}
606
				}
607
			}
608
 
609
			$path = rtrim($path, DS);
610
			//@codingStandardsIgnoreStart
611
			if (@rmdir($path)) {
612
				//@codingStandardsIgnoreEnd
613
				$this->_messages[] = __d('cake_dev', '%s removed', $path);
614
			} else {
615
				$this->_errors[] = __d('cake_dev', '%s NOT removed', $path);
616
				return false;
617
			}
618
		}
619
		return true;
620
	}
621
 
622
/**
623
 * Recursive directory copy.
624
 *
625
 * ### Options
626
 *
627
 * - `to` The directory to copy to.
628
 * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
629
 * - `mode` The mode to copy the files/directories with.
630
 * - `skip` Files/directories to skip.
631
 * - `scheme` Folder::MERGE, Folder::OVERWRITE, Folder::SKIP
632
 *
633
 * @param array|string $options Either an array of options (see above) or a string of the destination directory.
634
 * @return boolean Success
635
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::copy
636
 */
637
	public function copy($options) {
638
		if (!$this->pwd()) {
639
			return false;
640
		}
641
		$to = null;
642
		if (is_string($options)) {
643
			$to = $options;
644
			$options = array();
645
		}
646
		$options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array(), 'scheme' => Folder::MERGE), $options);
647
 
648
		$fromDir = $options['from'];
649
		$toDir = $options['to'];
650
		$mode = $options['mode'];
651
 
652
		if (!$this->cd($fromDir)) {
653
			$this->_errors[] = __d('cake_dev', '%s not found', $fromDir);
654
			return false;
655
		}
656
 
657
		if (!is_dir($toDir)) {
658
			$this->create($toDir, $mode);
659
		}
660
 
661
		if (!is_writable($toDir)) {
662
			$this->_errors[] = __d('cake_dev', '%s not writable', $toDir);
663
			return false;
664
		}
665
 
666
		$exceptions = array_merge(array('.', '..', '.svn'), $options['skip']);
667
		//@codingStandardsIgnoreStart
668
		if ($handle = @opendir($fromDir)) {
669
			//@codingStandardsIgnoreEnd
670
			while (($item = readdir($handle)) !== false) {
671
				$to = Folder::addPathElement($toDir, $item);
672
				if (($options['scheme'] != Folder::SKIP || !is_dir($to)) && !in_array($item, $exceptions)) {
673
					$from = Folder::addPathElement($fromDir, $item);
674
					if (is_file($from)) {
675
						if (copy($from, $to)) {
676
							chmod($to, intval($mode, 8));
677
							touch($to, filemtime($from));
678
							$this->_messages[] = __d('cake_dev', '%s copied to %s', $from, $to);
679
						} else {
680
							$this->_errors[] = __d('cake_dev', '%s NOT copied to %s', $from, $to);
681
						}
682
					}
683
 
684
					if (is_dir($from) && file_exists($to) && $options['scheme'] == Folder::OVERWRITE) {
685
						$this->delete($to);
686
					}
687
 
688
					if (is_dir($from) && !file_exists($to)) {
689
						$old = umask(0);
690
						if (mkdir($to, $mode)) {
691
							umask($old);
692
							$old = umask(0);
693
							chmod($to, $mode);
694
							umask($old);
695
							$this->_messages[] = __d('cake_dev', '%s created', $to);
696
							$options = array_merge($options, array('to' => $to, 'from' => $from));
697
							$this->copy($options);
698
						} else {
699
							$this->_errors[] = __d('cake_dev', '%s not created', $to);
700
						}
701
					} elseif (is_dir($from) && $options['scheme'] == Folder::MERGE) {
702
						$options = array_merge($options, array('to' => $to, 'from' => $from));
703
						$this->copy($options);
704
					}
705
				}
706
			}
707
			closedir($handle);
708
		} else {
709
			return false;
710
		}
711
 
712
		if (!empty($this->_errors)) {
713
			return false;
714
		}
715
		return true;
716
	}
717
 
718
/**
719
 * Recursive directory move.
720
 *
721
 * ### Options
722
 *
723
 * - `to` The directory to copy to.
724
 * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
725
 * - `chmod` The mode to copy the files/directories with.
726
 * - `skip` Files/directories to skip.
727
 * - `scheme` Folder::MERGE, Folder::OVERWRITE, Folder::SKIP
728
 *
729
 * @param array $options (to, from, chmod, skip, scheme)
730
 * @return boolean Success
731
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::move
732
 */
733
	public function move($options) {
734
		$to = null;
735
		if (is_string($options)) {
736
			$to = $options;
737
			$options = (array)$options;
738
		}
739
		$options = array_merge(
740
			array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()),
741
			$options
742
		);
743
 
744
		if ($this->copy($options)) {
745
			if ($this->delete($options['from'])) {
746
				return (bool)$this->cd($options['to']);
747
			}
748
		}
749
		return false;
750
	}
751
 
752
/**
753
 * get messages from latest method
754
 *
755
 * @param boolean $reset Reset message stack after reading
756
 * @return array
757
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::messages
758
 */
759
	public function messages($reset = true) {
760
		$messages = $this->_messages;
761
		if ($reset) {
762
			$this->_messages = array();
763
		}
764
		return $messages;
765
	}
766
 
767
/**
768
 * get error from latest method
769
 *
770
 * @param boolean $reset Reset error stack after reading
771
 * @return array
772
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::errors
773
 */
774
	public function errors($reset = true) {
775
		$errors = $this->_errors;
776
		if ($reset) {
777
			$this->_errors = array();
778
		}
779
		return $errors;
780
	}
781
 
782
/**
783
 * Get the real path (taking ".." and such into account)
784
 *
785
 * @param string $path Path to resolve
786
 * @return string The resolved path
787
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::realpath
788
 */
789
	public function realpath($path) {
790
		$path = str_replace('/', DS, trim($path));
791
		if (strpos($path, '..') === false) {
792
			if (!Folder::isAbsolute($path)) {
793
				$path = Folder::addPathElement($this->path, $path);
794
			}
795
			return $path;
796
		}
797
		$parts = explode(DS, $path);
798
		$newparts = array();
799
		$newpath = '';
800
		if ($path[0] === DS) {
801
			$newpath = DS;
802
		}
803
 
804
		while (($part = array_shift($parts)) !== null) {
805
			if ($part === '.' || $part === '') {
806
				continue;
807
			}
808
			if ($part === '..') {
809
				if (!empty($newparts)) {
810
					array_pop($newparts);
811
					continue;
812
				}
813
				return false;
814
			}
815
			$newparts[] = $part;
816
		}
817
		$newpath .= implode(DS, $newparts);
818
 
819
		return Folder::slashTerm($newpath);
820
	}
821
 
822
/**
823
 * Returns true if given $path ends in a slash (i.e. is slash-terminated).
824
 *
825
 * @param string $path Path to check
826
 * @return boolean true if path ends with slash, false otherwise
827
 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isSlashTerm
828
 */
829
	public static function isSlashTerm($path) {
830
		$lastChar = $path[strlen($path) - 1];
831
		return $lastChar === '/' || $lastChar === '\\';
832
	}
833
 
834
}