FileSystem.php 26 KB

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