Viewing file: FileSystem.php (23.14 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/*
* $Id: FileSystem.php,v 1.11 2005/12/01 20:56:59 hlellelid Exp $
*
* 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) {
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
}
}
}
|