Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/mvc/components/cakeFolder

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