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: if (!class_exists('Object')) {
26: require LIBS . 'object.php';
27: }
28:
29: 30: 31: 32: 33: 34: 35:
36: class Folder extends Object {
37:
38: 39: 40: 41: 42: 43:
44: var $path = null;
45:
46: 47: 48: 49: 50: 51: 52:
53: var $sort = false;
54:
55: 56: 57: 58: 59: 60:
61: var $mode = 0755;
62:
63: 64: 65: 66: 67: 68:
69: var $__messages = array();
70:
71: 72: 73: 74: 75: 76:
77: var $__errors = false;
78:
79: 80: 81: 82: 83: 84:
85: var $__directories;
86:
87: 88: 89: 90: 91: 92:
93: var $__files;
94:
95: 96: 97: 98: 99: 100: 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: 124: 125: 126: 127:
128: function pwd() {
129: return $this->path;
130: }
131:
132: 133: 134: 135: 136: 137: 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: 149: 150: 151: 152: 153: 154: 155: 156: 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: 197: 198: 199: 200: 201: 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: 210: 211: 212: 213: 214: 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: 228: 229: 230: 231: 232: 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: 254: 255: 256: 257: 258: 259:
260: function isWindowsPath($path) {
261: return (preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
262: }
263:
264: 265: 266: 267: 268: 269: 270: 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: 278: 279: 280: 281: 282: 283:
284: function normalizePath($path) {
285: return Folder::correctSlashFor($path);
286: }
287:
288: 289: 290: 291: 292: 293: 294: 295:
296: function correctSlashFor($path) {
297: return (Folder::isWindowsPath($path)) ? '\\' : '/';
298: }
299:
300: 301: 302: 303: 304: 305: 306: 307:
308: function slashTerm($path) {
309: if (Folder::isSlashTerm($path)) {
310: return $path;
311: }
312: return $path . Folder::correctSlashFor($path);
313: }
314:
315: 316: 317: 318: 319: 320: 321: 322: 323:
324: function addPathElement($path, $element) {
325: return rtrim($path, DS) . DS . $element;
326: }
327:
328: 329: 330: 331: 332: 333: 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: 344: 345: 346: 347: 348: 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: 364: 365: 366: 367: 368: 369: 370: 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: 416: 417: 418: 419: 420: 421: 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: 458: 459: 460: 461: 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: 472: 473: 474: 475: 476: 477: 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: 514: 515: 516: 517: 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: 551: 552: 553: 554: 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: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 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: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 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: 717: 718: 719: 720:
721: function messages() {
722: return $this->__messages;
723: }
724:
725: 726: 727: 728: 729: 730:
731: function errors() {
732: return $this->__errors;
733: }
734:
735: 736: 737: 738: 739: 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: 777: 778: 779: 780: 781: 782:
783: function isSlashTerm($path) {
784: $lastChar = $path[strlen($path) - 1];
785: return $lastChar === '/' || $lastChar === '\\';
786: }
787: }
788: