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