home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / phing / system / io / FileSystem.php < prev    next >
Encoding:
PHP Script  |  2007-10-20  |  22.8 KB  |  671 lines

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