Spaces:
Sleeping
Sleeping
| /* | |
| * $Id: FileSystem.php 3076 2006-12-18 08:52:12Z fabien $ | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * This software consists of voluntary contributions made by many individuals | |
| * and is licensed under the LGPL. For more information please see | |
| * <http://phing.info>. | |
| */ | |
| /** | |
| * This is an abstract class for platform specific filesystem implementations | |
| * you have to implement each method in the platform specific filesystem implementation | |
| * classes Your local filesytem implementation must extend this class. | |
| * You should also use this class as a template to write your local implementation | |
| * Some native PHP filesystem specific methods are abstracted here as well. Anyway | |
| * you _must_ always use this methods via a PhingFile object (that by nature uses the | |
| * *FileSystem drivers to access the real filesystem via this class using natives. | |
| * | |
| * FIXME: | |
| * - Error handling reduced to min fallthrough runtime excetions | |
| * more precise errorhandling is done by the PhingFile class | |
| * | |
| * @author Charlie Killian <charlie@tizac.com> | |
| * @author Hans Lellelid <hans@xmpl.org> | |
| * @version $Revision: 1.11 $ | |
| * @package phing.system.io | |
| */ | |
| abstract class FileSystem { | |
| /* properties for simple boolean attributes */ | |
| const BA_EXISTS = 0x01; | |
| const BA_REGULAR = 0x02; | |
| const BA_DIRECTORY = 0x04; | |
| const BA_HIDDEN = 0x08; | |
| /** Instance for getFileSystem() method. */ | |
| private static $fs; | |
| /** | |
| * Static method to return the FileSystem singelton representing | |
| * this platform's local filesystem driver. | |
| */ | |
| public static function getFileSystem() { | |
| if (self::$fs === null) { | |
| switch(Phing::getProperty('host.fstype')) { | |
| case 'UNIX': | |
| include_once 'phing/system/io/UnixFileSystem.php'; | |
| self::$fs = new UnixFileSystem(); | |
| break; | |
| case 'WIN32': | |
| include_once 'phing/system/io/Win32FileSystem.php'; | |
| self::$fs = new Win32FileSystem(); | |
| break; | |
| case 'WINNT': | |
| include_once 'phing/system/io/WinNTFileSystem.php'; | |
| self::$fs = new WinNTFileSystem(); | |
| break; | |
| default: | |
| throw new Exception("Host uses unsupported filesystem, unable to proceed"); | |
| } | |
| } | |
| return self::$fs; | |
| } | |
| /* -- Normalization and construction -- */ | |
| /** | |
| * Return the local filesystem's name-separator character. | |
| */ | |
| abstract function getSeparator(); | |
| /** | |
| * Return the local filesystem's path-separator character. | |
| */ | |
| abstract function getPathSeparator(); | |
| /** | |
| * Convert the given pathname string to normal form. If the string is | |
| * already in normal form then it is simply returned. | |
| */ | |
| abstract function normalize($strPath); | |
| /** | |
| * Compute the length of this pathname string's prefix. The pathname | |
| * string must be in normal form. | |
| */ | |
| abstract function prefixLength($pathname); | |
| /** | |
| * Resolve the child pathname string against the parent. | |
| * Both strings must be in normal form, and the result | |
| * will be a string in normal form. | |
| */ | |
| abstract function resolve($parent, $child); | |
| /** | |
| * Resolve the given abstract pathname into absolute form. Invoked by the | |
| * getAbsolutePath and getCanonicalPath methods in the PhingFile class. | |
| */ | |
| abstract function resolveFile(PhingFile $f); | |
| /** | |
| * Return the parent pathname string to be used when the parent-directory | |
| * argument in one of the two-argument PhingFile constructors is the empty | |
| * pathname. | |
| */ | |
| abstract function getDefaultParent(); | |
| /** | |
| * Post-process the given URI path string if necessary. This is used on | |
| * win32, e.g., to transform "/c:/foo" into "c:/foo". The path string | |
| * still has slash separators; code in the PhingFile class will translate them | |
| * after this method returns. | |
| */ | |
| abstract function fromURIPath($path); | |
| /* -- Path operations -- */ | |
| /** | |
| * Tell whether or not the given abstract pathname is absolute. | |
| */ | |
| abstract function isAbsolute(PhingFile $f); | |
| /** | |
| * canonicalize filename by checking on disk | |
| * @return mixed Canonical path or false if the file doesn't exist. | |
| */ | |
| function canonicalize($strPath) { | |
| return @realpath($strPath); | |
| } | |
| /* -- Attribute accessors -- */ | |
| /** | |
| * Return the simple boolean attributes for the file or directory denoted | |
| * by the given abstract pathname, or zero if it does not exist or some | |
| * other I/O error occurs. | |
| */ | |
| function getBooleanAttributes(&$f = null) { | |
| throw new Exception("SYSTEM ERROR method getBooleanAttributes() not implemented by fs driver"); | |
| } | |
| /** | |
| * Check whether the file or directory denoted by the given abstract | |
| * pathname may be accessed by this process. If the second argument is | |
| * false, then a check for read access is made; if the second | |
| * argument is true, then a check for write (not read-write) | |
| * access is made. Return false if access is denied or an I/O error | |
| * occurs. | |
| */ | |
| function checkAccess(PhingFile $f, $write = false) { | |
| // we clear stat cache, its expensive to look up from scratch, | |
| // but we need to be sure | |
| @clearstatcache(); | |
| // Shouldn't this be $f->GetAbsolutePath() ? | |
| // And why doesn't GetAbsolutePath() work? | |
| $strPath = (string) $f->getPath(); | |
| // FIXME | |
| // if file object does denote a file that yet not existst | |
| // path rights are checked | |
| if (!@file_exists($strPath) && !is_dir($strPath)) { | |
| $strPath = $f->getParent(); | |
| if ($strPath === null || !is_dir($strPath)) { | |
| $strPath = Phing::getProperty("user.dir"); | |
| } | |
| //$strPath = dirname($strPath); | |
| } | |
| if (!$write) { | |
| return (boolean) @is_readable($strPath); | |
| } else { | |
| return (boolean) @is_writable($strPath); | |
| } | |
| } | |
| /** | |
| * Return the time at which the file or directory denoted by the given | |
| * abstract pathname was last modified, or zero if it does not exist or | |
| * some other I/O error occurs. | |
| */ | |
| function getLastModifiedTime(PhingFile $f) { | |
| if (!$f->exists()) { | |
| return 0; | |
| } | |
| @clearstatcache(); | |
| $strPath = (string) $f->getPath(); | |
| $mtime = @filemtime($strPath); | |
| if (false === $mtime) { | |
| // FAILED. Log and return err. | |
| $msg = "FileSystem::Filemtime() FAILED. Cannot can not get modified time of $strPath. $php_errormsg"; | |
| throw new Exception($msg); | |
| } else { | |
| return (int) $mtime; | |
| } | |
| } | |
| /** | |
| * Return the length in bytes of the file denoted by the given abstract | |
| * pathname, or zero if it does not exist, is a directory, or some other | |
| * I/O error occurs. | |
| */ | |
| function getLength(PhingFile $f) { | |
| $strPath = (string) $f->getAbsolutePath(); | |
| $fs = filesize((string) $strPath); | |
| if ($fs !== false) { | |
| return $fs; | |
| } else { | |
| $msg = "FileSystem::Read() FAILED. Cannot get filesize of $strPath. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| /* -- File operations -- */ | |
| /** | |
| * Create a new empty file with the given pathname. Return | |
| * true if the file was created and false if a | |
| * file or directory with the given pathname already exists. Throw an | |
| * IOException if an I/O error occurs. | |
| * | |
| * @param string Path of the file to be created. | |
| * | |
| * @throws IOException | |
| */ | |
| function createNewFile($strPathname) { | |
| if (@file_exists($strPathname)) | |
| return false; | |
| // Create new file | |
| $fp = @fopen($strPathname, "w"); | |
| if ($fp === false) { | |
| throw new IOException("The file \"$strPathname\" could not be created"); | |
| } | |
| @fclose($fp); | |
| return true; | |
| } | |
| /** | |
| * Delete the file or directory denoted by the given abstract pathname, | |
| * returning true if and only if the operation succeeds. | |
| */ | |
| function delete(PhingFile $f) { | |
| if ($f->isDirectory()) { | |
| return $this->rmdir($f->getPath()); | |
| } else { | |
| return $this->unlink($f->getPath()); | |
| } | |
| } | |
| /** | |
| * Arrange for the file or directory denoted by the given abstract | |
| * pathname to be deleted when Phing::shutdown is called, returning | |
| * true if and only if the operation succeeds. | |
| */ | |
| function deleteOnExit($f) { | |
| throw new Exception("deleteOnExit() not implemented by local fs driver"); | |
| } | |
| /** | |
| * List the elements of the directory denoted by the given abstract | |
| * pathname. Return an array of strings naming the elements of the | |
| * directory if successful; otherwise, return <code>null</code>. | |
| */ | |
| function listDir(PhingFile $f) { | |
| $strPath = (string) $f->getAbsolutePath(); | |
| $d = @dir($strPath); | |
| if (!$d) { | |
| return null; | |
| } | |
| $list = array(); | |
| while($entry = $d->read()) { | |
| if ($entry != "." && $entry != "..") { | |
| array_push($list, $entry); | |
| } | |
| } | |
| $d->close(); | |
| unset($d); | |
| return $list; | |
| } | |
| /** | |
| * Create a new directory denoted by the given abstract pathname, | |
| * returning true if and only if the operation succeeds. | |
| */ | |
| function createDirectory(&$f) { | |
| return @mkdir($f->getAbsolutePath(),0755); | |
| } | |
| /** | |
| * Rename the file or directory denoted by the first abstract pathname to | |
| * the second abstract pathname, returning true if and only if | |
| * the operation succeeds. | |
| * | |
| * @param PhingFile $f1 abstract source file | |
| * @param PhingFile $f2 abstract destination file | |
| * @return void | |
| * @throws Exception if rename cannot be performed | |
| */ | |
| function rename(PhingFile $f1, PhingFile $f2) { | |
| // get the canonical paths of the file to rename | |
| $src = $f1->getAbsolutePath(); | |
| $dest = $f2->getAbsolutePath(); | |
| if (false === @rename($src, $dest)) { | |
| $msg = "Rename FAILED. Cannot rename $src to $dest. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| /** | |
| * Set the last-modified time of the file or directory denoted by the | |
| * given abstract pathname returning true if and only if the | |
| * operation succeeds. | |
| * @return void | |
| * @throws Exception | |
| */ | |
| function setLastModifiedTime(PhingFile $f, $time) { | |
| $path = $f->getPath(); | |
| $success = @touch($path, $time); | |
| if (!$success) { | |
| throw new Exception("Could not create directory due to: $php_errormsg"); | |
| } | |
| } | |
| /** | |
| * Mark the file or directory denoted by the given abstract pathname as | |
| * read-only, returning <code>true</code> if and only if the operation | |
| * succeeds. | |
| */ | |
| function setReadOnly($f) { | |
| throw new Exception("setReadonle() not implemented by local fs driver"); | |
| } | |
| /* -- Filesystem interface -- */ | |
| /** | |
| * List the available filesystem roots, return array of PhingFile objects | |
| */ | |
| function listRoots() { | |
| throw new Exception("SYSTEM ERROR [listRoots() not implemented by local fs driver]"); | |
| } | |
| /* -- Basic infrastructure -- */ | |
| /** | |
| * Compare two abstract pathnames lexicographically. | |
| */ | |
| function compare($f1, $f2) { | |
| throw new Exception("SYSTEM ERROR [compare() not implemented by local fs driver]"); | |
| } | |
| /** | |
| * Copy a file. | |
| * | |
| * @param PhingFile $src Source path and name file to copy. | |
| * @param PhingFile $dest Destination path and name of new file. | |
| * | |
| * @return void | |
| * @throws Exception if file cannot be copied. | |
| */ | |
| function copy(PhingFile $src, PhingFile $dest) { | |
| global $php_errormsg; | |
| $srcPath = $src->getAbsolutePath(); | |
| $destPath = $dest->getAbsolutePath(); | |
| if (false === @copy($srcPath, $destPath)) { // Copy FAILED. Log and return err. | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::copy() FAILED. Cannot copy $srcPath to $destPath. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| try { | |
| $dest->setMode($src->getMode()); | |
| } catch(Exception $exc) { | |
| // [MA] does chmod returns an error on systems that do not support it ? | |
| // eat it up for now. | |
| } | |
| } | |
| /** | |
| * Change the permissions on a file or directory. | |
| * | |
| * @param pathname String. Path and name of file or directory. | |
| * @param mode Int. The mode (permissions) of the file or | |
| * directory. If using octal add leading 0. eg. 0777. | |
| * Mode is affected by the umask system setting. | |
| * | |
| * @return void | |
| * @throws Exception if operation failed. | |
| */ | |
| function chmod($pathname, $mode) { | |
| $str_mode = decoct($mode); // Show octal in messages. | |
| if (false === @chmod($pathname, $mode)) {// FAILED. | |
| $msg = "FileSystem::chmod() FAILED. Cannot chmod $pathname. Mode $str_mode. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| /** | |
| * Locks a file and throws an Exception if this is not possible. | |
| * @return void | |
| * @throws Exception | |
| */ | |
| function lock(PhingFile $f) { | |
| $filename = $f->getPath(); | |
| $fp = @fopen($filename, "w"); | |
| $result = @flock($fp, LOCK_EX); | |
| @fclose($fp); | |
| if (!$result) { | |
| throw new Exception("Could not lock file '$filename'"); | |
| } | |
| } | |
| /** | |
| * Unlocks a file and throws an IO Error if this is not possible. | |
| * | |
| * @throws Exception | |
| * @return void | |
| */ | |
| function unlock(PhingFile $f) { | |
| $filename = $f->getPath(); | |
| $fp = @fopen($filename, "w"); | |
| $result = @flock($fp, LOCK_UN); | |
| fclose($fp); | |
| if (!$result) { | |
| throw new Exception("Could not unlock file '$filename'"); | |
| } | |
| } | |
| /** | |
| * Delete a file. | |
| * | |
| * @param file String. Path and/or name of file to delete. | |
| * | |
| * @return void | |
| * @throws Exception - if an error is encountered. | |
| */ | |
| function unlink($file) { | |
| global $php_errormsg; | |
| if (false === @unlink($file)) { | |
| $msg = "FileSystem::unlink() FAILED. Cannot unlink '$file'. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| /** | |
| * Symbolically link a file to another name. | |
| * | |
| * Currently symlink is not implemented on Windows. Don't use if the application is to be portable. | |
| * | |
| * @param string $target Path and/or name of file to link. | |
| * @param string $link Path and/or name of link to be created. | |
| * @return void | |
| */ | |
| function symlink($target, $link) { | |
| // If Windows OS then symlink() will report it is not supported in | |
| // the build. Use this error instead of checking for Windows as the OS. | |
| if (false === @symlink($target, $link)) { | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::Symlink() FAILED. Cannot symlink '$target' to '$link'. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| /** | |
| * Set the modification and access time on a file to the present time. | |
| * | |
| * @param string $file Path and/or name of file to touch. | |
| * @param int $time | |
| * @return void | |
| */ | |
| function touch($file, $time = null) { | |
| global $php_errormsg; | |
| if (null === $time) { | |
| $error = @touch($file); | |
| } else { | |
| $error = @touch($file, $time); | |
| } | |
| if (false === $error) { // FAILED. | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::touch() FAILED. Cannot touch '$file'. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| /** | |
| * Delete an empty directory OR a directory and all of its contents. | |
| * | |
| * @param dir String. Path and/or name of directory to delete. | |
| * @param children Boolean. False: don't delete directory contents. | |
| * True: delete directory contents. | |
| * | |
| * @return void | |
| */ | |
| function rmdir($dir, $children = false) { | |
| global $php_errormsg; | |
| // If children=FALSE only delete dir if empty. | |
| if (false === $children) { | |
| if (false === @rmdir($dir)) { // FAILED. | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } else { // delete contents and dir. | |
| $handle = @opendir($dir); | |
| if (false === $handle) { // Error. | |
| $msg = "FileSystem::rmdir() FAILED. Cannot opendir() $dir. $php_errormsg"; | |
| throw new Exception($msg); | |
| } else { // Read from handle. | |
| // Don't error on readdir(). | |
| while (false !== ($entry = @readdir($handle))) { | |
| if ($entry != '.' && $entry != '..') { | |
| // Only add / if it isn't already the last char. | |
| // This ONLY serves the purpose of making the Logger | |
| // output look nice:) | |
| if (strpos(strrev($dir), DIRECTORY_SEPARATOR) === 0) {// there is a / | |
| $next_entry = $dir . $entry; | |
| } else { // no / | |
| $next_entry = $dir . DIRECTORY_SEPARATOR . $entry; | |
| } | |
| // NOTE: As of php 4.1.1 is_dir doesn't return FALSE it | |
| // returns 0. So use == not ===. | |
| // Don't error on is_dir() | |
| if (false == @is_dir($next_entry)) { // Is file. | |
| try { | |
| self::unlink($next_entry); // Delete. | |
| } catch (Exception $e) { | |
| $msg = "FileSystem::Rmdir() FAILED. Cannot FileSystem::Unlink() $next_entry. ". $e->getMessage(); | |
| throw new Exception($msg); | |
| } | |
| } else { // Is directory. | |
| try { | |
| self::rmdir($next_entry, true); // Delete | |
| } catch (Exception $e) { | |
| $msg = "FileSystem::rmdir() FAILED. Cannot FileSystem::rmdir() $next_entry. ". $e->getMessage(); | |
| throw new Exception($msg); | |
| } | |
| } // end is_dir else | |
| } // end .. if | |
| } // end while | |
| } // end handle if | |
| // Don't error on closedir() | |
| @closedir($handle); | |
| if (false === @rmdir($dir)) { // FAILED. | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| } | |
| /** | |
| * Set the umask for file and directory creation. | |
| * | |
| * @param mode Int. Permissions ususally in ocatal. Use leading 0 for | |
| * octal. Number between 0 and 0777. | |
| * | |
| * @return void | |
| * @throws Exception if there is an error performing operation. | |
| */ | |
| function umask($mode) { | |
| global $php_errormsg; | |
| // CONSIDERME: | |
| // Throw a warning if mode is 0. PHP converts illegal octal numbers to | |
| // 0 so 0 might not be what the user intended. | |
| $str_mode = decoct($mode); // Show octal in messages. | |
| if (false === @umask($mode)) { // FAILED. | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::Umask() FAILED. Value $mode. $php_errormsg"; | |
| throw new Exception($msg); | |
| } | |
| } | |
| /** | |
| * Compare the modified time of two files. | |
| * | |
| * @param file1 String. Path and name of file1. | |
| * @param file2 String. Path and name of file2. | |
| * | |
| * @return Int. 1 if file1 is newer. | |
| * -1 if file2 is newer. | |
| * 0 if files have the same time. | |
| * Err object on failure. | |
| * | |
| * @throws Exception - if cannot get modified time of either file. | |
| */ | |
| function compareMTimes($file1, $file2) { | |
| $mtime1 = filemtime($file1); | |
| $mtime2 = filemtime($file2); | |
| if ($mtime1 === false) { // FAILED. Log and return err. | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file1."; | |
| throw new Exception($msg); | |
| } elseif ($mtime2 === false) { // FAILED. Log and return err. | |
| // Add error from php to end of log message. $php_errormsg. | |
| $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file2."; | |
| throw new Exception($msg); | |
| } else { // Worked. Log and return compare. | |
| // Compare mtimes. | |
| if ($mtime1 == $mtime2) { | |
| return 0; | |
| } else { | |
| return ($mtime1 < $mtime2) ? -1 : 1; | |
| } // end compare | |
| } | |
| } | |
| } | |