Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/** $Id: PhingFile.php 362 2008-03-08 10:07:53Z mrook $** 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>.*/include_once 'phing/system/io/FileSystem.php';include_once 'phing/system/lang/NullPointerException.php';/*** An abstract representation of file and directory pathnames.** @version $Revision: 1.1 $* @package phing.system.io*/class PhingFile {/** separator string, static, obtained from FileSystem */public static $separator;/** path separator string, static, obtained from FileSystem (; or :)*/public static $pathSeparator;/*** This abstract pathname's normalized pathname string. A normalized* pathname string uses the default name-separator character and does not* contain any duplicate or redundant separators.*/private $path = null;/** The length of this abstract pathname's prefix, or zero if it has no prefix. */private $prefixLength = 0;/** constructor */function __construct($arg1 = null, $arg2 = null) {if (self::$separator === null || self::$pathSeparator === null) {$fs = FileSystem::getFileSystem();self::$separator = $fs->getSeparator();self::$pathSeparator = $fs->getPathSeparator();}/* simulate signature identified constructors */if ($arg1 instanceof PhingFile && is_string($arg2)) {$this->_constructFileParentStringChild($arg1, $arg2);} elseif (is_string($arg1) && ($arg2 === null)) {$this->_constructPathname($arg1);} elseif(is_string($arg1) && is_string($arg2)) {$this->_constructStringParentStringChild($arg1, $arg2);} else {if ($arg1 === null) {throw new NullPointerException("Argument1 to function must not be null");}$this->path = (string) $arg1;$this->prefixLength = (int) $arg2;}}/** Returns the length of this abstract pathname's prefix. */function getPrefixLength() {return (int) $this->prefixLength;}/* -- constructors not called by signature match, so we need some helpers --*/function _constructPathname($pathname) {// obtain ref to the filesystem layer$fs = FileSystem::getFileSystem();if ($pathname === null) {throw new NullPointerException("Argument to function must not be null");}$this->path = (string) $fs->normalize($pathname);$this->prefixLength = (int) $fs->prefixLength($this->path);}function _constructStringParentStringChild($parent, $child = null) {// obtain ref to the filesystem layer$fs = FileSystem::getFileSystem();if ($child === null) {throw new NullPointerException("Argument to function must not be null");}if ($parent !== null) {if ($parent === "") {$this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child));} else {$this->path = $fs->resolve($fs->normalize($parent), $fs->normalize($child));}} else {$this->path = (string) $fs->normalize($child);}$this->prefixLength = (int) $fs->prefixLength($this->path);}function _constructFileParentStringChild($parent, $child = null) {// obtain ref to the filesystem layer$fs = FileSystem::getFileSystem();if ($child === null) {throw new NullPointerException("Argument to function must not be null");}if ($parent !== null) {if ($parent->path === "") {$this->path = $fs->resolve($fs->getDefaultParent(), $fs->normalize($child));} else {$this->path = $fs->resolve($parent->path, $fs->normalize($child));}} else {$this->path = $fs->normalize($child);}$this->prefixLength = $fs->prefixLength($this->path);}/* -- Path-component accessors -- *//*** Returns the name of the file or directory denoted by this abstract* pathname. This is just the last name in the pathname's name* sequence. If the pathname's name sequence is empty, then the empty* string is returned.** @return The name of the file or directory denoted by this abstract* pathname, or the empty string if this pathname's name sequence* is empty*/function getName() {// that's a lastIndexOf$index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res);if ($index < $this->prefixLength) {return substr($this->path, $this->prefixLength);}return substr($this->path, $index + 1);}/*** Returns the pathname string of this abstract pathname's parent, or* null if this pathname does not name a parent directory.** The parent of an abstract pathname consists of the pathname's prefix,* if any, and each name in the pathname's name sequence except for the last.* If the name sequence is empty then the pathname does not name a parent* directory.** @return The pathname string of the parent directory named by this* abstract pathname, or null if this pathname does not name a parent*/function getParent() {// that's a lastIndexOf$index = ((($res = strrpos($this->path, self::$separator)) === false) ? -1 : $res);if ($index < $this->prefixLength) {if (($this->prefixLength > 0) && (strlen($this->path > $this->prefixLength))) {return substr($this->path, 0, $this->prefixLength);}return null;}return substr($this->path, 0, $index);}/*** Returns the abstract pathname of this abstract pathname's parent,* or null if this pathname does not name a parent directory.** The parent of an abstract pathname consists of the pathname's prefix,* if any, and each name in the pathname's name sequence except for the* last. If the name sequence is empty then the pathname does not name* a parent directory.** @return The abstract pathname of the parent directory named by this* abstract pathname, or null if this pathname* does not name a parent*/function getParentFile() {$p = $this->getParent();if ($p === null) {return null;}return new PhingFile((string) $p, (int) $this->prefixLength);}/*** Converts this abstract pathname into a pathname string. The resulting* string uses the default name-separator character to separate the names* in the name sequence.** @return The string form of this abstract pathname*/function getPath() {return (string) $this->path;}/*** Tests whether this abstract pathname is absolute. The definition of* absolute pathname is system dependent. On UNIX systems, a pathname is* absolute if its prefix is "/". On Win32 systems, a pathname is absolute* if its prefix is a drive specifier followed by "\\", or if its prefix* is "\\".** @return true if this abstract pathname is absolute, false otherwise*/function isAbsolute() {return ($this->prefixLength !== 0);}/*** Returns the absolute pathname string of this abstract pathname.** If this abstract pathname is already absolute, then the pathname* string is simply returned as if by the getPath method.* If this abstract pathname is the empty abstract pathname then* the pathname string of the current user directory, which is named by the* system property user.dir, is returned. Otherwise this* pathname is resolved in a system-dependent way. On UNIX systems, a* relative pathname is made absolute by resolving it against the current* user directory. On Win32 systems, a relative pathname is made absolute* by resolving it against the current directory of the drive named by the* pathname, if any; if not, it is resolved against the current user* directory.** @return The absolute pathname string denoting the same file or* directory as this abstract pathname* @see #isAbsolute()*/function getAbsolutePath() {$fs = FileSystem::getFileSystem();return $fs->resolveFile($this);}/*** Returns the absolute form of this abstract pathname. Equivalent to* getAbsolutePath.** @return The absolute abstract pathname denoting the same file or* directory as this abstract pathname*/function getAbsoluteFile() {return new PhingFile((string) $this->getAbsolutePath());}/*** Returns the canonical pathname string of this abstract pathname.** A canonical pathname is both absolute and unique. The precise* definition of canonical form is system-dependent. This method first* converts this pathname to absolute form if necessary, as if by invoking the* getAbsolutePath() method, and then maps it to its unique form in a* system-dependent way. This typically involves removing redundant names* such as "." and .. from the pathname, resolving symbolic links* (on UNIX platforms), and converting drive letters to a standard case* (on Win32 platforms).** Every pathname that denotes an existing file or directory has a* unique canonical form. Every pathname that denotes a nonexistent file* or directory also has a unique canonical form. The canonical form of* the pathname of a nonexistent file or directory may be different from* the canonical form of the same pathname after the file or directory is* created. Similarly, the canonical form of the pathname of an existing* file or directory may be different from the canonical form of the same* pathname after the file or directory is deleted.** @return The canonical pathname string denoting the same file or* directory as this abstract pathname*/function getCanonicalPath() {$fs = FileSystem::getFileSystem();return $fs->canonicalize($this->path);}/*** Returns the canonical form of this abstract pathname. Equivalent to* getCanonicalPath(.** @return PhingFile The canonical pathname string denoting the same file or* directory as this abstract pathname*/function getCanonicalFile() {return new PhingFile($this->getCanonicalPath());}/*** Converts this abstract pathname into a file: URL. The* exact form of the URL is system-dependent. If it can be determined that* the file denoted by this abstract pathname is a directory, then the* resulting URL will end with a slash.** Usage note: This method does not automatically escape* characters that are illegal in URLs. It is recommended that new code* convert an abstract pathname into a URL by first converting it into a* URI, via the toURI() method, and then converting the URI* into a URL via the URI::toURL()** @return A URL object representing the equivalent file URL***/function toURL() {/*// URL class not implemented yetreturn new URL("file", "", $this->_slashify($this->getAbsolutePath(), $this->isDirectory()));*/}/*** Constructs a file: URI that represents this abstract pathname.* Not implemented yet*/function toURI() {/*$f = $this->getAbsoluteFile();$sp = (string) $this->slashify($f->getPath(), $f->isDirectory());if (StringHelper::startsWith('//', $sp))$sp = '//' + sp;return new URI('file', null, $sp, null);*/}function _slashify($path, $isDirectory) {$p = (string) $path;if (self::$separator !== '/') {$p = str_replace(self::$separator, '/', $p);}if (!StringHelper::startsWith('/', $p)) {$p = '/'.$p;}if (!StringHelper::endsWith('/', $p) && $isDirectory) {$p = $p.'/';}return $p;}/* -- Attribute accessors -- *//*** Tests whether the application can read the file denoted by this* abstract pathname.** @return true if and only if the file specified by this* abstract pathname exists and can be read by the* application; false otherwise*/function canRead() {$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this)) {return (boolean) @is_readable($this->getAbsolutePath());}return false;}/*** Tests whether the application can modify to the file denoted by this* abstract pathname.** @return true if and only if the file system actually* contains a file denoted by this abstract pathname and* the application is allowed to write to the file;* false otherwise.**/function canWrite() {$fs = FileSystem::getFileSystem();return $fs->checkAccess($this, true);}/*** Tests whether the file denoted by this abstract pathname exists.** @return true if and only if the file denoted by this* abstract pathname exists; false otherwise**/function exists() {clearstatcache();if ($this->isFile()) {return @file_exists($this->path);} else {return @is_dir($this->path);}}/*** Tests whether the file denoted by this abstract pathname is a* directory.** @return true if and only if the file denoted by this* abstract pathname exists and is a directory;* false otherwise**/function isDirectory() {clearstatcache();$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this) !== true) {throw new IOException("No read access to ".$this->path);}return @is_dir($this->path);}/*** Tests whether the file denoted by this abstract pathname is a normal* file. A file is normal if it is not a directory and, in* addition, satisfies other system-dependent criteria. Any non-directory* file created by a Java application is guaranteed to be a normal file.** @return true if and only if the file denoted by this* abstract pathname exists and is a normal file;* false otherwise*/function isFile() {clearstatcache();//$fs = FileSystem::getFileSystem();return @is_file($this->path);}/*** Tests whether the file named by this abstract pathname is a hidden* file. The exact definition of hidden is system-dependent. On* UNIX systems, a file is considered to be hidden if its name begins with* a period character ('.'). On Win32 systems, a file is considered to be* hidden if it has been marked as such in the filesystem. Currently there* seems to be no way to dermine isHidden on Win file systems via PHP** @return true if and only if the file denoted by this* abstract pathname is hidden according to the conventions of the* underlying platform*/function isHidden() {$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this) !== true) {throw new IOException("No read access to ".$this->path);}return (($fs->getBooleanAttributes($this) & $fs->BA_HIDDEN) !== 0);}/*** Returns the time that the file denoted by this abstract pathname was* last modified.** @return A integer value representing the time the file was* last modified, measured in milliseconds since the epoch* (00:00:00 GMT, January 1, 1970), or 0 if the* file does not exist or if an I/O error occurs*/function lastModified() {$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this) !== true) {throw new IOException("No read access to " . $this->path);}return $fs->getLastModifiedTime($this);}/*** Returns the length of the file denoted by this abstract pathname.* The return value is unspecified if this pathname denotes a directory.** @return The length, in bytes, of the file denoted by this abstract* pathname, or 0 if the file does not exist*/function length() {$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this) !== true) {throw new IOException("No read access to ".$this->path."\n");}return $fs->getLength($this);}/*** Convenience method for returning the contents of this file as a string.* This method uses file_get_contents() to read file in an optimized way.* @return string* @throws Exception - if file cannot be read*/function contents() {if (!$this->canRead() || !$this->isFile()) {throw new IOException("Cannot read file contents!");}return file_get_contents($this->getAbsolutePath());}/* -- File operations -- *//*** Atomically creates a new, empty file named by this abstract pathname if* and only if a file with this name does not yet exist. The check for the* existence of the file and the creation of the file if it does not exist* are a single operation that is atomic with respect to all other* filesystem activities that might affect the file.** @return true if the named file does not exist and was* successfully created; <code>false</code> if the named file* already exists* @throws IOException if file can't be created*/function createNewFile($parents=true, $mode=0777) {$file = FileSystem::getFileSystem()->createNewFile($this->path);return $file;}/*** Deletes the file or directory denoted by this abstract pathname. If* this pathname denotes a directory, then the directory must be empty in* order to be deleted.** @return true if and only if the file or directory is* successfully deleted; false otherwise*/function delete() {$fs = FileSystem::getFileSystem();if ($fs->canDelete($this) !== true) {throw new IOException("Cannot delete " . $this->path . "\n");}return $fs->delete($this);}/*** Requests that the file or directory denoted by this abstract pathname* be deleted when php terminates. Deletion will be attempted only for* normal termination of php and if and if only Phing::shutdown() is* called.** Once deletion has been requested, it is not possible to cancel the* request. This method should therefore be used with care.**/function deleteOnExit() {$fs = FileSystem::getFileSystem();$fs->deleteOnExit($this);}/*** Returns an array of strings naming the files and directories in the* directory denoted by this abstract pathname.** If this abstract pathname does not denote a directory, then this* method returns null Otherwise an array of strings is* returned, one for each file or directory in the directory. Names* denoting the directory itself and the directory's parent directory are* not included in the result. Each string is a file name rather than a* complete path.** There is no guarantee that the name strings in the resulting array* will appear in any specific order; they are not, in particular,* guaranteed to appear in alphabetical order.** @return An array of strings naming the files and directories in the* directory denoted by this abstract pathname. The array will be* empty if the directory is empty. Returns null if* this abstract pathname does not denote a directory, or if an* I/O error occurs.**/function listDir($filter = null) {$fs = FileSystem::getFileSystem();return $fs->lister($this, $filter);}function listFiles($filter = null) {$ss = $this->listDir($filter);if ($ss === null) {return null;}$n = count($ss);$fs = array();for ($i = 0; $i < $n; $i++) {$fs[$i] = new PhingFile((string)$this->path, (string)$ss[$i]);}return $fs;}/*** Creates the directory named by this abstract pathname, including any* necessary but nonexistent parent directories. Note that if this* operation fails it may have succeeded in creating some of the necessary* parent directories.** @return true if and only if the directory was created,* along with all necessary parent directories; false* otherwise* @throws IOException*/function mkdirs() {if ($this->exists()) {return false;}try {if ($this->mkdir()) {return true;}} catch (IOException $ioe) {// IOException from mkdir() means that directory propbably didn't exist.}$parentFile = $this->getParentFile();return (($parentFile !== null) && ($parentFile->mkdirs() && $this->mkdir()));}/*** Creates the directory named by this abstract pathname.** @return true if and only if the directory was created; false otherwise* @throws IOException*/function mkdir() {$fs = FileSystem::getFileSystem();if ($fs->checkAccess(new PhingFile($this->path), true) !== true) {throw new IOException("No write access to " . $this->getPath());}return $fs->createDirectory($this);}/*** Renames the file denoted by this abstract pathname.** @param destFile The new abstract pathname for the named file* @return true if and only if the renaming succeeded; false otherwise*/function renameTo(PhingFile $destFile) {$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this) !== true) {throw new IOException("No write access to ".$this->getPath());}return $fs->rename($this, $destFile);}/*** Simple-copies file denoted by this abstract pathname into another* PhingFile** @param PhingFile $destFile The new abstract pathname for the named file* @return true if and only if the renaming succeeded; false otherwise*/function copyTo(PhingFile $destFile) {$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this) !== true) {throw new IOException("No read access to ".$this->getPath()."\n");}if ($fs->checkAccess($destFile, true) !== true) {throw new IOException("File::copyTo() No write access to ".$destFile->getPath());}return $fs->copy($this, $destFile);}/*** Sets the last-modified time of the file or directory named by this* abstract pathname.** All platforms support file-modification times to the nearest second,* but some provide more precision. The argument will be truncated to fit* the supported precision. If the operation succeeds and no intervening* operations on the file take place, then the next invocation of the* lastModified method will return the (possibly truncated) time argument* that was passed to this method.** @param time The new last-modified time, measured in milliseconds since* the epoch (00:00:00 GMT, January 1, 1970)* @return true if and only if the operation succeeded; false otherwise*/function setLastModified($time) {$time = (int) $time;if ($time < 0) {throw new Exception("IllegalArgumentException, Negative $time\n");}$fs = FileSystem::getFileSystem();return $fs->setLastModifiedTime($this, $time);}/*** Marks the file or directory named by this abstract pathname so that* only read operations are allowed. After invoking this method the file* or directory is guaranteed not to change until it is either deleted or* marked to allow write access. Whether or not a read-only file or* directory may be deleted depends upon the underlying system.** @return true if and only if the operation succeeded; false otherwise*/function setReadOnly() {$fs = FileSystem::getFileSystem();if ($fs->checkAccess($this, true) !== true) {// Error, no write accessthrow new IOException("No write access to " . $this->getPath());}return $fs->setReadOnly($this);}/*** Sets the owner of the file.* @param mixed $user User name or number.*/public function setUser($user) {$fs = FileSystem::getFileSystem();return $fs->chown($this->getPath(), $user);}/*** Retrieve the owner of this file.* @return int User ID of the owner of this file.*/function getUser() {return @fileowner($this->getPath());}/*** Sets the mode of the file* @param int $mode Ocatal mode.*/function setMode($mode) {$fs = FileSystem::getFileSystem();return $fs->chmod($this->getPath(), $mode);}/*** Retrieve the mode of this file.* @return int*/function getMode() {return @fileperms($this->getPath());}/* -- Filesystem interface -- *//*** List the available filesystem roots.** A particular platform may support zero or more hierarchically-organized* file systems. Each file system has a root directory from which all* other files in that file system can be reached.* Windows platforms, for example, have a root directory for each active* drive; UNIX platforms have a single root directory, namely "/".* The set of available filesystem roots is affected by various system-level* operations such the insertion or ejection of removable media and the* disconnecting or unmounting of physical or virtual disk drives.** This method returns an array of PhingFile objects that* denote the root directories of the available filesystem roots. It is* guaranteed that the canonical pathname of any file physically present on* the local machine will begin with one of the roots returned by this* method.** The canonical pathname of a file that resides on some other machine* and is accessed via a remote-filesystem protocol such as SMB or NFS may* or may not begin with one of the roots returned by this method. If the* pathname of a remote file is syntactically indistinguishable from the* pathname of a local file then it will begin with one of the roots* returned by this method. Thus, for example, PhingFile objects* denoting the root directories of the mapped network drives of a Windows* platform will be returned by this method, while PhingFile* objects containing UNC pathnames will not be returned by this method.** @return An array of PhingFile objects denoting the available* filesystem roots, or null if the set of roots* could not be determined. The array will be empty if there are* no filesystem roots.*/function listRoots() {$fs = FileSystem::getFileSystem();return (array) $fs->listRoots();}/* -- Tempfile management -- *//*** Returns the path to the temp directory.*/function getTempDir() {return Phing::getProperty('php.tmpdir');}/*** Static method that creates a unique filename whose name begins with* $prefix and ends with $suffix in the directory $directory. $directory* is a reference to a PhingFile Object.* Then, the file is locked for exclusive reading/writing.** @author manuel holtgrewe, grin@gmx.net* @throws IOException* @access public*/function createTempFile($prefix, $suffix, PhingFile $directory) {// quick but efficient hack to create a unique filename ;-)$result = null;do {$result = new PhingFile($directory, $prefix . substr(md5(time()), 0, 8) . $suffix);} while (file_exists($result->getPath()));$fs = FileSystem::getFileSystem();$fs->createNewFile($result->getPath());$fs->lock($result);return $result;}/*** If necessary, $File the lock on $File is removed and then the file is* deleted** @access public*/function removeTempFile() {$fs = FileSystem::getFileSystem();// catch IO Exception$fs->unlock($this);$this->delete();}/* -- Basic infrastructure -- *//*** Compares two abstract pathnames lexicographically. The ordering* defined by this method depends upon the underlying system. On UNIX* systems, alphabetic case is significant in comparing pathnames; on Win32* systems it is not.** @param PhingFile $file Th file whose pathname sould be compared to the pathname of this file.** @return int Zero if the argument is equal to this abstract pathname, a* value less than zero if this abstract pathname is* lexicographically less than the argument, or a value greater* than zero if this abstract pathname is lexicographically* greater than the argument*/function compareTo(PhingFile $file) {$fs = FileSystem::getFileSystem();return $fs->compare($this, $file);}/*** Tests this abstract pathname for equality with the given object.* Returns <code>true</code> if and only if the argument is not* <code>null</code> and is an abstract pathname that denotes the same file* or directory as this abstract pathname. Whether or not two abstract* pathnames are equal depends upon the underlying system. On UNIX* systems, alphabetic case is significant in comparing pathnames; on Win32* systems it is not.* @return boolean*/function equals($obj) {if (($obj !== null) && ($obj instanceof PhingFile)) {return ($this->compareTo($obj) === 0);}return false;}/** Backwards compatibility -- use PHP5's native __tostring method. */function toString() {return $this->getPath();}/** PHP5's native method. */function __toString() {return $this->getPath();}}