| <?php |
| /** |
| * File containing the abstract ezcArchive class. |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @package Archive |
| * @version //autogentag// |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| */ |
| |
| /** |
| * The ezcArchive class provides the common interface for reading and writing |
| * the archive formats Tar and Zip. |
| * |
| * ezcArchive provides the main API for reading and writing to an archive. The |
| * archive itself can be compressed with GZip or BZip2 and will be handled |
| * transparently. |
| * |
| * The {@link open()} method creates a new archive instance. For |
| * existing archives, ezcArchive determines the correct archive format by the |
| * mime-type and returns an instance of a subclass handling this format. |
| * New archives should force a format type via a parameter in the open() |
| * method. |
| * |
| * The instance of an ezcArchive class is also an iterator, which |
| * points to the first file in the archive by default. Moving this pointer can |
| * be done via the iterator methods: {@link rewind()}, {@link next()}, |
| * {@link valid()}, {@link key()}, and {@link current()}. This iterator is |
| * defined as an object iterator and allows, for example, the {@link |
| * http://www.php.net/foreach foreach} statement to iterate through the files. |
| * |
| * Extra methods that operate on the current iterator are: {@link |
| * extractCurrent()} and {@link appendToCurrent()}. Which can be used, |
| * respectively, to extract the files and to append a new file to the archive. |
| * To append a directory to an archive you need to add a slash '/' at the end |
| * of the directory name. |
| * |
| * The following example will open an existing tar.gz file and will append each |
| * file to a zip archive: |
| * <code> |
| * $tar = ezcArchive::open( "/tmp/archive.tar.gz" ); |
| * $newZip = ezcArchive::open( "/tmp/new_archive.zip", ezcArchive::ZIP ); |
| * |
| * foreach ( $tar as $entry ) |
| * { |
| * // $entry contains the information of the current entry in the archive. |
| * $tar->extractCurrent( "/tmp/" ); |
| * $newZip->appendToCurrent( $entry->getPath(), "/tmp/" ); |
| * $newZip->next(); |
| * } |
| * </code> |
| * |
| * In order to extract an entire archive at once, use the {@link extract()} |
| * method. |
| * |
| * @package Archive |
| * @version //autogentag// |
| * @mainclass |
| */ |
| abstract class ezcArchive implements Iterator |
| { |
| /** |
| * Normal tar archive. |
| */ |
| const TAR = 0; |
| |
| /** |
| * Tar version 7 archive. |
| */ |
| const TAR_V7 = 1; |
| |
| /** |
| * USTAR tar archive. |
| */ |
| const TAR_USTAR = 2; |
| |
| /** |
| * PAX tar archive. |
| */ |
| const TAR_PAX = 3; |
| |
| /** |
| * GNU tar archive. |
| */ |
| const TAR_GNU = 4; |
| |
| /** |
| * ZIP archive. |
| */ |
| const ZIP = 10; |
| |
| /** |
| * Gnu ZIP compression format. |
| */ |
| const GZIP = 20; |
| |
| /** |
| * BZIP2 compression format. |
| */ |
| const BZIP2 = 30; |
| |
| /** |
| * The entry or file number to which the iterator points. |
| * |
| * The first $fileNumber starts with 0. |
| * |
| * @var int |
| */ |
| protected $fileNumber = 0; |
| |
| /** |
| * The number of entries currently read from the archive. |
| * |
| * @var int |
| */ |
| protected $entriesRead = 0; |
| |
| /** |
| * Is true when the archive is read until the end, otherwise false. |
| * |
| * @var bool |
| */ |
| protected $completed = false; |
| |
| /** |
| * Stores the entries read from the archive. |
| * |
| * The array is not complete when the {@link $completed} variable is set to |
| * false. The array may be over-complete, so the {@link $entriesRead} |
| * should be checked if the {@link $completed} variable is set to true. |
| * |
| * @var array(ezcArchiveEntry) |
| */ |
| protected $entries; |
| |
| /** |
| * Direct access to the archive file. |
| * |
| * @var ezcArchiveFile |
| */ |
| protected $file = null; |
| |
| /** |
| * Holds the options if passed to the open method. |
| * |
| * @var ezcArchiveOptions |
| */ |
| protected $options; |
| |
| /** |
| * Use the {@link open()} method to get an instance of this class. |
| */ |
| private function __construct() |
| { |
| } |
| |
| /** |
| * Returns a new ezcArchive instance. |
| * |
| * This method returns a new instance according to the mime-type or |
| * the given $forceType. |
| * |
| * - If $forceType is set to null, this method will try to determine the |
| * archive format via the file data. Therefore the $forceType can only be |
| * null when the archive contains data. |
| * - If $forceType is set, it will use the specified algorithm. Even when |
| * the given archive is from another type than specified. |
| * |
| * @throws ezcArchiveUnknownTypeException if the type of the archive cannot be determined. |
| * |
| * @param string $archiveName Absolute or relative path to the archive. |
| * @param int $forceType Open the archive with the $forceType |
| * algorithm. Possible values are: {@link ezcArchive::ZIP}, |
| * {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7}, {@link ezcArchive::TAR_USTAR}, |
| * {@link ezcArchive::TAR_PAX}, {@link ezcArchive::TAR_GNU}. |
| * TAR will use the TAR_USTAR algorithm by default. |
| * @param ezcArchiveOptions $options |
| * |
| * @return ezcArchive |
| */ |
| public static function open( $archiveName, $forceType = null, ezcArchiveOptions $options = null ) |
| { |
| $options = self::initOptions( $options ); |
| |
| if ( !ezcArchiveFile::fileExists( $archiveName ) && $forceType === null ) |
| { |
| throw new ezcArchiveUnknownTypeException( $archiveName ); |
| } |
| |
| if ( $forceType !== null ) |
| { |
| return self::createInstance( $archiveName, $forceType, $options ); |
| } |
| |
| $h = ezcArchiveFileType::detect( $archiveName ); |
| |
| while ( $h == ezcArchive::GZIP || $h == ezcArchive::BZIP2 ) |
| { |
| if ( $h == ezcArchive::GZIP ) |
| { |
| $archiveName = "compress.zlib://$archiveName"; |
| $h = ezcArchiveFileType::detect( $archiveName ); |
| } |
| |
| if ( $h == ezcArchive::BZIP2 ) |
| { |
| $archiveName = "compress.bzip2://$archiveName"; |
| $h = ezcArchiveFileType::detect( $archiveName ); |
| } |
| } |
| |
| return self::createInstance( $archiveName, $h, $options ); |
| } |
| |
| /** |
| * Close the current archive. |
| */ |
| public function close() |
| { |
| } |
| |
| /** |
| * Sets the property $name to $value. |
| * |
| * Because there are no properties available, this method will always |
| * throw an {@link ezcBasePropertyNotFoundException}. |
| * |
| * @throws ezcBasePropertyNotFoundException if the property does not exist. |
| * @param string $name |
| * @param mixed $value |
| * @ignore |
| */ |
| public function __set( $name, $value ) |
| { |
| throw new ezcBasePropertyNotFoundException( $name ); |
| } |
| |
| /** |
| * Returns the property $name. |
| * |
| * Because there are no properties available, this method will always |
| * throw an {@link ezcBasePropertyNotFoundException}. |
| * |
| * @throws ezcBasePropertyNotFoundException if the property does not exist. |
| * @param string $name |
| * @ignore |
| */ |
| public function __get( $name ) |
| { |
| throw new ezcBasePropertyNotFoundException( $name ); |
| } |
| |
| /** |
| * Returns the algorithm that is used currently. |
| * |
| * @return int Possible values are: {@link ezcArchive::ZIP}, {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7}, |
| * {@link ezcArchive::TAR_USTAR}, {@link ezcArchive::TAR_PAX}, or {@link ezcArchive::TAR_GNU}. |
| */ |
| public abstract function getAlgorithm(); |
| |
| /** |
| * Returns true if writing to the archive is implemented, otherwise false. |
| * |
| * @see isWritable() |
| * |
| * @return bool |
| */ |
| public abstract function algorithmCanWrite(); |
| |
| /** |
| * Returns an instance of the archive with the given type. |
| * |
| * Similar to {@link open()}, but the type is required. |
| * |
| * @param string $archiveName The path of the archive. |
| * @param int $type Open the archive with the $forceType |
| * algorithm. Possible values are: {@link ezcArchive::ZIP}, |
| * {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7}, {@link ezcArchive::TAR_USTAR}, |
| * {@link ezcArchive::TAR_PAX}, {@link ezcArchive::TAR_GNU}. |
| * TAR will use the TAR_USTAR algorithm by default. |
| * |
| * @return ezcArchive Subclass of ezcArchive: {@link ezcArchiveZip}, |
| * {@link ezcArchiveV7Tar}, {@link ezcArchivePax}, |
| * {@link ezcArchiveGnuTar}, or {@link ezcArchiveUstar}. |
| */ |
| protected static function createInstance( $archiveName, $type, ezcArchiveOptions $options = null ) |
| { |
| $options = self::initOptions( $options ); |
| |
| if ( $type == self::ZIP ) |
| { |
| $af = new ezcArchiveCharacterFile( $archiveName, true, $options->readOnly ); |
| return self::getZipInstance( $af ); |
| } |
| |
| $af = new ezcArchiveBlockFile( $archiveName, true, 512, $options->readOnly ); |
| $instance = self::getTarInstance( $af, $type ); |
| $instance->options = $options; |
| return $instance; |
| } |
| |
| /** |
| * This methods initializes the options by generating an options object if $options is null. |
| * |
| * @param null|ezcArchiveOptions $options |
| * |
| * @return ezcArchiveOptions |
| */ |
| private static function initOptions( $options ) |
| { |
| if ( $options === null ) |
| { |
| $options = new ezcArchiveOptions; |
| } |
| return $options; |
| } |
| |
| /** |
| * This method associates a new $options object with this archive. |
| * |
| * @param ezcArchiveOptions $options |
| */ |
| public function setOptions( ezcArchiveOptions $options ) |
| { |
| $this->options = $options; |
| } |
| |
| /** |
| * Open a tar instance. |
| * |
| * This method is made public for testing purposes, and should not be used. |
| * |
| * @param ezcArchiveBlockFile $blockFile |
| * @param int $type |
| * The algorithm type. Possible values are: |
| * {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7}, {@link ezcArchive::TAR_USTAR}, |
| * {@link ezcArchive::TAR_PAX}, {@link ezcArchive::TAR_GNU}. |
| * TAR will use the TAR_USTAR algorithm by default. |
| * |
| * @return ezcArchive Subclass of ezcArchive: |
| * {@link ezcArchiveV7Tar}, {@link ezcArchivePax}, |
| * {@link ezcArchiveGnuTar}, or {@link ezcArchiveUstar}. |
| * @access private |
| */ |
| public static function getTarInstance( ezcArchiveBlockFile $blockFile, $type ) |
| { |
| switch ( $type ) |
| { |
| case self::TAR_V7: |
| return new ezcArchiveV7Tar( $blockFile ); |
| |
| case self::TAR_USTAR: |
| return new ezcArchiveUstarTar( $blockFile ); |
| |
| case self::TAR_PAX: |
| return new ezcArchivePaxTar( $blockFile ); |
| |
| case self::TAR_GNU: |
| return new ezcArchiveGnuTar( $blockFile ); |
| |
| case self::TAR: |
| return new ezcArchiveUstarTar( $blockFile ); // Default type. |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Open a zip instance. This method is made public for testing purposes, and |
| * should not be used. |
| * |
| * @param ezcArchiveCharacterFile $charFile The character file which |
| * contains the archive. |
| * @return ezcArchive Subclass of ezcArchive: {@link ezcArchiveZip}. |
| * @access private |
| */ |
| public static function getZipInstance( ezcArchiveCharacterFile $charFile ) |
| { |
| return new ezcArchiveZip( $charFile ); |
| } |
| |
| /** |
| * Returns true if the iterator points to a valid entry, otherwise false. |
| * |
| * @return bool |
| */ |
| public function valid() |
| { |
| return ( $this->fileNumber >= 0 && $this->fileNumber < $this->entriesRead ); |
| } |
| |
| /** |
| * Rewinds the iterator to the first entry. |
| * |
| * @return void |
| */ |
| public function rewind() |
| { |
| $this->fileNumber = 0; |
| } |
| |
| /** |
| * Returns the current ezcArchiveEntry if it is valid, otherwise false is returned. |
| * |
| * @return ezcArchiveEntry |
| */ |
| public function current() |
| { |
| return ( $this->valid() ? $this->entries[$this->fileNumber] : false ); |
| } |
| |
| /** |
| * Returns the current key, entry number, if it is valid, otherwise false is returned. |
| * |
| * @return int |
| */ |
| public function key() |
| { |
| return ( $this->valid() ? $this->fileNumber : false ); |
| } |
| |
| /** |
| * Forwards the iterator to the next entry. |
| * |
| * If there is no next entry all iterator methods except for {@link |
| * rewind()} will return false. |
| * |
| * @see rewind() |
| * |
| * @return ezcArchiveEntry The next entry if it exists, otherwise false. |
| */ |
| public function next() |
| { |
| if ( $this->valid() ) |
| { |
| $this->fileNumber++; |
| if ( $this->valid() ) |
| { |
| return $this->current(); |
| } |
| |
| if ( !$this->completed ) |
| { |
| if ( $this->readCurrentFromArchive() ) |
| { |
| return $this->current(); |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Extract the current entry to which the iterator points. |
| * |
| * Extract the current entry to which the iterator points, and return true if the current entry is extracted. |
| * If the iterator doesn't point to a valid entry, this method returns false. |
| * |
| * True if the file is extracted correctly, otherwise false. |
| * |
| * @param string $target |
| * The full path to which the target should be extracted. |
| * @param bool $keepExisting |
| * True if the file shouldn't be overwritten if they already exist. |
| * For the opposite behaviour, false should be given. |
| * |
| * @throws ezcArchiveValueException if the archive contains invalid values. |
| * @throws ezcBaseFileNotFoundException if the link cannot be found. |
| * |
| * @return bool |
| */ |
| public function extractCurrent( $target, $keepExisting = false ) |
| { |
| if ( $this->file === null ) |
| { |
| throw new ezcArchiveException( "The archive is closed" ); |
| } |
| |
| if ( !$this->valid() ) |
| { |
| return false; |
| } |
| |
| $isWindows = ( substr( php_uname( 's' ), 0, 7 ) == 'Windows' ) ? true : false; |
| $entry = $this->current(); |
| $type = $entry->getType(); |
| $fileName = $target . DIRECTORY_SEPARATOR. $entry->getPath(); |
| |
| if ( $type == ezcArchiveEntry::IS_LINK ) |
| { |
| $linkName = $target . DIRECTORY_SEPARATOR . $entry->getLink(); |
| if ( !file_exists( $linkName ) ) |
| { |
| throw new ezcBaseFileNotFoundException( $linkName, "link", "Hard link could not be created." ); |
| } |
| } |
| |
| $this->createDefaultDirectory( $fileName ); |
| |
| if ( !$keepExisting || ( !is_link( $fileName ) && !file_exists( $fileName ) ) ) |
| { |
| if ( ( file_exists( $fileName ) || is_link( $fileName ) ) && !is_dir( $fileName ) ) |
| { |
| unlink ( $fileName ); |
| } |
| |
| if ( !file_exists( $fileName ) ) // For example, directories are not removed. |
| { |
| switch ( $type ) |
| { |
| case ezcArchiveEntry::IS_CHARACTER_DEVICE: |
| if ( ezcBaseFeatures::hasFunction( 'posix_mknod' ) ) |
| { |
| posix_mknod( $fileName, POSIX_S_IFCHR, $entry->getMajor(), $entry->getMinor() ); |
| } |
| else |
| { |
| throw new ezcArchiveValueException( $type ); |
| } |
| break; |
| |
| case ezcArchiveEntry::IS_BLOCK_DEVICE: |
| if ( ezcBaseFeatures::hasFunction( 'posix_mknod' ) ) |
| { |
| posix_mknod( $fileName, POSIX_S_IFBLK, $entry->getMajor(), $entry->getMinor() ); |
| } |
| else |
| { |
| throw new ezcArchiveValueException( $type ); |
| } |
| break; |
| |
| case ezcArchiveEntry::IS_FIFO: |
| if ( ezcBaseFeatures::hasFunction( 'posix_mknod' ) ) |
| { |
| posix_mknod( $fileName, POSIX_S_IFIFO ); |
| } |
| else |
| { |
| throw new ezcArchiveValueException( $type ); |
| } |
| break; |
| |
| case ezcArchiveEntry::IS_SYMBOLIC_LINK: |
| if ( $isWindows ) |
| { |
| // FIXME.. need to be sure that target file |
| // already extracted before copying it to link destination. |
| $sourcePath = dirname( $fileName ) . '/' . $entry->getLink(); |
| $fileName = str_replace( '/', '\\', $fileName ); |
| copy( $sourcePath, $fileName ); |
| } |
| else |
| { |
| symlink( $entry->getLink(), $fileName ); |
| } |
| break; |
| |
| case ezcArchiveEntry::IS_LINK: |
| if ( $isWindows ) |
| { |
| copy( $target . DIRECTORY_SEPARATOR . $entry->getLink(), $fileName ); |
| } |
| else |
| { |
| link( $target . DIRECTORY_SEPARATOR . $entry->getLink(), $fileName ); |
| } |
| break; |
| |
| case ezcArchiveEntry::IS_DIRECTORY: |
| $permissions = $entry->getPermissions(); |
| |
| if ( $permissions === null || $permissions === false ) |
| { |
| $permissions = '0777'; |
| } |
| mkdir( $fileName, octdec( $permissions ), true ); |
| break; |
| |
| case ezcArchiveEntry::IS_FILE: |
| $this->writeCurrentDataToFile( $fileName ); |
| break; |
| |
| default: |
| throw new ezcArchiveValueException( $type ); |
| } |
| |
| if ( $type == ezcArchiveEntry::IS_SYMBOLIC_LINK && |
| ezcBaseFeatures::hasFunction( 'posix_geteuid' ) && |
| posix_geteuid() == 0 ) |
| { |
| $user = $entry->getUserId(); |
| $group = $entry->getGroupId(); |
| @lchown( $fileName, $user ); |
| @lchgrp( $fileName, $group ); |
| } |
| |
| // Change the username and group if the filename exists and if |
| // the intention is to keep it as a file. A zip archive |
| // stores the symlinks in a file; thus don't change these. |
| if ( file_exists( $fileName ) && ( $type == ezcArchiveEntry::IS_FILE || $type == ezcArchiveEntry::IS_DIRECTORY ) ) |
| { |
| $group = $entry->getGroupId(); |
| $user = $entry->getUserId(); |
| $time = $entry->getModificationTime(); |
| $perms = octdec( $entry->getPermissions() ); |
| |
| if ( $this->options && $this->options->extractCallback ) |
| { |
| $this->options->extractCallback->{$type == ezcArchiveEntry::IS_DIRECTORY ? 'createDirectoryCallback' : 'createFileCallback'}( $fileName, $perms, $user, $group ); |
| } |
| |
| if ( ezcBaseFeatures::hasFunction( 'posix_geteuid' ) && |
| posix_geteuid() === 0 ) |
| { |
| @chgrp( $fileName, $group ); |
| @chown( $fileName, $user ); |
| } |
| |
| if ( $perms != false ) |
| { |
| chmod( $fileName, $perms ); |
| } |
| |
| touch( $fileName, $time ); |
| } |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Search for the entry number. |
| * |
| * The two parameters here are the same as the PHP {@link http://www.php.net/fseek fseek()} method. |
| * The internal iterator position will be set by $offset added to $whence iterations forward. |
| * Where $whence is: |
| * - SEEK_SET, Set the position equal to $offset. |
| * - SEEK_CUR, Set the current position plus $offset. |
| * - SEEK_END, Set the last file in archive position plus $offset. |
| * |
| * This method returns true if the new position is valid, otherwise false. |
| * |
| * @throws ezcArchiveException |
| * if the archive is closed |
| * @param int $offset |
| * @param int $whence |
| * @return bool |
| */ |
| public function seek( $offset, $whence = SEEK_SET ) |
| { |
| if ( $this->file === null ) |
| { |
| throw new ezcArchiveException( "The archive is closed" ); |
| } |
| |
| // Cannot trust the current position if the current position is invalid. |
| if ( $whence == SEEK_CUR && $this->valid() == false ) |
| { |
| return false; |
| } |
| |
| if ( $whence == SEEK_END && !$this->completed ) |
| { |
| // read the entire archive. |
| $this->fileNumber = $this->entriesRead; |
| while ( $this->readCurrentFromArchive() ) |
| { |
| $this->fileNumber++; |
| } |
| } |
| |
| switch ( $whence ) |
| { |
| case SEEK_SET: |
| $requestedFileNumber = $offset; |
| break; |
| |
| case SEEK_CUR: |
| $requestedFileNumber = $offset + $this->fileNumber; |
| break; |
| |
| case SEEK_END: |
| $requestedFileNumber = $offset + $this->entriesRead - 1; |
| break; |
| |
| default: |
| return false; // Invalid whence. |
| } |
| |
| $this->fileNumber = $requestedFileNumber; |
| if ( $this->valid() ) |
| { |
| return true; |
| } |
| |
| if ( !$this->completed ) |
| { |
| $this->fileNumber = $this->entriesRead - 1; |
| |
| while ( $this->fileNumber != $requestedFileNumber ) |
| { |
| $this->fileNumber++; |
| if ( !$this->readCurrentFromArchive() ) |
| { |
| break; |
| } |
| } |
| |
| return $this->valid(); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Creates all the directories needed to create the file $file. |
| * |
| * @param string $file Path to a file, where all the base directory names will be created. |
| */ |
| protected function createDefaultDirectory( $file ) |
| { |
| // Does the directory exist? |
| $dirName = dirname( $file ); |
| |
| if ( !file_exists( $dirName ) ) |
| { |
| // Try to create the directory. |
| if ( substr( php_uname( 's' ), 0, 7 ) == 'Windows' ) |
| { |
| // make all slashes to be '/' |
| $dirName = str_replace( '/', '\\', $dirName ); |
| } |
| |
| // Call the callback, to see whether we need to change permissions |
| $permissions = 0777; |
| $dummy = null; |
| if ( $this->options && $this->options->extractCallback ) |
| { |
| $this->options->extractCallback->createDirectoryCallback( $dirName, $permissions, $dummy, $dummy ); |
| } |
| |
| mkdir( $dirName, $permissions, true ); |
| } |
| } |
| |
| /** |
| * Appends a file to the archive after the current entry. |
| * |
| * One or multiple files can be added directly after the current file. |
| * The remaining entries after the current are removed from the archive! |
| * |
| * The $files can either be a string or an array of strings. Which, respectively, represents a |
| * single file or multiple files. |
| * |
| * $prefix specifies the begin part of the $files path that should not be included in the archive. |
| * The files in the archive are always stored relatively. |
| * |
| * Example: |
| * <code> |
| * $tar = ezcArchive( "/tmp/my_archive.tar", ezcArchive::TAR ); |
| * |
| * // Append two files to the end of the archive. |
| * $tar->seek( 0, SEEK_END ); |
| * $tar->appendToCurrent( array( "/home/rb/file1.txt", "/home/rb/file2.txt" ), "/home/rb/" ); |
| * </code> |
| * |
| * When multiple files are added to the archive at the same time, thus using an array, does not |
| * necessarily produce the same archive as repeatively adding one file to the archive. |
| * For example, the Tar archive format, can detect that files hardlink to each other and will store |
| * it in a more efficient way. |
| * |
| * @throws ezcArchiveWriteException if one of the files cannot be written to the archive. |
| * @throws ezcFileReadException if one of the files cannot be read from the local filesystem. |
| * |
| * @param string|array(string) $files Array or a single path to a file. |
| * @param string $prefix First part of the path used in $files. |
| * @return bool |
| */ |
| public abstract function appendToCurrent( $files, $prefix ); |
| |
| /** |
| * Appends a file or directory to the end of the archive. Multiple files or directory can |
| * be added to the archive when an array is used as input parameter. |
| * |
| * @see appendToCurrent() |
| * |
| * @throws ezcArchiveWriteException if one of the files cannot be written to the archive. |
| * @throws ezcFileReadException if one of the files cannot be read from the local filesystem. |
| * |
| * @param string|array(string) $files Array or a single path to a file. |
| * @param string $prefix First part of the path used in $files. |
| * @return bool |
| */ |
| public abstract function append( $files, $prefix ); |
| |
| /** |
| * Truncates the archive to $fileNumber of files. |
| * |
| * The $fileNumber parameter specifies the amount of files that should remain. |
| * If the default value, zero, is used then the entire archive file is cleared. |
| * |
| * @param int $fileNumber |
| * @return bool |
| */ |
| public abstract function truncate( $fileNumber = 0 ); |
| |
| /** |
| * Writes the file data from the current entry to the given file. |
| * |
| * @param string $targetPath The absolute or relative path of the target file. |
| * @return void |
| */ |
| protected abstract function writeCurrentDataToFile( $targetPath ); |
| |
| /** |
| * Returns an array that lists the content of the archive. |
| * |
| * Use the getArchiveEntry method to get more information about an entry. |
| * |
| * @see __toString() |
| * |
| * @throws ezcArchiveException |
| * if the archive is closed |
| * @return array(string) |
| */ |
| public function getListing() |
| { |
| if ( $this->file === null ) |
| { |
| throw new ezcArchiveException( "The archive is closed" ); |
| } |
| |
| $result = array(); |
| $this->rewind(); |
| |
| do |
| { |
| $entry = $this->current(); |
| $result[] = rtrim( $entry->__toString(), "\n" ); // remove newline. |
| } while ( $this->next() ); |
| |
| return $result; |
| } |
| |
| /** |
| * Returns a string which represents all the entries from the archive. |
| * |
| * @throws ezcArchiveException |
| * if the archive is closed |
| * @return string |
| */ |
| public function __toString() |
| { |
| if ( $this->file === null ) |
| { |
| throw new ezcArchiveException( "The archive is closed" ); |
| } |
| |
| $result = ""; |
| $this->rewind(); |
| |
| while ( $this->valid() ) |
| { |
| $result .= $this->current()->__toString() . "\n"; |
| $this->next(); |
| } |
| |
| return $result; |
| } |
| |
| /** |
| * Extract entries from the archive to the target directory. |
| * |
| * All entries from the archive are extracted to the target directory. |
| * By default the files in the target directory are overwritten. |
| * If the $keepExisting is set to true, the files from the archive will not overwrite existing files. |
| * |
| * @see extractCurrent() |
| * |
| * @throws ezcArchiveException |
| * if the archive is closed |
| * @throws ezcArchiveEmptyException |
| * if the archive is invalid |
| * @param string $target Absolute or relative path of the directory. |
| * @param bool $keepExisting If set to true then the file will be overwritten, otherwise not. |
| * @return void |
| */ |
| public function extract( $target, $keepExisting = false ) |
| { |
| if ( $this->file === null ) |
| { |
| throw new ezcArchiveException( "The archive is closed" ); |
| } |
| |
| $this->rewind(); |
| if ( !$this->valid() ) |
| { |
| throw new ezcArchiveEmptyException( ); |
| } |
| |
| while ( $this->valid() ) |
| { |
| $this->extractCurrent( $target, $keepExisting ); |
| $this->next(); |
| } |
| } |
| |
| /** |
| * Returns true if the current archive is empty, otherwise false. |
| * |
| * @return bool |
| */ |
| public function isEmpty() |
| { |
| return ( $this->entriesRead == 0 ); |
| } |
| |
| /** |
| * Get the file entries from the archive. |
| * |
| * @param string|array(string) $files Array or a single path to a file. |
| * @param string $prefix First part of the path used in $files. |
| * |
| * @return ezcArchiveEntry |
| */ |
| protected function getEntries( $files, $prefix ) |
| { |
| if ( !is_array( $files ) ) |
| { |
| $files = array( $files ); |
| } |
| |
| // Check whether the files are correct. |
| foreach ( $files as $file ) |
| { |
| if ( !file_exists( $file ) && !is_link( $file ) ) |
| { |
| throw new ezcBaseFileNotFoundException( $file ); |
| } |
| } |
| |
| // Search for all the entries, because otherwise hardlinked files show up as an ordinary file. |
| return ezcArchiveEntry::getEntryFromFile( $files, $prefix ); |
| } |
| } |
| ?> |