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