pictcode / lib / Cake / Utility / Folder.php @ 40928d1c
履歴 | 表示 | アノテート | ダウンロード (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 | } |