ConLite/conlite/plugins/pluginmanager/docs/pear/File/Archive.php

1435 Zeilen
51 KiB
PHP

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Factory to access the most common File_Archive features
* It uses lazy include, so you dont have to include the files from
* File/Archive/* directories
*
* 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: Archive.php 2 2010-11-23 14:32:26Z oldperl $
* @link http://pear.php.net/package/File_Archive
*/
/**
* To have access to PEAR::isError and PEAR::raiseError
* We should probably use lazy include and remove this inclusion...
*/
require_once "PEAR.php";
function File_Archive_cleanCache($file, $group)
{
$file = split('_', $file);
if (count($file) != 3) {
return false; //not a File_Archive file, keep it
}
$name = $file[2];
$name = urldecode($name);
$group = $file[1];
//clean the cache only for files in File_Archive groups
return substr($group, 0, 11) == 'FileArchive' &&
!file_exists($name); //and only if the related file no longer exists
}
/**
* Factory to access the most common File_Archive features
* It uses lazy include, so you dont have to include the files from
* File/Archive/* directories
*/
class File_Archive
{
function& _option($name)
{
static $container = array(
'zipCompressionLevel' => 9,
'gzCompressionLevel' => 9,
'tmpDirectory' => '.',
'cache' => null,
'appendRemoveDuplicates' => false,
'blockSize' => 65536,
'cacheCondition' => false
);
return $container[$name];
}
/**
* Sets an option that will be used by default by all readers or writers
* Option names are case sensitive
* Currently, the following options are used:
*
* "cache"
* Instance of a Cache_Lite object used to cache some compressed
* data to speed up future compressions of files
* Default: null (no cache used)
*
* "zipCompressionLevel"
* Value between 0 and 9 specifying the default compression level used
* by Zip writers (0 no compression, 9 highest compression)
* Default: 9
*
* "gzCompressionLevel"
* Value between 0 and 9 specifying the default compression level used
* by Gz writers (0 no compression, 9 highest compression)
* Default: 9
*
* "tmpDirectory"
* Directory where the temporary files generated by File_Archive will
* be created
* Default: '.'
*
* "appendRemoveDuplicates"
* If set to true, the appender created will by default remove the
* file present in the archive when adding a new one. This will slow the
* appending of files to archives
* Default: false
*
* "blockSize"
* To transfer data from a reader to a writer, some chunks a read from the
* source and written to the writer. This parameter controls the size of the
* chunks
* Default: 64kB
*
* "cacheCondition"
* This parameter specifies when a cache should be used. When the cache is
* used, the data of the reader is saved in a temporary file for future access.
* The cached reader will be read only once, even if you read it several times.
* This can be usefull to read compressed files or downloaded files (from http or ftp)
* The possible values for this option are
* - false: never use cache
* - a regexp: A cache will be used if the specified URL matches the regexp
* preg_match is used
* Default: false
* Example: '/^(http|ftp):\/\//' will cache all files downloaded via http or ftp
*
*/
function setOption($name, $value)
{
$option =& File_Archive::_option($name);
$option = $value;
if ($name == 'cache' && $value !== null) {
//TODO: ask to Cache_Lite to allow that
$value->_fileNameProtection = false;
}
}
/**
* Retrieve the value of an option
*/
function getOption($name)
{
return File_Archive::_option($name);
}
/**
* Create a reader to read the URL $URL.
* If the URL is a directory, it will recursively read that directory.
* If $uncompressionLevel is not null, the archives (files with extension
* tar, zip, gz or tgz) will be considered as directories (up to a depth of
* $uncompressionLevel if $uncompressionLevel > 0). The reader will only
* read files with a directory depth of $directoryDepth. It reader will
* replace the given URL ($URL) with $symbolic in the public filenames
* The default symbolic name is the last filename in the URL (or '' for
* directories)
*
* Examples:
* Considere the following file system
* <pre>
* a.txt
* b.tar (archive that contains the following files)
* c.txt
* d.tgz (archive that contains the following files)
* e.txt
* dir1/
* f.txt
* dir2/
* g.txt
* dir3/
* h.tar (archive that contains the following files)
* i.txt
* </pre>
*
* read('.') will return a reader that gives access to following
* files (recursively read current dir):
* <pre>
* a.txt
* b.tar
* dir2/g.txt
* dir2/dir3/h.tar
* </pre>
*
* read('.', 'myBaseDir') will return the following reader:
* <pre>
* myBaseDir/a.txt
* myBaseDir/b.tar
* myBaseDir/dir2/g.txt
* myBaseDir/dir2/dir3/h.tar
* </pre>
*
* read('.', '', -1) will return the following reader (uncompress
* everything)
* <pre>
* a.txt
* b.tar/c.txt
* b.tar/d.tgz/e.txt
* b.tar/d.tgz/dir1/f.txt
* dir2/g.txt
* dir2/dir3/h.tar/i.txt
* </pre>
*
* read('.', '', 1) will uncompress only one level (so d.tgz will
* not be uncompressed):
* <pre>
* a.txt
* b.tar/c.txt
* b.tar/d.tgz
* dir2/g.txt
* dir2/dir3/h.tar/i.txt
* </pre>
*
* read('.', '', 0, 0) will not recurse into subdirectories
* <pre>
* a.txt
* b.tar
* </pre>
*
* read('.', '', 0, 1) will recurse only one level in
* subdirectories
* <pre>
* a.txt
* b.tar
* dir2/g.txt
* </pre>
*
* read('.', '', -1, 2) will uncompress everything and recurse in
* only 2 levels in subdirectories or archives
* <pre>
* a.txt
* b.tar/c.txt
* b.tar/d.tgz/e.txt
* dir2/g.txt
* </pre>
*
* The recursion level is determined by the real path, not the symbolic one.
* So read('.', 'myBaseDir', -1, 2) will result to the same files:
* <pre>
* myBaseDir/a.txt
* myBaseDir/b.tar/c.txt
* myBaseDir/b.tar/d.tgz/e.txt (accepted because the real depth is 2)
* myBaseDir/dir2/g.txt
* </pre>
*
* Use readSource to do the same thing, reading from a specified reader instead of
* reading from the system files
*
* To read a single file, you can do read('a.txt', 'public_name.txt')
* If no public name is provided, the default one is the name of the file
* read('dir2/g.txt') contains the single file named 'g.txt'
* read('b.tar/c.txt') contains the single file named 'c.txt'
*
* Note: This function uncompress files reading their extension
* The compressed files must have a tar, zip, gz or tgz extension
* Since it is impossible for some URLs to use is_dir or is_file, this
* function may not work with
* URLs containing folders which name ends with such an extension
*/
function readSource(&$source, $URL, $symbolic = null,
$uncompression = 0, $directoryDepth = -1)
{
return File_Archive::_readSource($source, $URL, $reachable, $baseDir,
$symbolic, $uncompression, $directoryDepth);
}
/**
* This function performs exactly as readSource, but with two additional parameters
* ($reachable and $baseDir) that will be set so that $reachable."/".$baseDir == $URL
* and $reachable can be reached (in case of error)
*
* @access private
*/
function _readSource(&$toConvert, $URL, &$reachable, &$baseDir, $symbolic = null,
$uncompression = 0, $directoryDepth = -1)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
if (is_array($URL)) {
$converted = array();
foreach($URL as $key => $foo) {
$converted[] =& File_Archive::_convertToReader($URL[$key]);
}
return File_Archive::readMulti($converted);
}
//No need to uncompress more than $directoryDepth
//That's not perfect, and some archives will still be uncompressed just
//to be filtered out :(
if ($directoryDepth >= 0) {
$uncompressionLevel = min($uncompression, $directoryDepth);
} else {
$uncompressionLevel = $uncompression;
}
require_once 'File/Archive/Reader.php';
$std = File_Archive_Reader::getStandardURL($URL);
//Modify the symbolic name if necessary
$slashPos = strrpos($std, '/');
if ($symbolic === null) {
if ($slashPos === false) {
$realSymbolic = $std;
} else {
$realSymbolic = substr($std, $slashPos+1);
}
} else {
$realSymbolic = $symbolic;
}
if ($slashPos !== false) {
$baseFile = substr($std, 0, $slashPos+1);
$lastFile = substr($std, $slashPos+1);
} else {
$baseFile = '';
$lastFile = $std;
}
if (strpos($lastFile, '*')!==false ||
strpos($lastFile, '?')!==false) {
//We have to build a regexp here
$regexp = str_replace(
array('\*', '\?'),
array('[^/]*', '[^/]'),
preg_quote($lastFile)
);
$result = File_Archive::_readSource($source, $baseFile,
$reachable, $baseDir, null, 0, -1);
return File_Archive::filter(
File_Archive::predEreg('^'.$regexp.'$'),
$result
);
}
//If the URL can be interpreted as a directory, and we are reading from the file system
if ((empty($URL) || is_dir($URL)) && $source === null) {
require_once "File/Archive/Reader/Directory.php";
if ($uncompressionLevel != 0) {
require_once "File/Archive/Reader/Uncompress.php";
$result = new File_Archive_Reader_Uncompress(
new File_Archive_Reader_Directory($std, '', $directoryDepth),
$uncompressionLevel
);
} else {
$result = new File_Archive_Reader_Directory($std, '', $directoryDepth);
}
if ($directoryDepth >= 0) {
require_once 'File/Archive/Reader/Filter.php';
require_once 'File/Archive/Predicate/MaxDepth.php';
$tmp =& File_Archive::filter(
new File_Archive_Predicate_MaxDepth($directoryDepth),
$result
);
unset($result);
$result =& $tmp;
}
if (!empty($realSymbolic)) {
if ($symbolic === null) {
$realSymbolic = '';
}
require_once "File/Archive/Reader/ChangeName/AddDirectory.php";
$tmp = new File_Archive_Reader_ChangeName_AddDirectory(
$realSymbolic,
$result
);
unset($result);
$result =& $tmp;
}
//If the URL can be interpreted as a file, and we are reading from the file system
} else if (is_file($URL) && substr($URL, -1)!='/' && $source === null) {
require_once "File/Archive/Reader/File.php";
$result = new File_Archive_Reader_File($URL, $realSymbolic);
//Else, we will have to build a complex reader
} else {
require_once "File/Archive/Reader/File.php";
$realPath = $std;
// Try to find a file with a known extension in the path (
// (to manage URLs like archive.tar/directory/file)
$pos = 0;
do {
if ($pos+1<strlen($realPath)) {
$pos = strpos($realPath, '/', $pos+1);
} else {
$pos = false;
}
if ($pos === false) {
$pos = strlen($realPath);
}
$file = substr($realPath, 0, $pos);
$baseDir = substr($realPath, $pos+1);
$dotPos = strrpos($file, '.');
$extension = '';
if ($dotPos !== false) {
$extension = substr($file, $dotPos+1);
}
} while ($pos < strlen($realPath) &&
(!File_Archive::isKnownExtension($extension) ||
(is_dir($file) && $source==null)));
$reachable = $file;
//If we are reading from the file system
if ($source === null) {
//Create a file reader
$result = new File_Archive_Reader_File($file);
} else {
//Select in the source the file $file
require_once "File/Archive/Reader/Select.php";
$result = new File_Archive_Reader_Select($file, $source);
}
require_once "File/Archive/Reader/Uncompress.php";
$tmp = new File_Archive_Reader_Uncompress($result, $uncompressionLevel);
unset($result);
$result = $tmp;
//Select the requested folder in the uncompress reader
$isDir = $result->setBaseDir($std);
if (PEAR::isError($isDir)) {
return $isDir;
}
if ($isDir && $symbolic==null) {
//Default symbolic name for directories is empty
$realSymbolic = '';
}
if ($directoryDepth >= 0) {
//Limit the maximum depth if necessary
require_once "File/Archive/Reader/Filter.php";
require_once "File/Archive/Predicate/MaxDepth.php";
$tmp = new File_Archive_Reader_Filter(
new File_Archive_Predicate(
$directoryDepth +
substr_count(substr($std, $pos+1), '/')
),
$result
);
unset($result);
$result =& $tmp;
}
if ($std != $realSymbolic) {
require_once "File/Archive/Reader/ChangeName/Directory.php";
//Change the base name to the symbolic one if necessary
$tmp = new File_Archive_Reader_ChangeName_Directory(
$std,
$realSymbolic,
$result
);
unset($result);
$result =& $tmp;
}
}
$cacheCondition = File_Archive::getOption('cacheCondition');
if ($cacheCondition !== false &&
preg_match($cacheCondition, $URL)) {
$tmp =& File_Archive::cache($result);
unset($result);
$result =& $tmp;
}
return $result;
}
function read($URL, $symbolic = null,
$uncompression = 0, $directoryDepth = -1)
{
$source = null;
return File_Archive::readSource($source, $URL, $symbolic, $uncompression, $directoryDepth);
}
/**
* Create a file reader on an uploaded file. The reader will read
* $_FILES[$name]['tmp_name'] and will have $_FILES[$name]['name']
* as a symbolic filename.
*
* A PEAR error is returned if one of the following happen
* - $_FILES[$name] is not set
* - $_FILES[$name]['error'] is not 0
* - is_uploaded_file returns false
*
* @param string $name Index of the file in the $_FILES array
* @return File_Archive_Reader File reader on the uploaded file
*/
function readUploadedFile($name)
{
if (!isset($_FILES[$name])) {
return PEAR::raiseError("File $name has not been uploaded");
}
switch ($_FILES[$name]['error']) {
case 0:
//No error
break;
case 1:
return PEAR::raiseError(
"The upload size limit didn't allow to upload file ".
$_FILES[$name]['name']
);
case 2:
return PEAR::raiseError(
"The form size limit didn't allow to upload file ".
$_FILES[$name]['name']
);
case 3:
return PEAR::raiseError(
"The file was not entirely uploaded"
);
case 4:
return PEAR::raiseError(
"The uploaded file is empty"
);
default:
return PEAR::raiseError(
"Unknown error ".$_FILES[$name]['error']." in file upload. ".
"Please, report a bug"
);
}
if (!is_uploaded_file($_FILES[$name]['tmp_name'])) {
return PEAR::raiseError("The file is not an uploaded file");
}
require_once "File/Archive/Reader/File.php";
return new File_Archive_Reader_File(
$_FILES[$name]['tmp_name'],
$_FILES[$name]['name'],
$_FILES[$name]['type']
);
}
/**
* Adds a cache layer above the specified reader
* The data of the reader is saved in a temporary file for future access.
* The cached reader will be read only once, even if you read it several times.
* This can be usefull to read compressed files or downloaded files (from http or ftp)
*
* @param mixed $toConvert The reader to cache
* It can be a File_Archive_Reader or a string, which will be converted using the
* read function
*/
function cache(&$toConvert)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
require_once 'File/Archive/Reader/Cache.php';
return new File_Archive_Reader_Cache($source);
}
/**
* Try to interpret the object as a reader
* Strings are converted to readers using File_Archive::read
* Arrays are converted to readers using File_Archive::readMulti
*
* @access private
*/
function &_convertToReader(&$source)
{
if (is_string($source)) {
$cacheCondition = File_Archive::getOption('cacheCondition');
if ($cacheCondition !== false &&
preg_match($cacheCondition, $source)) {
$obj = File_Archive::cache(File_Archive::read($source));
return $obj;
} else {
$obj = File_Archive::read($source);
return $obj;
}
} else if (is_array($source)) {
return File_Archive::readMulti($source);
} else {
return $source;
}
}
/**
* Try to interpret the object as a writer
* Strings are converted to writers using File_Archive::appender
* Arrays are converted to writers using a multi writer
*
* @access private
*/
function &_convertToWriter(&$dest)
{
if (is_string($dest)) {
$obj =& File_Archive::appender($dest);
return $obj;
} else if (is_array($dest)) {
require_once 'File/Archive/Writer/Multi.php';
$writer = new File_Archive_Writer_Multi();
foreach($dest as $key => $foo) {
$writer->addWriter($dest[$key]);
}
return $writer;
} else {
return $dest;
}
}
/**
* Check if a file with a specific extension can be read as an archive
* with File_Archive::read*
* This function is case sensitive.
*
* @param string $extension the checked extension
* @return bool whether this file can be understood reading its extension
* Currently, supported extensions are tar, zip, jar, gz, tgz,
* tbz, bz2, bzip2, ar, deb
*/
function isKnownExtension($extension)
{
return $extension == 'tar' ||
$extension == 'zip' ||
$extension == 'jar' ||
$extension == 'gz' ||
$extension == 'tgz' ||
$extension == 'tbz' ||
$extension == 'bz2' ||
$extension == 'bzip2' ||
$extension == 'ar' ||
$extension == 'deb' /* ||
$extension == 'cab' ||
$extension == 'rar' */;
}
/**
* Create a reader that will read the single file source $source as
* a specific archive
*
* @param string $extension determines the kind of archive $source contains
* $extension is case sensitive
* @param File_Archive_Reader $source stores the archive
* @param bool $sourceOpened specifies if the archive is already opened
* if false, next will be called on source
* Closing the returned archive will close $source iif $sourceOpened
* is true
* @return A File_Archive_Reader that uncompresses the archive contained in
* $source interpreting it as a $extension archive
* If $extension is not handled return false
*/
function readArchive($extension, &$toConvert, $sourceOpened = false)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
switch($extension) {
case 'tgz':
return File_Archive::readArchive('tar',
File_Archive::readArchive('gz', $source, $sourceOpened)
);
case 'tbz':
return File_Archive::readArchive('tar',
File_Archive::readArchive('bz2', $source, $sourceOpened)
);
case 'tar':
require_once 'File/Archive/Reader/Tar.php';
return new File_Archive_Reader_Tar($source, $sourceOpened);
case 'gz':
case 'gzip':
require_once 'File/Archive/Reader/Gzip.php';
return new File_Archive_Reader_Gzip($source, $sourceOpened);
case 'zip':
case 'jar':
require_once 'File/Archive/Reader/Zip.php';
return new File_Archive_Reader_Zip($source, $sourceOpened);
case 'bz2':
case 'bzip2':
require_once 'File/Archive/Reader/Bzip2.php';
return new File_Archive_Reader_Bzip2($source, $sourceOpened);
case 'deb':
case 'ar':
require_once 'File/Archive/Reader/Ar.php';
return new File_Archive_Reader_Ar($source, $sourceOpened);
/* case 'cab':
require_once 'File/Archive/Reader/Cab.php';
return new File_Archive_Reader_Cab($source, $sourceOpened);
case 'rar':
require_once "File/Archive/Reader/Rar.php";
return new File_Archive_Reader_Rar($source, $sourceOpened); */
default:
return false;
}
}
/**
* Contains only one file with data read from a memory buffer
*
* @param string $memory content of the file
* @param string $filename public name of the file
* @param array $stat statistics of the file. Index 7 (size) will be
* overwritten to match the size of $memory
* @param string $mime mime type of the file. Default will determine the
* mime type thanks to the extension of $filename
* @see File_Archive_Reader_Memory
*/
function readMemory($memory, $filename, $stat=array(), $mime=null)
{
require_once "File/Archive/Reader/Memory.php";
return new File_Archive_Reader_Memory($memory, $filename, $stat, $mime);
}
/**
* Contains several other sources. Take care the sources don't have several
* files with the same filename. The sources are given as a parameter, or
* can be added thanks to the reader addSource method
*
* @param array $sources Array of strings or readers that will be added to
* the multi reader. If the parameter is a string, a reader will be
* built thanks to the read function
* @see File_Archive_Reader_Multi, File_Archive::read()
*/
function readMulti($sources = array())
{
require_once "File/Archive/Reader/Multi.php";
$result = new File_Archive_Reader_Multi();
foreach ($sources as $index => $foo) {
$s =& File_Archive::_convertToReader($sources[$index]);
if (PEAR::isError($s)) {
return $s;
} else {
$result->addSource($s);
}
}
return $result;
}
/**
* Make the files of a source appear as one large file whose content is the
* concatenation of the content of all the files
*
* @param File_Archive_Reader $toConvert The source whose files must be
* concatened
* @param string $filename name of the only file of the created reader
* @param array $stat statistics of the file. Index 7 (size) will be
* overwritten to match the total size of the files
* @param string $mime mime type of the file. Default will determine the
* mime type thanks to the extension of $filename
* @see File_Archive_Reader_Concat
*/
function readConcat(&$toConvert, $filename, $stat=array(), $mime=null)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
require_once "File/Archive/Reader/Concat.php";
return new File_Archive_Reader_Concat($source, $filename, $stat, $mime);
}
/**
* Changes the name of each file in a reader by applying a custom function
* The function must return false if the file is to be discarded, or the new
* name of the file else
*
* @param Callable $function Function called to modify the name of the file
* $function takes the name of the file as a parameter and returns the
* new name, or false if the file must be discarded
* @param File_Archive_Reader $toConvert The files of this source will be
* modified
* @return File_Archive_Reader a new reader that contains the same files
* as $toConvert but with a different name
*/
function changeName($function, &$toConvert)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
require_once "File/Archive/Reader/ChangeName.php";
return new File_Archive_Reader_RemoveDirectory($source);
}
/**
* Removes from a source the files that do not follow a given predicat
*
* @param File_Archive_Predicate $predicate Only the files for which
* $predicate->isTrue() will be kept
* @param File_Archive_Reader $source Source that will be filtered
* @see File_Archive_Reader_Filter
*/
function filter($predicate, &$toConvert)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
require_once "File/Archive/Reader/Filter.php";
return new File_Archive_Reader_Filter($predicate, $source);
}
/**
* Predicate that always evaluate to true
*
* @see File_Archive_Predicate_True
*/
function predTrue()
{
require_once "File/Archive/Predicate/True.php";
return new File_Archive_Predicate_True();
}
/**
* Predicate that always evaluate to false
*
* @see File_Archive_Predicate_False
*/
function predFalse()
{
require_once "File/Archive/Predicate/False.php";
return new File_Archive_Predicate_False();
}
/**
* Predicate that evaluates to the logical AND of the parameters
* You can add other predicates thanks to the
* File_Archive_Predicate_And::addPredicate() function
*
* @param File_Archive_Predicate (any number of them)
* @see File_Archive_Predicate_And
*/
function predAnd()
{
require_once "File/Archive/Predicate/And.php";
$pred = new File_Archive_Predicate_And();
$args = func_get_args();
foreach ($args as $p) {
$pred->addPredicate($p);
}
return $pred;
}
/**
* Predicate that evaluates to the logical OR of the parameters
* You can add other predicates thanks to the
* File_Archive_Predicate_Or::addPredicate() function
*
* @param File_Archive_Predicate (any number of them)
* @see File_Archive_Predicate_Or
*/
function predOr()
{
require_once "File/Archive/Predicate/Or.php";
$pred = new File_Archive_Predicate_Or();
$args = func_get_args();
foreach ($args as $p) {
$pred->addPredicate($p);
}
return $pred;
}
/**
* Negate a predicate
*
* @param File_Archive_Predicate $pred Predicate to negate
* @see File_Archive_Predicate_Not
*/
function predNot($pred)
{
require_once "File/Archive/Predicate/Not.php";
return new File_Archive_Predicate_Not($pred);
}
/**
* Evaluates to true iif the file is larger than a given size
*
* @param int $size the minimal size of the files (in Bytes)
* @see File_Archive_Predicate_MinSize
*/
function predMinSize($size)
{
require_once "File/Archive/Predicate/MinSize.php";
return new File_Archive_Predicate_MinSize($size);
}
/**
* Evaluates to true iif the file has been modified after a given time
*
* @param int $time Unix timestamp of the minimal modification time of the
* files
* @see File_Archive_Predicate_MinTime
*/
function predMinTime($time)
{
require_once "File/Archive/Predicate/MinTime.php";
return new File_Archive_Predicate_MinTime($time);
}
/**
* Evaluates to true iif the file has less that a given number of
* directories in its path
*
* @param int $depth Maximal number of directories in path of the files
* @see File_Archive_Predicate_MaxDepth
*/
function predMaxDepth($depth)
{
require_once "File/Archive/Predicate/MaxDepth.php";
return new File_Archive_Predicate_MaxDepth($depth);
}
/**
* Evaluates to true iif the extension of the file is in a given list
*
* @param array or string $list List or comma separated string of possible
* extension of the files
* @see File_Archive_Predicate_Extension
*/
function predExtension($list)
{
require_once "File/Archive/Predicate/Extension.php";
return new File_Archive_Predicate_Extension($list);
}
/**
* Evaluates to true iif the MIME type of the file is in a given list
*
* @param array or string $list List or comma separated string of possible
* MIME types of the files. You may enter wildcards like "image/*" to
* select all the MIME in class image
* @see File_Archive_Predicate_MIME, MIME_Type::isWildcard()
*/
function predMIME($list)
{
require_once "File/Archive/Predicate/MIME.php";
return new File_Archive_Predicate_MIME($list);
}
/**
* Evaluates to true iif the name of the file follow a given regular
* expression
*
* @param string $ereg regular expression that the filename must follow
* @see File_Archive_Predicate_Ereg, ereg()
*/
function predEreg($ereg)
{
require_once "File/Archive/Predicate/Ereg.php";
return new File_Archive_Predicate_Ereg($ereg);
}
/**
* Evaluates to true iif the name of the file follow a given regular
* expression (case insensitive version)
*
* @param string $ereg regular expression that the filename must follow
* @see File_Archive_Predicate_Eregi, eregi
*/
function predEregi($ereg)
{
require_once "File/Archive/Predicate/Eregi.php";
return new File_Archive_Predicate_Eregi($ereg);
}
/**
* Evaluates to true only after a given number of evaluations
* This can be used to select files by index since the evaluation is done
* once per file
*
* @param array The indexes for which the returned predicate will return true
* are the keys of the array
* The predicate will return true if isset($indexes[$pos])
*/
function predIndex($indexes)
{
require_once "File/Archive/Predicate/Index.php";
return new File_Archive_Predicate_Index($indexes);
}
/**
* Custom predicate built by supplying a string expression
*
* Here are different ways to create a predicate that keeps only files
* with names shorter than 100 chars
* <sample>
* File_Archive::predCustom("return strlen($name)<100;")
* File_Archive::predCustom("strlen($name)<100;")
* File_Archive::predCustom("strlen($name)<100")
* File_Archive::predCustom("strlen($source->getFilename())<100")
* </sample>
*
* @param string $expression String containing an expression that evaluates
* to a boolean. If the expression doesn't contain a return
* statement, it will be added at the begining of the expression
* A ';' will be added at the end of the expression so that you don't
* have to write it. You may use the $name variable to refer to the
* current filename (with path...), $time for the modification time
* (unix timestamp), $size for the size of the file in bytes, $mime
* for the MIME type of the file
* @see File_Archive_Predicate_Custom
*/
function predCustom($expression)
{
require_once "File/Archive/Predicate/Custom.php";
return new File_Archive_Predicate_Custom($expression);
}
/**
* Send the files as a mail attachment
*
* @param Mail $mail Object used to send mail (see Mail::factory)
* @param array or String $to An array or a string with comma separated
* recipients
* @param array $headers The headers that will be passed to the Mail_mime
* object
* @param string $message Text body of the mail
* @see File_Archive_Writer_Mail
*/
function toMail($to, $headers, $message, $mail = null)
{
require_once "File/Archive/Writer/Mail.php";
return new File_Archive_Writer_Mail($to, $headers, $message, $mail);
}
/**
* Write the files on the hard drive
*
* @param string $baseDir if specified, the files will be created in that
* directory. If they don't exist, the directories will automatically
* be created
* @see File_Archive_Writer_Files
*/
function toFiles($baseDir = "")
{
require_once "File/Archive/Writer/Files.php";
return new File_Archive_Writer_Files($baseDir);
}
/**
* Send the content of the files to a memory buffer
*
* toMemory returns a writer where the data will be written.
* In this case, the data is accessible using the getData member
*
* toVariable returns a writer that will write into the given
* variable
*
* @param out $data if specified, the data will be written to this buffer
* Else, you can retrieve the buffer with the
* File_Archive_Writer_Memory::getData() function
* @see File_Archive_Writer_Memory
*/
function toMemory()
{
$v = '';
return File_Archive::toVariable($v);
}
function toVariable(&$v)
{
require_once "File/Archive/Writer/Memory.php";
return new File_Archive_Writer_Memory($v);
}
/**
* Duplicate the writing operation on two writers
*
* @param File_Archive_Writer $a, $b writers where data will be duplicated
* @see File_Archive_Writer_Multi
*/
function toMulti(&$aC, &$bC)
{
$a =& File_Archive::_convertToWriter($aC);
$b =& File_Archive::_convertToWriter($bC);
if (PEAR::isError($a)) {
return $a;
}
if (PEAR::isError($b)) {
return $b;
}
require_once "File/Archive/Writer/Multi.php";
$writer = new File_Archive_Writer_Multi();
$writer->addWriter($a);
$writer->addWriter($b);
return $writer;
}
/**
* Send the content of the files to the standard output (so to the client
* for a website)
*
* @param bool $sendHeaders If true some headers will be sent to force the
* download of the file. Default value is true
* @see File_Archive_Writer_Output
*/
function toOutput($sendHeaders = true)
{
require_once "File/Archive/Writer/Output.php";
return new File_Archive_Writer_Output($sendHeaders);
}
/**
* Compress the data to a tar, gz, tar/gz or zip format
*
* @param string $filename name of the archive file
* @param File_Archive_Writer $innerWriter writer where the archive will be
* written
* @param string $type can be one of tgz, tbz, tar, zip, gz, gzip, bz2,
* bzip2 (default is the extension of $filename) or any composition
* of them (for example tar.gz or tar.bz2). The case of this
* parameter is not important.
* @param array $stat Statistics of the archive (see stat function)
* @param bool $autoClose If set to true, $innerWriter will be closed when
* the returned archive is close. Default value is true.
*/
function toArchive($filename, &$toConvert, $type = null,
$stat = array(), $autoClose = true)
{
$innerWriter =& File_Archive::_convertToWriter($toConvert);
if (PEAR::isError($innerWriter)) {
return $innerWriter;
}
$shortcuts = array("tgz" , "tbz" );
$reals = array("tar.gz", "tar.bz2");
if ($type === null) {
$extensions = strtolower($filename);
} else {
$extensions = strtolower($type);
}
$extensions = explode('.', str_replace($shortcuts, $reals, $extensions));
if ($innerWriter !== null) {
$writer =& $innerWriter;
} else {
$writer = File_Archive::toFiles();
}
$nbCompressions = 0;
$currentFilename = $filename;
while (($extension = array_pop($extensions)) !== null) {
unset($next);
switch($extension) {
case "tar":
require_once "File/Archive/Writer/Tar.php";
$next = new File_Archive_Writer_Tar(
$currentFilename, $writer, $stat, $autoClose
);
unset($writer); $writer =& $next;
break;
case "zip":
require_once "File/Archive/Writer/Zip.php";
$next = new File_Archive_Writer_Zip(
$currentFilename, $writer, $stat, $autoClose
);
unset($writer); $writer =& $next;
break;
case "gz":
case "gzip":
require_once "File/Archive/Writer/Gzip.php";
$next = new File_Archive_Writer_Gzip(
$currentFilename, $writer, $stat, $autoClose
);
unset($writer); $writer =& $next;
break;
case "bz2":
case "bzip2":
require_once "File/Archive/Writer/Bzip2.php";
$next = new File_Archive_Writer_Bzip2(
$currentFilename, $writer, $stat, $autoClose
);
unset($writer); $writer =& $next;
break;
case "deb":
case "ar":
require_once "File/Archive/Writer/Ar.php";
$next = new File_Archive_Writer_Ar(
$currentFilename, $writer, $stat, $autoClose
);
unset($writer); $writer =& $next;
break;
default:
if ($type !== null || $nbCompressions == 0) {
return PEAR::raiseError("Archive $extension unknown");
}
break;
}
$nbCompressions ++;
$autoClose = true;
$currentFilename = implode(".", $extensions);
}
return $writer;
}
/**
* File_Archive::extract($source, $dest) is equivalent to $source->extract($dest)
* If $source is a PEAR error, the error will be returned
* It is thus easier to use this function than $source->extract, since it reduces the number of
* error checking and doesn't force you to define a variable $source
*
* You may use strings as source and dest. In that case the source is automatically
* converted to a reader using File_Archive::read and the dest is converted to a
* writer using File_Archive::appender
* Since PHP doesn't allow to pass literal strings by ref, you will have to use temporary
* variables.
* File_Archive::extract($src = 'archive.zip/', $dest = 'dir') will extract the archive to 'dir'
* It is the same as
* File_Archive::extract(
* File_Archive::read('archive.zip/'),
* File_Archive::appender('dir')
* );
* You may use any variable in the extract function ($from/$to, $a/$b...).
*
* @param File_Archive_Reader $source The source that will be read
* @param File_Archive_Writer $dest Where to copy $source files
* @param bool $autoClose if true (default), $dest will be closed after the extraction
* @param int $bufferSize Size of the buffer to use to move data from the reader to the buffer
* If $bufferSize <= 0 (default), the blockSize option is used
* You shouldn't need to change that
* @return null or a PEAR error if an error occured
*/
function extract(&$sourceToConvert, &$destToConvert, $autoClose = true, $bufferSize = 0)
{
$source =& File_Archive::_convertToReader($sourceToConvert);
if (PEAR::isError($source)) {
return $source;
}
$dest =& File_Archive::_convertToWriter($destToConvert);
return $source->extract($dest, $autoClose, $bufferSize);
}
/**
* Create a writer that can be used to append files to an archive inside a source
* If the archive can't be found in the source, it will be created
* If source is set to null, File_Archive::toFiles will be assumed
* If type is set to null, the type of the archive will be determined looking at
* the extension in the URL
* stat is the array of stat (returned by stat() PHP function of Reader getStat())
* to use if the archive must be created
*
* This function allows to create or append data to nested archives. Only one
* archive will be created and if your creation requires creating several nested
* archives, a PEAR error will be returned
*
* After this call, $source will be closed and should not be used until the
* returned writer is closed.
*
* @param File_Archive_Reader $source A reader where some files will be appended
* @param string $URL URL to reach the archive in the source.
* if $URL is null, a writer to append files to the $source reader will
* be returned
* @param bool $unique If true, the duplicate files will be deleted on close
* Default is false (and setting it to true may have some performance
* consequences)
* @param string $type Extension of the archive (or null to use the one in the URL)
* @param array $stat Used only if archive is created, array of stat as returned
* by PHP stat function or Reader getStat function: stats of the archive)
* Time (index 9) will be overwritten to current time
* @return File_Archive_Writer a writer that you can use to append files to the reader
*/
function appenderFromSource(&$toConvert, $URL = null, $unique = null,
$type = null, $stat = array())
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
if ($unique == null) {
$unique = File_Archive::getOption("appendRemoveDuplicates");
}
//Do not report the fact that the archive does not exist as an error
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
if ($URL === null) {
$result =& $source;
} else {
if ($type === null) {
$result = File_Archive::_readSource($source, $URL.'/', $reachable, $baseDir);
} else {
$result = File_Archive::readArchive(
$type,
File_Archive::_readSource($source, $URL, $reachable, $baseDir)
);
}
}
PEAR::popErrorHandling();
if (!PEAR::isError($result)) {
if ($unique) {
require_once "File/Archive/Writer/UniqueAppender.php";
return new File_Archive_Writer_UniqueAppender($result);
} else {
return $result->makeAppendWriter();
}
}
//The source can't be found and has to be created
$stat[9] = $stat['mtime'] = time();
if (empty($baseDir)) {
if ($source !== null) {
$writer =& $source->makeWriter();
} else {
$writer =& File_Archive::toFiles();
}
if (PEAR::isError($writer)) {
return $writer;
}
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$result = File_Archive::toArchive($reachable, $writer, $type);
PEAR::popErrorHandling();
if (PEAR::isError($result)) {
$result = File_Archive::toFiles($reachable);
}
} else {
$reachedSource = File_Archive::readSource($source, $reachable);
if (PEAR::isError($reachedSource)) {
return $reachedSource;
}
$writer = $reachedSource->makeWriter();
if (PEAR::isError($writer)) {
return $writer;
}
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$result = File_Archive::toArchive($baseDir, $writer, $type);
PEAR::popErrorHandling();
if (PEAR::isError($result)) {
require_once "File/Archive/Writer/AddBaseName.php";
$result = new File_Archive_Writer_AddBaseName(
$baseDir, $writer);
if (PEAR::isError($result)) {
return $result;
}
}
}
return $result;
}
/**
* Create a writer that allows appending new files to an existing archive
* This function actes as appendToSource with source being the system files
* $URL can't be null here
*
* @param File_Archive_Reader $source A reader where some files will be appended
* @return File_Archive_Writer a writer that you can use to append files to the reader
*/
function appender($URL, $unique = null, $type = null, $stat = array())
{
$source = null;
return File_Archive::appenderFromSource($source, $URL, $unique, $type, $stat);
}
/**
* Remove the files that follow a given predicate from the source
* If URL is null, the files will be removed from the source directly
* Else, URL must link to a source from which the files will be removed
*
* @param File_Archive_Predicate $pred The files that follow the predicate
* (for which $pred->isTrue($source) is true) will be erased
* @param File_Archive_Reader $source A reader that contains the files to remove
*/
function removeFromSource(&$pred, &$toConvert, $URL = null)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
if ($URL === null) {
$result = &$source;
} else {
if (substr($URL, -1) !== '/') {
$URL .= '/';
}
$result = File_Archive::readSource($source, $URL);
}
$writer = $result->makeWriterRemoveFiles($pred);
if (PEAR::isError($writer)) {
return $writer;
}
$writer->close();
}
/**
* Remove the files that follow a given predicate from the archive specified
* in $URL
*
* @param $URL URL of the archive where some files must be removed
*/
function remove($pred, $URL)
{
$source = null;
return File_Archive::removeFromSource($pred, $source, $URL);
}
/**
* Remove duplicates from a source, keeping the most recent one (or the one that has highest pos in
* the archive if the files have same date or no date specified)
*
* @param File_Archive_Reader a reader that may contain duplicates
*/
function removeDuplicatesFromSource(&$toConvert, $URL = null)
{
$source =& File_Archive::_convertToReader($toConvert);
if (PEAR::isError($source)) {
return $source;
}
if ($URL !== null && substr($URL, -1) != '/') {
$URL .= '/';
}
if ($source === null) {
$source = File_Archive::read($URL);
}
require_once "File/Archive/Predicate/Duplicate.php";
$pred = new File_Archive_Predicate_Duplicate($source);
$source->close();
return File_Archive::removeFromSource(
$pred,
$source,
null
);
}
/**
* Remove duplicates from the archive specified in the URL
*/
function removeDuplicates($URL)
{
$source = null;
return File_Archive::removeDuplicatesFromSource($source, $URL);
}
}
?>