Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: *//*** Abstract base class for all the readers** A reader is a compilation of serveral files that can be read** PHP versions 4 and 5** This library is free software; you can redistribute it and/or* modify it under the terms of the GNU Lesser General Public* License as published by the Free Software Foundation; either* version 2.1 of the License, or (at your option) any later version.** This library is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU* Lesser General Public License for more details.** You should have received a copy of the GNU Lesser General Public* License along with this library; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA** @category File Formats* @package File_Archive* @author Vincent Lascaux <vincentlascaux@php.net>* @copyright 1997-2005 The PHP Group* @license http://www.gnu.org/copyleft/lesser.html LGPL* @version CVS: $Id: Reader.php,v 1.36 2007/01/15 21:43:54 pfischer Exp $* @link http://pear.php.net/package/File_Archive*/require_once "PEAR.php";/*** Abstract base class for all the readers** A reader is a compilation of serveral files that can be read*/class File_Archive_Reader{/*** Move to the next file or folder in the reader** @return bool false iif no more files are available*/function next(){return false;}/*** Move to the next file whose name is in directory $filename* or is exactly $filename** @param string $filename Name of the file to find in the archive* @param bool $close If true, close the reader and search from the first file* @return bool whether the file was found in the archive or not*/function select($filename, $close = true){$std = $this->getStandardURL($filename);if (substr($std, -1)=='/') {$std = substr($std, 0, -1);}if ($close) {$error = $this->close();if (PEAR::isError($error)) {return $error;}}while (($error = $this->next()) === true) {$sourceName = $this->getFilename();if (empty($std) ||//$std is a file$std == $sourceName ||//$std is a directory(strncmp($std.'/', $sourceName, strlen($std)+1) == 0 &&strlen($sourceName) > strlen($std)+1)) {return true;}}return $error;}/*** Returns the standard path* Changes \ to /* Removes the .. and . from the URL* @param string $path a valid URL that may contain . or .. and \* @static*/function getStandardURL($path){if ($path == '.') {return '';}$std = str_replace("\\", "/", $path);while ($std != ($std = preg_replace("/[^\/:?]+\/\.\.\//", "", $std))) ;$std = str_replace("/./", "", $std);if (strncmp($std, "./", 2) == 0) {/*** If return value is going to be / (root on POSIX)*/if (substr($std, 2) === '/') {return $std;}return substr($std, 2);} else {return $std;}}/*** Returns the name of the file currently read by the reader** Warning: undefined behaviour if no call to next have been* done or if last call to next has returned false** @return string Name of the current file*/function getFilename(){return PEAR::raiseError("Reader abstract function call (getFilename)");}/*** Returns the list of filenames from the current pos to the end of the source* The source will be closed after having called this function* This function goes through the whole archive (which may be slow).* If you intend to work on the reader, doing it in one pass would be faster** @return array filenames from the current pos to the end of the source*/function getFileList(){$result = array();while ( ($error = $this->next()) === true) {$result[] = $this->getFilename();}$this->close();if (PEAR::isError($error)) {return $error;} else {return $result;}}/*** Returns an array of statistics about the file* (see the PHP stat function for more information)** The returned array may be empty, even if readers should try* their best to return as many data as possible*/function getStat() { return array(); }/*** Returns the MIME associated with the current file* The default function does that by looking at the extension of the file*/function getMime(){require_once "File/Archive/Reader/MimeList.php";return File_Archive_Reader_GetMime($this->getFilename());}/*** If the current file of the archive is a physical file,** @return the name of the physical file containing the data* or null if no such file exists** The data filename may not be the same as the filename.*/function getDataFilename() { return null; }/*** Reads some data from the current file* If the end of the file is reached, returns null* If $length is not specified, reads up to the end of the file* If $length is specified reads up to $length*/function getData($length = -1){return PEAR::raiseError("Reader abstract function call (getData)");}/*** Skip some data and returns how many bytes have been skipped* This is strictly equivalent to* return strlen(getData($length))* But could be far more efficient*/function skip($length = -1){$data = $this->getData($length);if (PEAR::isError($data)) {return $data;} else {return strlen($data);}}/*** Move the current position back of a given amount of bytes.* Not all readers may implement this function (a PEAR error will* be returned if the reader can't rewind)** @param int $length number of bytes to seek before the current pos* or -1 to move back to the begining of the current file* @return the number of bytes really rewinded (which may be less than* $length if the current pos is less than $length*/function rewind($length = -1){return PEAR::raiseError('Rewind function is not implemented on this reader');}/*** Returns the current offset in the current file*/function tell(){$offset = $this->rewind();$this->skip($offset);return $offset;}/*** Put back the reader in the state it was before the first call* to next()*/function close(){}/*** Sends the current file to the Writer $writer* The data will be sent by chunks of at most $bufferSize bytes* If $bufferSize <= 0 (default), the blockSize option is used*/function sendData(&$writer, $bufferSize = 0){if (PEAR::isError($writer)) {return $writer;}if ($bufferSize <= 0) {$bufferSize = File_Archive::getOption('blockSize');}$filename = $this->getDataFilename();if ($filename !== null) {$error = $writer->writeFile($filename);if (PEAR::isError($error)) {return $error;}} else {while (($data = $this->getData($bufferSize)) !== null) {if (PEAR::isError($data)) {return $data;}$error = $writer->writeData($data);if (PEAR::isError($error)) {return $error;}}}}/*** Sends the whole reader to $writer and close the reader** @param File_Archive_Writer $writer Where to write the files of the reader* @param bool $autoClose If true, close $writer at the end of the function.* Default value is true* @param int $bufferSize Size of the chunks that will be sent to the writer* If $bufferSize <= 0 (default value), the blockSize option is used*/function extract(&$writer, $autoClose = true, $bufferSize = 0){if (PEAR::isError($writer)) {$this->close();return $writer;}while (($error = $this->next()) === true) {if ($writer->newFileNeedsMIME()) {$mime = $this->getMime();} else {$mime = null;}$error = $writer->newFile($this->getFilename(),$this->getStat(),$mime);if (PEAR::isError($error)) {break;}$error = $this->sendData($writer, $bufferSize);if (PEAR::isError($error)) {break;}}$this->close();if ($autoClose) {$writer->close();}if (PEAR::isError($error)) {return $error;}}/*** Extract only one file (given by the URL)** @param string $filename URL of the file to extract from this* @param File_Archive_Writer $writer Where to write the file* @param bool $autoClose If true, close $writer at the end of the function* Default value is true* @param int $bufferSize Size of the chunks that will be sent to the writer* If $bufferSize <= 0 (default value), the blockSize option is used*/function extractFile($filename, &$writer,$autoClose = true, $bufferSize = 0){if (PEAR::isError($writer)) {return $writer;}if (($error = $this->select($filename)) === true) {$result = $this->sendData($writer, $bufferSize);if (!PEAR::isError($result)) {$result = true;}} else if ($error === false) {$result = PEAR::raiseError("File $filename not found");} else {$result = $error;}if ($autoClose) {$error = $writer->close();if (PEAR::isError($error)) {return $error;}}return $result;}/*** Return a writer that allows appending files to the archive* After having called makeAppendWriter, $this is closed and should not be* used until the returned writer is closed.** @return a writer that will allow to append files to an existing archive* @see makeWriter*/function makeAppendWriter(){require_once "File/Archive/Predicate/False.php";return $this->makeWriterRemoveFiles(new File_Archive_Predicate_False());}/*** Return a writer that has the same properties as the one returned by* makeAppendWriter, but after having removed all the files that follow a* given predicate.* After a call to makeWriterRemoveFiles, $this is closed and should not* be used until the returned writer is closed** @param File_Archive_Predicate $pred the predicate verified by removed files* @return File_Archive_Writer that allows to append files to the archive*/function makeWriterRemoveFiles($pred){return PEAR::raiseError("Reader abstract function call (makeWriterRemoveFiles)");}/*** Returns a writer that removes the current file* This is a syntaxic sugar for makeWriterRemoveFiles(new File_Archive_Predicate_Current());*/function makeWriterRemove(){require_once "File/Archive/Predicate/Current.php";return $this->makeWriterRemoveFiles(new File_Archive_Predicate_Current());}/*** Removes the current file from the reader*/function remove(){$writer = $this->makeWriterRemove();if (PEAR::isError($writer)) {return $writer;}$writer->close();}/*** Return a writer that has the same properties as the one returned by makeWriter, but after* having removed a block of data from the current file. The writer will append data to the current file* no data (other than the block) will be removed** @param array Lengths of the blocks. The first one will be discarded, the second one kept, the third* one discarded... If the sum of the blocks is less than the size of the file, the comportment is the* same as if a last block was set in the array to reach the size of the file* if $length is -1, the file is truncated from the specified pos* It is possible to specify blocks of size 0* @param int $seek relative pos of the block*/function makeWriterRemoveBlocks($blocks, $seek = 0){return PEAR::raiseError("Reader abstract function call (makeWriterRemoveBlocks)");}}?>