Ticket #219: FileSystem.php
| File FileSystem.php, 23.5 KB (added by hans, 4 years ago) |
|---|
| Line | |
|---|---|
| 1 | <?php |
| 2 | |
| 3 | /* |
| 4 | * $Id: FileSystem.php 313 2007-11-17 04:20:58Z hans $ |
| 5 | * |
| 6 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 7 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 8 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 9 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 10 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 11 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 12 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 13 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 14 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 15 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 16 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 17 | * |
| 18 | * This software consists of voluntary contributions made by many individuals |
| 19 | * and is licensed under the LGPL. For more information please see |
| 20 | * <http://phing.info>. |
| 21 | */ |
| 22 | |
| 23 | namespace phing::system::io; |
| 24 | use phing::BuildException; |
| 25 | use phing::Phing; |
| 26 | |
| 27 | /** |
| 28 | * This is an abstract class for platform specific filesystem implementations |
| 29 | * you have to implement each method in the platform specific filesystem implementation |
| 30 | * classes Your local filesytem implementation must extend this class. |
| 31 | * You should also use this class as a template to write your local implementation |
| 32 | * Some native PHP filesystem specific methods are abstracted here as well. Anyway |
| 33 | * you _must_ always use this methods via a File object (that by nature uses the |
| 34 | * *FileSystem drivers to access the real filesystem via this class using natives. |
| 35 | * |
| 36 | * FIXME: |
| 37 | * - Error handling reduced to min fallthrough runtime excetions |
| 38 | * more precise errorhandling is done by the File class |
| 39 | * |
| 40 | * @author Charlie Killian <charlie@tizac.com> |
| 41 | * @author Hans Lellelid <hans@xmpl.org> |
| 42 | * @version $Revision: 1.11 $ |
| 43 | * @package phing.system.io |
| 44 | */ |
| 45 | abstract class FileSystem { |
| 46 | |
| 47 | /* properties for simple boolean attributes */ |
| 48 | const BA_EXISTS = 0x01; |
| 49 | const BA_REGULAR = 0x02; |
| 50 | const BA_DIRECTORY = 0x04; |
| 51 | const BA_HIDDEN = 0x08; |
| 52 | |
| 53 | /** |
| 54 | * Singleton instance for getFileSystem() method. |
| 55 | * @var FileSystem |
| 56 | */ |
| 57 | private static $fs; |
| 58 | |
| 59 | /** |
| 60 | * Static method to return the FileSystem singelton representing |
| 61 | * this platform's local filesystem driver. |
| 62 | * @return FileSystem |
| 63 | */ |
| 64 | public static function getFileSystem() { |
| 65 | if (self::$fs === null) { |
| 66 | switch(Phing::getProperty('host.fstype')) { |
| 67 | case 'UNIX': |
| 68 | self::$fs = new UnixFileSystem(); |
| 69 | break; |
| 70 | case 'WIN32': |
| 71 | self::$fs = new Win32FileSystem(); |
| 72 | break; |
| 73 | case 'WINNT': |
| 74 | self::$fs = new WinNTFileSystem(); |
| 75 | break; |
| 76 | default: |
| 77 | throw new Exception("Host uses unsupported filesystem, unable to proceed"); |
| 78 | } |
| 79 | } |
| 80 | return self::$fs; |
| 81 | } |
| 82 | |
| 83 | /* -- Normalization and construction -- */ |
| 84 | |
| 85 | /** |
| 86 | * Return the local filesystem's name-separator character. |
| 87 | */ |
| 88 | abstract function getSeparator(); |
| 89 | |
| 90 | /** |
| 91 | * Return the local filesystem's path-separator character. |
| 92 | */ |
| 93 | abstract function getPathSeparator(); |
| 94 | |
| 95 | /** |
| 96 | * Convert the given pathname string to normal form. If the string is |
| 97 | * already in normal form then it is simply returned. |
| 98 | */ |
| 99 | abstract function normalize($strPath); |
| 100 | |
| 101 | /** |
| 102 | * Compute the length of this pathname string's prefix. The pathname |
| 103 | * string must be in normal form. |
| 104 | */ |
| 105 | abstract function prefixLength($pathname); |
| 106 | |
| 107 | /** |
| 108 | * Resolve the child pathname string against the parent. |
| 109 | * Both strings must be in normal form, and the result |
| 110 | * will be a string in normal form. |
| 111 | */ |
| 112 | abstract function resolve($parent, $child); |
| 113 | |
| 114 | /** |
| 115 | * Resolve the given abstract pathname into absolute form. Invoked by the |
| 116 | * getAbsolutePath and getCanonicalPath methods in the File class. |
| 117 | */ |
| 118 | abstract function resolveFile(File $f); |
| 119 | |
| 120 | /** |
| 121 | * Return the parent pathname string to be used when the parent-directory |
| 122 | * argument in one of the two-argument File constructors is the empty |
| 123 | * pathname. |
| 124 | */ |
| 125 | abstract function getDefaultParent(); |
| 126 | |
| 127 | /** |
| 128 | * Post-process the given URI path string if necessary. This is used on |
| 129 | * win32, e.g., to transform "/c:/foo" into "c:/foo". The path string |
| 130 | * still has slash separators; code in the File class will translate them |
| 131 | * after this method returns. |
| 132 | */ |
| 133 | abstract function fromURIPath($path); |
| 134 | |
| 135 | /* -- Path operations -- */ |
| 136 | |
| 137 | /** |
| 138 | * Tell whether or not the given abstract pathname is absolute. |
| 139 | */ |
| 140 | abstract function isAbsolute(File $f); |
| 141 | |
| 142 | /** |
| 143 | * canonicalize filename by checking on disk |
| 144 | * @return mixed Canonical path or false if the file doesn't exist. |
| 145 | */ |
| 146 | function canonicalize($strPath) { |
| 147 | return @realpath($strPath); |
| 148 | } |
| 149 | |
| 150 | /* -- Attribute accessors -- */ |
| 151 | |
| 152 | /** |
| 153 | * Return the simple boolean attributes for the file or directory denoted |
| 154 | * by the given abstract pathname, or zero if it does not exist or some |
| 155 | * other I/O error occurs. |
| 156 | */ |
| 157 | function getBooleanAttributes($f) { |
| 158 | throw new Exception("SYSTEM ERROR method getBooleanAttributes() not implemented by fs driver"); |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Check whether the file or directory denoted by the given abstract |
| 163 | * pathname may be accessed by this process. If the second argument is |
| 164 | * false, then a check for read access is made; if the second |
| 165 | * argument is true, then a check for write (not read-write) |
| 166 | * access is made. Return false if access is denied or an I/O error |
| 167 | * occurs. |
| 168 | */ |
| 169 | function checkAccess(File $f, $write = false) { |
| 170 | // we clear stat cache, its expensive to look up from scratch, |
| 171 | // but we need to be sure |
| 172 | @clearstatcache(); |
| 173 | |
| 174 | |
| 175 | // Shouldn't this be $f->GetAbsolutePath() ? |
| 176 | // And why doesn't GetAbsolutePath() work? |
| 177 | |
| 178 | $strPath = (string) $f->getPath(); |
| 179 | |
| 180 | // FIXME |
| 181 | // if file object does denote a file that yet not existst |
| 182 | // path rights are checked |
| 183 | if (!@file_exists($strPath) && !is_dir($strPath)) { |
| 184 | $strPath = $f->getParent(); |
| 185 | if ($strPath === null || !is_dir($strPath)) { |
| 186 | $strPath = Phing::getProperty("user.dir"); |
| 187 | } |
| 188 | //$strPath = dirname($strPath); |
| 189 | } |
| 190 | |
| 191 | if (!$write) { |
| 192 | return (boolean) @is_readable($strPath); |
| 193 | } else { |
| 194 | return (boolean) @is_writable($strPath); |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Whether file can be deleted. |
| 200 | * @param File $f |
| 201 | * @return boolean |
| 202 | */ |
| 203 | function canDelete(File $f) |
| 204 | { |
| 205 | clearstatcache(); |
| 206 | $dir = dirname($f->getAbsolutePath()); |
| 207 | return (bool) @is_writable($dir); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Return the time at which the file or directory denoted by the given |
| 212 | * abstract pathname was last modified, or zero if it does not exist or |
| 213 | * some other I/O error occurs. |
| 214 | */ |
| 215 | function getLastModifiedTime(File $f) { |
| 216 | |
| 217 | if (!$f->exists()) { |
| 218 | return 0; |
| 219 | } |
| 220 | |
| 221 | @clearstatcache(); |
| 222 | $strPath = (string) $f->getPath(); |
| 223 | $mtime = @filemtime($strPath); |
| 224 | if (false === $mtime) { |
| 225 | // FAILED. Log and return err. |
| 226 | $msg = "FileSystem::Filemtime() FAILED. Cannot can not get modified time of $strPath. $php_errormsg"; |
| 227 | throw new Exception($msg); |
| 228 | } else { |
| 229 | return (int) $mtime; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | /** |
| 234 | * Return the length in bytes of the file denoted by the given abstract |
| 235 | * pathname, or zero if it does not exist, is a directory, or some other |
| 236 | * I/O error occurs. |
| 237 | */ |
| 238 | function getLength(File $f) { |
| 239 | $strPath = (string) $f->getAbsolutePath(); |
| 240 | $fs = filesize((string) $strPath); |
| 241 | if ($fs !== false) { |
| 242 | return $fs; |
| 243 | } else { |
| 244 | $msg = "FileSystem::Read() FAILED. Cannot get filesize of $strPath. $php_errormsg"; |
| 245 | throw new Exception($msg); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | /* -- File operations -- */ |
| 250 | |
| 251 | /** |
| 252 | * Create a new empty file with the given pathname. Return |
| 253 | * true if the file was created and false if a |
| 254 | * file or directory with the given pathname already exists. Throw an |
| 255 | * IOException if an I/O error occurs. |
| 256 | * |
| 257 | * @param string Path of the file to be created. |
| 258 | * |
| 259 | * @throws IOException |
| 260 | */ |
| 261 | function createNewFile($strPathname) { |
| 262 | if (@file_exists($strPathname)) |
| 263 | return false; |
| 264 | |
| 265 | // Create new file |
| 266 | $fp = @fopen($strPathname, "w"); |
| 267 | if ($fp === false) { |
| 268 | throw new IOException("The file \"$strPathname\" could not be created"); |
| 269 | } |
| 270 | @fclose($fp); |
| 271 | return true; |
| 272 | } |
| 273 | |
| 274 | /** |
| 275 | * Delete the file or directory denoted by the given abstract pathname, |
| 276 | * returning true if and only if the operation succeeds. |
| 277 | */ |
| 278 | function delete(File $f) { |
| 279 | if ($f->isDirectory()) { |
| 280 | return $this->rmdir($f->getPath()); |
| 281 | } else { |
| 282 | return $this->unlink($f->getPath()); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | /** |
| 287 | * Arrange for the file or directory denoted by the given abstract |
| 288 | * pathname to be deleted when Phing::shutdown is called, returning |
| 289 | * true if and only if the operation succeeds. |
| 290 | */ |
| 291 | function deleteOnExit($f) { |
| 292 | throw new Exception("deleteOnExit() not implemented by local fs driver"); |
| 293 | } |
| 294 | |
| 295 | /** |
| 296 | * List the elements of the directory denoted by the given abstract |
| 297 | * pathname. Return an array of strings naming the elements of the |
| 298 | * directory if successful; otherwise, return <code>null</code>. |
| 299 | */ |
| 300 | function listDir(File $f) { |
| 301 | $strPath = (string) $f->getAbsolutePath(); |
| 302 | $d = @dir($strPath); |
| 303 | if (!$d) { |
| 304 | return null; |
| 305 | } |
| 306 | $list = array(); |
| 307 | while($entry = $d->read()) { |
| 308 | if ($entry != "." && $entry != "..") { |
| 309 | array_push($list, $entry); |
| 310 | } |
| 311 | } |
| 312 | $d->close(); |
| 313 | unset($d); |
| 314 | return $list; |
| 315 | } |
| 316 | |
| 317 | /** |
| 318 | * Create a new directory denoted by the given abstract pathname, |
| 319 | * returning true if and only if the operation succeeds. |
| 320 | */ |
| 321 | function createDirectory(&$f) { |
| 322 | return @mkdir($f->getAbsolutePath(),0755); |
| 323 | } |
| 324 | |
| 325 | /** |
| 326 | * Rename the file or directory denoted by the first abstract pathname to |
| 327 | * the second abstract pathname, returning true if and only if |
| 328 | * the operation succeeds. |
| 329 | * |
| 330 | * @param File $f1 abstract source file |
| 331 | * @param File $f2 abstract destination file |
| 332 | * @return void |
| 333 | * @throws Exception if rename cannot be performed |
| 334 | */ |
| 335 | function rename(File $f1, File $f2) { |
| 336 | // get the canonical paths of the file to rename |
| 337 | $src = $f1->getAbsolutePath(); |
| 338 | $dest = $f2->getAbsolutePath(); |
| 339 | if (false === @rename($src, $dest)) { |
| 340 | $msg = "Rename FAILED. Cannot rename $src to $dest. $php_errormsg"; |
| 341 | throw new Exception($msg); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Set the last-modified time of the file or directory denoted by the |
| 347 | * given abstract pathname returning true if and only if the |
| 348 | * operation succeeds. |
| 349 | * @return void |
| 350 | * @throws Exception |
| 351 | */ |
| 352 | function setLastModifiedTime(File $f, $time) { |
| 353 | $path = $f->getPath(); |
| 354 | $success = @touch($path, $time); |
| 355 | if (!$success) { |
| 356 | throw new Exception("Could not create directory due to: $php_errormsg"); |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | /** |
| 361 | * Mark the file or directory denoted by the given abstract pathname as |
| 362 | * read-only, returning <code>true</code> if and only if the operation |
| 363 | * succeeds. |
| 364 | */ |
| 365 | function setReadOnly($f) { |
| 366 | throw new Exception("setReadonle() not implemented by local fs driver"); |
| 367 | } |
| 368 | |
| 369 | /* -- Filesystem interface -- */ |
| 370 | |
| 371 | /** |
| 372 | * List the available filesystem roots, return array of File objects |
| 373 | */ |
| 374 | function listRoots() { |
| 375 | throw new Exception("SYSTEM ERROR [listRoots() not implemented by local fs driver]"); |
| 376 | } |
| 377 | |
| 378 | /* -- Basic infrastructure -- */ |
| 379 | |
| 380 | /** |
| 381 | * Compare two abstract pathnames lexicographically. |
| 382 | */ |
| 383 | function compare($f1, $f2) { |
| 384 | throw new Exception("SYSTEM ERROR [compare() not implemented by local fs driver]"); |
| 385 | } |
| 386 | |
| 387 | /** |
| 388 | * Copy a file. |
| 389 | * |
| 390 | * @param File $src Source path and name file to copy. |
| 391 | * @param File $dest Destination path and name of new file. |
| 392 | * |
| 393 | * @return void |
| 394 | * @throws Exception if file cannot be copied. |
| 395 | */ |
| 396 | function copy(File $src, File $dest) { |
| 397 | global $php_errormsg; |
| 398 | $srcPath = $src->getAbsolutePath(); |
| 399 | $destPath = $dest->getAbsolutePath(); |
| 400 | |
| 401 | if (false === @copy($srcPath, $destPath)) { // Copy FAILED. Log and return err. |
| 402 | // Add error from php to end of log message. $php_errormsg. |
| 403 | $msg = "FileSystem::copy() FAILED. Cannot copy $srcPath to $destPath. $php_errormsg"; |
| 404 | throw new Exception($msg); |
| 405 | } |
| 406 | |
| 407 | try { |
| 408 | $dest->setMode($src->getMode()); |
| 409 | } catch(Exception $exc) { |
| 410 | // [MA] does chmod returns an error on systems that do not support it ? |
| 411 | // eat it up for now. |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | /** |
| 416 | * Change the permissions on a file or directory. |
| 417 | * |
| 418 | * @param pathname String. Path and name of file or directory. |
| 419 | * @param mode Int. The mode (permissions) of the file or |
| 420 | * directory. If using octal add leading 0. eg. 0777. |
| 421 | * Mode is affected by the umask system setting. |
| 422 | * |
| 423 | * @return void |
| 424 | * @throws Exception if operation failed. |
| 425 | */ |
| 426 | function chmod($pathname, $mode) { |
| 427 | $str_mode = decoct($mode); // Show octal in messages. |
| 428 | if (false === @chmod($pathname, $mode)) {// FAILED. |
| 429 | $msg = "FileSystem::chmod() FAILED. Cannot chmod $pathname. Mode $str_mode." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); |
| 430 | throw new Exception($msg); |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | |
| 435 | /** |
| 436 | * Change the ownership on a file or directory. |
| 437 | * |
| 438 | * @param pathname String. Path and name of file or directory. |
| 439 | * @param user String. The user of the file or |
| 440 | * directory.See http://us.php.net/chown |
| 441 | * |
| 442 | * @return void |
| 443 | * @throws Exception if operation failed. |
| 444 | */ |
| 445 | function chown($pathname, $user) { |
| 446 | |
| 447 | if (false === @chown($pathname, $user)) {// FAILED. |
| 448 | $msg = "FileSystem::chown() FAILED. Cannot chown $pathname. User $user." . (isset($php_errormsg) ? ' ' . $php_errormsg : ""); |
| 449 | throw new Exception($msg); |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | /** |
| 454 | * Locks a file and throws an Exception if this is not possible. |
| 455 | * @return void |
| 456 | * @throws Exception |
| 457 | */ |
| 458 | function lock(File $f) { |
| 459 | $filename = $f->getPath(); |
| 460 | $fp = @fopen($filename, "w"); |
| 461 | $result = @flock($fp, LOCK_EX); |
| 462 | @fclose($fp); |
| 463 | if (!$result) { |
| 464 | throw new Exception("Could not lock file '$filename'"); |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | /** |
| 469 | * Unlocks a file and throws an IO Error if this is not possible. |
| 470 | * |
| 471 | * @throws Exception |
| 472 | * @return void |
| 473 | */ |
| 474 | function unlock(File $f) { |
| 475 | $filename = $f->getPath(); |
| 476 | $fp = @fopen($filename, "w"); |
| 477 | $result = @flock($fp, LOCK_UN); |
| 478 | fclose($fp); |
| 479 | if (!$result) { |
| 480 | throw new Exception("Could not unlock file '$filename'"); |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | /** |
| 485 | * Delete a file. |
| 486 | * |
| 487 | * @param file String. Path and/or name of file to delete. |
| 488 | * |
| 489 | * @return void |
| 490 | * @throws Exception - if an error is encountered. |
| 491 | */ |
| 492 | function unlink($file) { |
| 493 | global $php_errormsg; |
| 494 | if (false === @unlink($file)) { |
| 495 | $msg = "FileSystem::unlink() FAILED. Cannot unlink '$file'. $php_errormsg"; |
| 496 | throw new Exception($msg); |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | /** |
| 501 | * Symbolically link a file to another name. |
| 502 | * |
| 503 | * Currently symlink is not implemented on Windows. Don't use if the application is to be portable. |
| 504 | * |
| 505 | * @param string $target Path and/or name of file to link. |
| 506 | * @param string $link Path and/or name of link to be created. |
| 507 | * @return void |
| 508 | */ |
| 509 | function symlink($target, $link) { |
| 510 | |
| 511 | // If Windows OS then symlink() will report it is not supported in |
| 512 | // the build. Use this error instead of checking for Windows as the OS. |
| 513 | |
| 514 | if (false === @symlink($target, $link)) { |
| 515 | // Add error from php to end of log message. $php_errormsg. |
| 516 | $msg = "FileSystem::Symlink() FAILED. Cannot symlink '$target' to '$link'. $php_errormsg"; |
| 517 | throw new Exception($msg); |
| 518 | } |
| 519 | |
| 520 | } |
| 521 | |
| 522 | /** |
| 523 | * Set the modification and access time on a file to the present time. |
| 524 | * |
| 525 | * @param string $file Path and/or name of file to touch. |
| 526 | * @param int $time |
| 527 | * @return void |
| 528 | */ |
| 529 | function touch($file, $time = null) { |
| 530 | global $php_errormsg; |
| 531 | |
| 532 | if (null === $time) { |
| 533 | $error = @touch($file); |
| 534 | } else { |
| 535 | $error = @touch($file, $time); |
| 536 | } |
| 537 | |
| 538 | if (false === $error) { // FAILED. |
| 539 | // Add error from php to end of log message. $php_errormsg. |
| 540 | $msg = "FileSystem::touch() FAILED. Cannot touch '$file'. $php_errormsg"; |
| 541 | throw new Exception($msg); |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | /** |
| 546 | * Delete an empty directory OR a directory and all of its contents. |
| 547 | * |
| 548 | * @param dir String. Path and/or name of directory to delete. |
| 549 | * @param children Boolean. False: don't delete directory contents. |
| 550 | * True: delete directory contents. |
| 551 | * |
| 552 | * @return void |
| 553 | */ |
| 554 | function rmdir($dir, $children = false) { |
| 555 | global $php_errormsg; |
| 556 | |
| 557 | // If children=FALSE only delete dir if empty. |
| 558 | if (false === $children) { |
| 559 | |
| 560 | if (false === @rmdir($dir)) { // FAILED. |
| 561 | // Add error from php to end of log message. $php_errormsg. |
| 562 | $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; |
| 563 | throw new Exception($msg); |
| 564 | } |
| 565 | |
| 566 | } else { // delete contents and dir. |
| 567 | |
| 568 | $handle = @opendir($dir); |
| 569 | |
| 570 | if (false === $handle) { // Error. |
| 571 | |
| 572 | $msg = "FileSystem::rmdir() FAILED. Cannot opendir() $dir. $php_errormsg"; |
| 573 | throw new Exception($msg); |
| 574 | |
| 575 | } else { // Read from handle. |
| 576 | |
| 577 | // Don't error on readdir(). |
| 578 | while (false !== ($entry = @readdir($handle))) { |
| 579 | |
| 580 | if ($entry != '.' && $entry != '..') { |
| 581 | |
| 582 | // Only add / if it isn't already the last char. |
| 583 | // This ONLY serves the purpose of making the Logger |
| 584 | // output look nice:) |
| 585 | |
| 586 | if (strpos(strrev($dir), DIRECTORY_SEPARATOR) === 0) {// there is a / |
| 587 | $next_entry = $dir . $entry; |
| 588 | } else { // no / |
| 589 | $next_entry = $dir . DIRECTORY_SEPARATOR . $entry; |
| 590 | } |
| 591 | |
| 592 | // NOTE: As of php 4.1.1 is_dir doesn't return FALSE it |
| 593 | // returns 0. So use == not ===. |
| 594 | |
| 595 | // Don't error on is_dir() |
| 596 | if (false == @is_dir($next_entry)) { // Is file. |
| 597 | |
| 598 | try { |
| 599 | self::unlink($next_entry); // Delete. |
| 600 | } catch (Exception $e) { |
| 601 | $msg = "FileSystem::Rmdir() FAILED. Cannot FileSystem::Unlink() $next_entry. ". $e->getMessage(); |
| 602 | throw new Exception($msg); |
| 603 | } |
| 604 | |
| 605 | } else { // Is directory. |
| 606 | |
| 607 | try { |
| 608 | self::rmdir($next_entry, true); // Delete |
| 609 | } catch (Exception $e) { |
| 610 | $msg = "FileSystem::rmdir() FAILED. Cannot FileSystem::rmdir() $next_entry. ". $e->getMessage(); |
| 611 | throw new Exception($msg); |
| 612 | } |
| 613 | |
| 614 | } // end is_dir else |
| 615 | } // end .. if |
| 616 | } // end while |
| 617 | } // end handle if |
| 618 | |
| 619 | // Don't error on closedir() |
| 620 | @closedir($handle); |
| 621 | |
| 622 | if (false === @rmdir($dir)) { // FAILED. |
| 623 | // Add error from php to end of log message. $php_errormsg. |
| 624 | $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; |
| 625 | throw new Exception($msg); |
| 626 | } |
| 627 | |
| 628 | } |
| 629 | |
| 630 | } |
| 631 | |
| 632 | /** |
| 633 | * Set the umask for file and directory creation. |
| 634 | * |
| 635 | * @param mode Int. Permissions ususally in ocatal. Use leading 0 for |
| 636 | * octal. Number between 0 and 0777. |
| 637 | * |
| 638 | * @return void |
| 639 | * @throws Exception if there is an error performing operation. |
| 640 | */ |
| 641 | function umask($mode) { |
| 642 | global $php_errormsg; |
| 643 | |
| 644 | // CONSIDERME: |
| 645 | // Throw a warning if mode is 0. PHP converts illegal octal numbers to |
| 646 | // 0 so 0 might not be what the user intended. |
| 647 | |
| 648 | $str_mode = decoct($mode); // Show octal in messages. |
| 649 | |
| 650 | if (false === @umask($mode)) { // FAILED. |
| 651 | // Add error from php to end of log message. $php_errormsg. |
| 652 | $msg = "FileSystem::Umask() FAILED. Value $mode. $php_errormsg"; |
| 653 | throw new Exception($msg); |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | /** |
| 658 | * Compare the modified time of two files. |
| 659 | * |
| 660 | * @param file1 String. Path and name of file1. |
| 661 | * @param file2 String. Path and name of file2. |
| 662 | * |
| 663 | * @return Int. 1 if file1 is newer. |
| 664 | * -1 if file2 is newer. |
| 665 | * 0 if files have the same time. |
| 666 | * Err object on failure. |
| 667 | * |
| 668 | * @throws Exception - if cannot get modified time of either file. |
| 669 | */ |
| 670 | function compareMTimes($file1, $file2) { |
| 671 | |
| 672 | $mtime1 = filemtime($file1); |
| 673 | $mtime2 = filemtime($file2); |
| 674 | |
| 675 | if ($mtime1 === false) { // FAILED. Log and return err. |
| 676 | // Add error from php to end of log message. $php_errormsg. |
| 677 | $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file1."; |
| 678 | throw new Exception($msg); |
| 679 | } elseif ($mtime2 === false) { // FAILED. Log and return err. |
| 680 | // Add error from php to end of log message. $php_errormsg. |
| 681 | $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file2."; |
| 682 | throw new Exception($msg); |
| 683 | } else { // Worked. Log and return compare. |
| 684 | // Compare mtimes. |
| 685 | if ($mtime1 == $mtime2) { |
| 686 | return 0; |
| 687 | } else { |
| 688 | return ($mtime1 < $mtime2) ? -1 : 1; |
| 689 | } // end compare |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | } |
