1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
24: 25: 26: 27:
28: if (!class_exists('Object')) {
29: uses('object');
30: }
31: 32: 33: 34: 35: 36: 37: 38:
39: class Folder extends Object {
40: 41: 42: 43: 44: 45:
46: var $path = null;
47: 48: 49: 50: 51: 52:
53: var $sort = false;
54: 55: 56: 57: 58: 59:
60: var $mode = 0755;
61: 62: 63: 64: 65: 66:
67: var $__messages = array();
68: 69: 70: 71: 72: 73:
74: var $__errors = false;
75: 76: 77: 78: 79: 80:
81: var $__directories;
82: 83: 84: 85: 86: 87:
88: var $__files;
89: 90: 91: 92: 93: 94: 95:
96: function __construct($path = false, $create = false, $mode = false) {
97: parent::__construct();
98: if (empty($path)) {
99: $path = TMP;
100: }
101: if ($mode) {
102: $this->mode = $mode;
103: }
104:
105: if (!file_exists($path) && $create === true) {
106: $this->create($path, $this->mode);
107: }
108: if (!Folder::isAbsolute($path)) {
109: $path = realpath($path);
110: }
111: if (!empty($path)) {
112: $this->cd($path);
113: }
114: }
115: 116: 117: 118: 119: 120:
121: function pwd() {
122: return $this->path;
123: }
124: 125: 126: 127: 128: 129: 130:
131: function cd($path) {
132: $path = $this->realpath($path);
133: if (is_dir($path)) {
134: return $this->path = $path;
135: }
136: return false;
137: }
138: 139: 140: 141: 142: 143: 144: 145: 146: 147:
148: function read($sort = true, $exceptions = false, $fullPath = false) {
149: $dirs = $files = array();
150:
151: if (is_array($exceptions)) {
152: $exceptions = array_flip($exceptions);
153: }
154: $skipHidden = isset($exceptions['.']) || $exceptions === true;
155:
156: if (false === ($dir = @opendir($this->path))) {
157: return array($dirs, $files);
158: }
159:
160: while (false !== ($item = readdir($dir))) {
161: if ($item === '.' || $item === '..' || ($skipHidden && $item[0] === '.') || isset($exceptions[$item])) {
162: continue;
163: }
164:
165: $path = Folder::addPathElement($this->path, $item);
166: if (is_dir($path)) {
167: $dirs[] = $fullPath ? $path : $item;
168: } else {
169: $files[] = $fullPath ? $path : $item;
170: }
171: }
172:
173: if ($sort || $this->sort) {
174: sort($dirs);
175: sort($files);
176: }
177:
178: closedir($dir);
179: return array($dirs, $files);
180: }
181: 182: 183: 184: 185: 186: 187:
188: function find($regexpPattern = '.*', $sort = false) {
189: list($dirs, $files) = $this->read($sort);
190: return array_values(preg_grep('/^' . $regexpPattern . '$/i', $files)); ;
191: }
192: 193: 194: 195: 196: 197: 198:
199: function findRecursive($pattern = '.*', $sort = false) {
200: $startsOn = $this->path;
201: $out = $this->_findRecursive($pattern, $sort);
202: $this->cd($startsOn);
203: return $out;
204: }
205: 206: 207: 208: 209: 210: 211:
212: function _findRecursive($pattern, $sort = false) {
213: list($dirs, $files) = $this->read($sort);
214: $found = array();
215:
216: foreach ($files as $file) {
217: if (preg_match('/^' . $pattern . '$/i', $file)) {
218: $found[] = Folder::addPathElement($this->path, $file);
219: }
220: }
221: $start = $this->path;
222:
223: foreach ($dirs as $dir) {
224: $this->cd(Folder::addPathElement($start, $dir));
225: $found = array_merge($found, $this->findRecursive($pattern, $sort));
226: }
227: return $found;
228: }
229: 230: 231: 232: 233: 234: 235: 236:
237: function isWindowsPath($path) {
238: return (preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
239: }
240: 241: 242: 243: 244: 245: 246: 247:
248: function isAbsolute($path) {
249: return !empty($path) && ($path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
250: }
251: 252: 253: 254: 255: 256: 257: 258:
259: function normalizePath($path) {
260: return Folder::correctSlashFor($path);
261: }
262: 263: 264: 265: 266: 267: 268: 269:
270: function correctSlashFor($path) {
271: if (Folder::isWindowsPath($path)) {
272: return '\\';
273: }
274: return '/';
275: }
276: 277: 278: 279: 280: 281: 282: 283:
284: function slashTerm($path) {
285: if (Folder::isSlashTerm($path)) {
286: return $path;
287: }
288: return $path . Folder::correctSlashFor($path);
289: }
290: 291: 292: 293: 294: 295: 296: 297: 298:
299: function addPathElement($path, $element) {
300: return rtrim($path, DS) . DS . $element;
301: }
302: 303: 304: 305: 306: 307:
308: function inCakePath($path = '') {
309: $dir = substr(Folder::slashTerm(ROOT), 0, -1);
310: $newdir = $dir . $path;
311:
312: return $this->inPath($newdir);
313: }
314: 315: 316: 317: 318: 319:
320: function inPath($path = '', $reverse = false) {
321: $dir = Folder::slashTerm($path);
322: $current = Folder::slashTerm($this->pwd());
323:
324: if (!$reverse) {
325: $return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
326: } else {
327: $return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
328: }
329: if ($return == 1) {
330: return true;
331: } else {
332: return false;
333: }
334: }
335: 336: 337: 338: 339: 340: 341: 342: 343: 344:
345: function chmod($path, $mode = false, $recursive = true, $exceptions = array()) {
346: if (!$mode) {
347: $mode = $this->mode;
348: }
349:
350: if ($recursive === false && is_dir($path)) {
351: if (@chmod($path, intval($mode, 8))) {
352: $this->__messages[] = sprintf(__('%s changed to %s', true), $path, $mode);
353: return true;
354: }
355:
356: $this->__errors[] = sprintf(__('%s NOT changed to %s', true), $path, $mode);
357: return false;
358: }
359:
360: if (is_dir($path)) {
361: $paths = $this->tree($path);
362:
363: foreach ($paths as $type) {
364: foreach ($type as $key => $fullpath) {
365: $check = explode(DS, $fullpath);
366: $count = count($check);
367:
368: if (in_array($check[$count - 1], $exceptions)) {
369: continue;
370: }
371:
372: if (@chmod($fullpath, intval($mode, 8))) {
373: $this->__messages[] = sprintf(__('%s changed to %s', true), $fullpath, $mode);
374: } else {
375: $this->__errors[] = sprintf(__('%s NOT changed to %s', true), $fullpath, $mode);
376: }
377: }
378: }
379:
380: if (empty($this->__errors)) {
381: return true;
382: }
383: }
384: return false;
385: }
386: 387: 388: 389: 390: 391: 392: 393: 394:
395: function tree($path, $exceptions = true, $type = null) {
396: $original = $this->path;
397: $path = rtrim($path, DS);
398: $this->__files = array();
399: $this->__directories = array($path);
400: $directories = array();
401:
402: if ($exceptions === false) {
403: $exceptions = true;
404: }
405: while (count($this->__directories)) {
406: $dir = array_pop($this->__directories);
407: $this->__tree($dir, $exceptions);
408: $directories[] = $dir;
409: }
410:
411: if ($type === null) {
412: return array($directories, $this->__files);
413: }
414: if ($type === 'dir') {
415: return $directories;
416: }
417: $this->cd($original);
418:
419: return $this->__files;
420: }
421: 422: 423: 424: 425: 426: 427:
428: function __tree($path, $exceptions) {
429: if ($this->cd($path)) {
430: list($dirs, $files) = $this->read(false, $exceptions, true);
431: $this->__directories = array_merge($this->__directories, $dirs);
432: $this->__files = array_merge($this->__files, $files);
433: }
434: }
435: 436: 437: 438: 439: 440: 441: 442:
443: function create($pathname, $mode = false) {
444: if (is_dir($pathname) || empty($pathname)) {
445: return true;
446: }
447:
448: if (!$mode) {
449: $mode = $this->mode;
450: }
451:
452: if (is_file($pathname)) {
453: $this->__errors[] = sprintf(__('%s is a file', true), $pathname);
454: return false;
455: }
456: $nextPathname = substr($pathname, 0, strrpos($pathname, DS));
457:
458: if ($this->create($nextPathname, $mode)) {
459: if (!file_exists($pathname)) {
460: $old = umask(0);
461: if (mkdir($pathname, $mode)) {
462: umask($old);
463: $this->__messages[] = sprintf(__('%s created', true), $pathname);
464: return true;
465: } else {
466: umask($old);
467: $this->__errors[] = sprintf(__('%s NOT created', true), $pathname);
468: return false;
469: }
470: }
471: }
472: return false;
473: }
474: 475: 476: 477: 478: 479: 480:
481: function dirsize() {
482: $size = 0;
483: $directory = Folder::slashTerm($this->path);
484: $stack = array($directory);
485: $count = count($stack);
486: for ($i = 0, $j = $count; $i < $j; ++$i) {
487: if (is_file($stack[$i])) {
488: $size += filesize($stack[$i]);
489: } elseif (is_dir($stack[$i])) {
490: $dir = dir($stack[$i]);
491: if ($dir) {
492: while (false !== ($entry = $dir->read())) {
493: if ($entry === '.' || $entry === '..') {
494: continue;
495: }
496: $add = $stack[$i] . $entry;
497:
498: if (is_dir($stack[$i] . $entry)) {
499: $add = Folder::slashTerm($add);
500: }
501: $stack[] = $add;
502: }
503: $dir->close();
504: }
505: }
506: $j = count($stack);
507: }
508: return $size;
509: }
510: 511: 512: 513: 514: 515: 516:
517: function delete($path = null) {
518: if (!$path) {
519: $path = $this->pwd();
520: }
521: $path = Folder::slashTerm($path);
522: if (is_dir($path) === true) {
523: $normalFiles = glob($path . '*');
524: $hiddenFiles = glob($path . '\.?*');
525:
526: $normalFiles = $normalFiles ? $normalFiles : array();
527: $hiddenFiles = $hiddenFiles ? $hiddenFiles : array();
528:
529: $files = array_merge($normalFiles, $hiddenFiles);
530: if (is_array($files)) {
531: foreach ($files as $file) {
532: if (preg_match('/(\.|\.\.)$/', $file)) {
533: continue;
534: }
535: if (is_file($file) === true) {
536: if (@unlink($file)) {
537: $this->__messages[] = sprintf(__('%s removed', true), $file);
538: } else {
539: $this->__errors[] = sprintf(__('%s NOT removed', true), $file);
540: }
541: } elseif (is_dir($file) === true && $this->delete($file) === false) {
542: return false;
543: }
544: }
545: }
546: $path = substr($path, 0, strlen($path) - 1);
547: if (rmdir($path) === false) {
548: $this->__errors[] = sprintf(__('%s NOT removed', true), $path);
549: return false;
550: } else {
551: $this->__messages[] = sprintf(__('%s removed', true), $path);
552: }
553: }
554: return true;
555: }
556: 557: 558: 559: 560: 561: 562:
563: function copy($options = array()) {
564: $to = null;
565: if (is_string($options)) {
566: $to = $options;
567: $options = array();
568: }
569: $options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
570:
571: $fromDir = $options['from'];
572: $toDir = $options['to'];
573: $mode = $options['mode'];
574:
575: if (!$this->cd($fromDir)) {
576: $this->__errors[] = sprintf(__('%s not found', true), $fromDir);
577: return false;
578: }
579:
580: if (!is_dir($toDir)) {
581: $this->mkdir($toDir, $mode);
582: }
583:
584: if (!is_writable($toDir)) {
585: $this->__errors[] = sprintf(__('%s not writable', true), $toDir);
586: return false;
587: }
588:
589: $exceptions = array_merge(array('.', '..', '.svn'), $options['skip']);
590: if ($handle = @opendir($fromDir)) {
591: while (false !== ($item = readdir($handle))) {
592: if (!in_array($item, $exceptions)) {
593: $from = Folder::addPathElement($fromDir, $item);
594: $to = Folder::addPathElement($toDir, $item);
595: if (is_file($from)) {
596: if (copy($from, $to)) {
597: chmod($to, intval($mode, 8));
598: touch($to, filemtime($from));
599: $this->__messages[] = sprintf(__('%s copied to %s', true), $from, $to);
600: } else {
601: $this->__errors[] = sprintf(__('%s NOT copied to %s', true), $from, $to);
602: }
603: }
604:
605: if (is_dir($from) && !file_exists($to)) {
606: $old = umask(0);
607: if (mkdir($to, $mode)) {
608: umask($old);
609: $old = umask(0);
610: chmod($to, $mode);
611: umask($old);
612: $this->__messages[] = sprintf(__('%s created', true), $to);
613: $options = array_merge($options, array('to'=> $to, 'from'=> $from));
614: $this->copy($options);
615: } else {
616: $this->__errors[] = sprintf(__('%s not created', true), $to);
617: }
618: }
619: }
620: }
621: closedir($handle);
622: } else {
623: return false;
624: }
625:
626: if (!empty($this->__errors)) {
627: return false;
628: }
629: return true;
630: }
631: 632: 633: 634: 635: 636: 637:
638: function move($options) {
639: $to = null;
640: if (is_string($options)) {
641: $to = $options;
642: $options = (array)$options;
643: }
644: $options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
645:
646: if ($this->copy($options)) {
647: if ($this->delete($options['from'])) {
648: return $this->cd($options['to']);
649: }
650: }
651: return false;
652: }
653: 654: 655: 656: 657: 658:
659: function messages() {
660: return $this->__messages;
661: }
662: 663: 664: 665: 666: 667:
668: function errors() {
669: return $this->__errors;
670: }
671: 672: 673: 674: 675: 676:
677: function ls($sort = true, $exceptions = false) {
678: return $this->read($sort, $exceptions);
679: }
680: 681: 682: 683: 684: 685:
686: function mkdir($pathname, $mode = 0755) {
687: return $this->create($pathname, $mode);
688: }
689: 690: 691: 692: 693: 694:
695: function cp($options) {
696: return $this->copy($options);
697: }
698: 699: 700: 701: 702: 703:
704: function mv($options) {
705: return $this->move($options);
706: }
707: 708: 709: 710: 711: 712:
713: function rm($path) {
714: return $this->delete($path);
715: }
716: 717: 718: 719: 720: 721:
722: function realpath($path) {
723: $path = str_replace('/', DS, trim($path));
724: if (strpos($path, '..') === false) {
725: if (!Folder::isAbsolute($path)) {
726: $path = Folder::addPathElement($this->path, $path);
727: }
728: return $path;
729: }
730: $parts = explode(DS, $path);
731: $newparts = array();
732: $newpath = '';
733: if ($path[0] === DS) {
734: $newpath = DS;
735: }
736:
737: while (($part = array_shift($parts)) !== NULL) {
738: if ($part === '.' || $part === '') {
739: continue;
740: }
741: if ($part === '..') {
742: if (!empty($newparts)) {
743: array_pop($newparts);
744: continue;
745: } else {
746: return false;
747: }
748: }
749: $newparts[] = $part;
750: }
751: $newpath .= implode(DS, $newparts);
752:
753: return Folder::slashTerm($newpath);
754: }
755: 756: 757: 758: 759: 760: 761: 762:
763: function isSlashTerm($path) {
764: $lastChar = $path[strlen($path) - 1];
765: return $lastChar === '/' || $lastChar === '\\';
766: }
767: }
768: ?>
769: