1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4/** 5 * Factory to access the most common File_Archive features 6 * It uses lazy include, so you dont have to include the files from 7 * File/Archive/* directories 8 * 9 * PHP versions 4 and 5 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA 24 * 25 * @category File Formats 26 * @package File_Archive 27 * @author Vincent Lascaux <vincentlascaux@php.net> 28 * @copyright 1997-2005 The PHP Group 29 * @license http://www.gnu.org/copyleft/lesser.html LGPL 30 * @version CVS: $Id: Archive.php,v 1.85 2005/08/16 08:48:59 vincentlascaux Exp $ 31 * @link http://pear.php.net/package/File_Archive 32 */ 33 34/** 35 * To have access to PEAR::isError and PEAR::raiseError 36 * We should probably use lazy include and remove this inclusion... 37 */ 38require_once "PEAR.php"; 39 40function File_Archive_cleanCache($file, $group) 41{ 42 $file = split('_', $file); 43 if (count($file) != 3) { 44 return false; //not a File_Archive file, keep it 45 } 46 47 $name = $file[2]; 48 $name = urldecode($name); 49 50 $group = $file[1]; 51 52 //clean the cache only for files in File_Archive groups 53 return substr($group, 0, 11) == 'FileArchive' && 54 !file_exists($name); //and only if the related file no longer exists 55} 56 57/** 58 * Factory to access the most common File_Archive features 59 * It uses lazy include, so you dont have to include the files from 60 * File/Archive/* directories 61 */ 62class File_Archive 63{ 64 function& _option($name) 65 { 66 static $container = array( 67 'zipCompressionLevel' => 9, 68 'gzCompressionLevel' => 9, 69 'tmpDirectory' => '.', 70 'cache' => null, 71 'appendRemoveDuplicates' => false, 72 'blockSize' => 65536, 73 'cacheCondition' => false 74 ); 75 return $container[$name]; 76 } 77 /** 78 * Sets an option that will be used by default by all readers or writers 79 * Option names are case sensitive 80 * Currently, the following options are used: 81 * 82 * "cache" 83 * Instance of a Cache_Lite object used to cache some compressed 84 * data to speed up future compressions of files 85 * Default: null (no cache used) 86 * 87 * "zipCompressionLevel" 88 * Value between 0 and 9 specifying the default compression level used 89 * by Zip writers (0 no compression, 9 highest compression) 90 * Default: 9 91 * 92 * "gzCompressionLevel" 93 * Value between 0 and 9 specifying the default compression level used 94 * by Gz writers (0 no compression, 9 highest compression) 95 * Default: 9 96 * 97 * "tmpDirectory" 98 * Directory where the temporary files generated by File_Archive will 99 * be created 100 * Default: '.' 101 * 102 * "appendRemoveDuplicates" 103 * If set to true, the appender created will by default remove the 104 * file present in the archive when adding a new one. This will slow the 105 * appending of files to archives 106 * Default: false 107 * 108 * "blockSize" 109 * To transfer data from a reader to a writer, some chunks a read from the 110 * source and written to the writer. This parameter controls the size of the 111 * chunks 112 * Default: 64kB 113 * 114 * "cacheCondition" 115 * This parameter specifies when a cache should be used. When the cache is 116 * used, the data of the reader is saved in a temporary file for future access. 117 * The cached reader will be read only once, even if you read it several times. 118 * This can be usefull to read compressed files or downloaded files (from http or ftp) 119 * The possible values for this option are 120 * - false: never use cache 121 * - a regexp: A cache will be used if the specified URL matches the regexp 122 * preg_match is used 123 * Default: false 124 * Example: '/^(http|ftp):\/\//' will cache all files downloaded via http or ftp 125 * 126 */ 127 function setOption($name, $value) 128 { 129 $option =& File_Archive::_option($name); 130 $option = $value; 131 if ($name == 'cache' && $value !== null) { 132 //TODO: ask to Cache_Lite to allow that 133 $value->_fileNameProtection = false; 134 } 135 } 136 137 /** 138 * Retrieve the value of an option 139 */ 140 function getOption($name) 141 { 142 return File_Archive::_option($name); 143 } 144 145 /** 146 * Create a reader to read the URL $URL. 147 * If the URL is a directory, it will recursively read that directory. 148 * If $uncompressionLevel is not null, the archives (files with extension 149 * tar, zip, gz or tgz) will be considered as directories (up to a depth of 150 * $uncompressionLevel if $uncompressionLevel > 0). The reader will only 151 * read files with a directory depth of $directoryDepth. It reader will 152 * replace the given URL ($URL) with $symbolic in the public filenames 153 * The default symbolic name is the last filename in the URL (or '' for 154 * directories) 155 * 156 * Examples: 157 * Considere the following file system 158 * <pre> 159 * a.txt 160 * b.tar (archive that contains the following files) 161 * c.txt 162 * d.tgz (archive that contains the following files) 163 * e.txt 164 * dir1/ 165 * f.txt 166 * dir2/ 167 * g.txt 168 * dir3/ 169 * h.tar (archive that contains the following files) 170 * i.txt 171 * </pre> 172 * 173 * read('.') will return a reader that gives access to following 174 * files (recursively read current dir): 175 * <pre> 176 * a.txt 177 * b.tar 178 * dir2/g.txt 179 * dir2/dir3/h.tar 180 * </pre> 181 * 182 * read('.', 'myBaseDir') will return the following reader: 183 * <pre> 184 * myBaseDir/a.txt 185 * myBaseDir/b.tar 186 * myBaseDir/dir2/g.txt 187 * myBaseDir/dir2/dir3/h.tar 188 * </pre> 189 * 190 * read('.', '', -1) will return the following reader (uncompress 191 * everything) 192 * <pre> 193 * a.txt 194 * b.tar/c.txt 195 * b.tar/d.tgz/e.txt 196 * b.tar/d.tgz/dir1/f.txt 197 * dir2/g.txt 198 * dir2/dir3/h.tar/i.txt 199 * </pre> 200 * 201 * read('.', '', 1) will uncompress only one level (so d.tgz will 202 * not be uncompressed): 203 * <pre> 204 * a.txt 205 * b.tar/c.txt 206 * b.tar/d.tgz 207 * dir2/g.txt 208 * dir2/dir3/h.tar/i.txt 209 * </pre> 210 * 211 * read('.', '', 0, 0) will not recurse into subdirectories 212 * <pre> 213 * a.txt 214 * b.tar 215 * </pre> 216 * 217 * read('.', '', 0, 1) will recurse only one level in 218 * subdirectories 219 * <pre> 220 * a.txt 221 * b.tar 222 * dir2/g.txt 223 * </pre> 224 * 225 * read('.', '', -1, 2) will uncompress everything and recurse in 226 * only 2 levels in subdirectories or archives 227 * <pre> 228 * a.txt 229 * b.tar/c.txt 230 * b.tar/d.tgz/e.txt 231 * dir2/g.txt 232 * </pre> 233 * 234 * The recursion level is determined by the real path, not the symbolic one. 235 * So read('.', 'myBaseDir', -1, 2) will result to the same files: 236 * <pre> 237 * myBaseDir/a.txt 238 * myBaseDir/b.tar/c.txt 239 * myBaseDir/b.tar/d.tgz/e.txt (accepted because the real depth is 2) 240 * myBaseDir/dir2/g.txt 241 * </pre> 242 * 243 * Use readSource to do the same thing, reading from a specified reader instead of 244 * reading from the system files 245 * 246 * To read a single file, you can do read('a.txt', 'public_name.txt') 247 * If no public name is provided, the default one is the name of the file 248 * read('dir2/g.txt') contains the single file named 'g.txt' 249 * read('b.tar/c.txt') contains the single file named 'c.txt' 250 * 251 * Note: This function uncompress files reading their extension 252 * The compressed files must have a tar, zip, gz or tgz extension 253 * Since it is impossible for some URLs to use is_dir or is_file, this 254 * function may not work with 255 * URLs containing folders which name ends with such an extension 256 */ 257 function readSource(&$source, $URL, $symbolic = null, 258 $uncompression = 0, $directoryDepth = -1) 259 { 260 return File_Archive::_readSource($source, $URL, $reachable, $baseDir, 261 $symbolic, $uncompression, $directoryDepth); 262 } 263 264 /** 265 * This function performs exactly as readSource, but with two additional parameters 266 * ($reachable and $baseDir) that will be set so that $reachable."/".$baseDir == $URL 267 * and $reachable can be reached (in case of error) 268 * 269 * @access private 270 */ 271 function _readSource(&$toConvert, $URL, &$reachable, &$baseDir, $symbolic = null, 272 $uncompression = 0, $directoryDepth = -1) 273 { 274 $source =& File_Archive::_convertToReader($toConvert); 275 if (PEAR::isError($source)) { 276 return $source; 277 } 278 if (is_array($URL)) { 279 $converted = array(); 280 foreach($URL as $key => $foo) { 281 $converted[] =& File_Archive::_convertToReader($URL[$key]); 282 } 283 return File_Archive::readMulti($converted); 284 } 285 286 //No need to uncompress more than $directoryDepth 287 //That's not perfect, and some archives will still be uncompressed just 288 //to be filtered out :( 289 if ($directoryDepth >= 0) { 290 $uncompressionLevel = min($uncompression, $directoryDepth); 291 } else { 292 $uncompressionLevel = $uncompression; 293 } 294 295 require_once 'File/Archive/Reader.php'; 296 $std = File_Archive_Reader::getStandardURL($URL); 297 298 //Modify the symbolic name if necessary 299 $slashPos = strrpos($std, '/'); 300 if ($symbolic === null) { 301 if ($slashPos === false) { 302 $realSymbolic = $std; 303 } else { 304 $realSymbolic = substr($std, $slashPos+1); 305 } 306 } else { 307 $realSymbolic = $symbolic; 308 } 309 if ($slashPos !== false) { 310 $baseFile = substr($std, 0, $slashPos+1); 311 $lastFile = substr($std, $slashPos+1); 312 } else { 313 $baseFile = ''; 314 $lastFile = $std; 315 } 316 317 if (strpos($lastFile, '*')!==false || 318 strpos($lastFile, '?')!==false) { 319 //We have to build a regexp here 320 $regexp = str_replace( 321 array('\*', '\?'), 322 array('[^/]*', '[^/]'), 323 preg_quote($lastFile) 324 ); 325 $result = File_Archive::_readSource($source, $baseFile, 326 $reachable, $baseDir, null, 0, -1); 327 return File_Archive::filter( 328 File_Archive::predEreg('^'.$regexp.'$'), 329 $result 330 ); 331 } 332 333 //If the URL can be interpreted as a directory, and we are reading from the file system 334 if ((empty($URL) || is_dir($URL)) && $source === null) { 335 require_once "File/Archive/Reader/Directory.php"; 336 require_once "File/Archive/Reader/ChangeName.php"; 337 338 if ($uncompressionLevel != 0) { 339 require_once "File/Archive/Reader/Uncompress.php"; 340 $result = new File_Archive_Reader_Uncompress( 341 new File_Archive_Reader_Directory($std, '', $directoryDepth), 342 $uncompressionLevel 343 ); 344 } else { 345 $result = new File_Archive_Reader_Directory($std, '', $directoryDepth); 346 } 347 348 if ($directoryDepth >= 0) { 349 require_once 'File/Archive/Reader/Filter.php'; 350 require_once 'File/Archive/Predicate/MaxDepth.php'; 351 352 $tmp =& File_Archive::filter( 353 new File_Archive_Predicate_MaxDepth($directoryDepth), 354 $result 355 ); 356 unset($result); 357 $result =& $tmp; 358 } 359 if (!empty($realSymbolic)) { 360 if ($symbolic === null) { 361 $realSymbolic = ''; 362 } 363 $tmp =& new File_Archive_Reader_AddBaseName( 364 $realSymbolic, 365 $result 366 ); 367 unset($result); 368 $result =& $tmp; 369 } 370 371 //If the URL can be interpreted as a file, and we are reading from the file system 372 } else if (is_file($URL) && substr($URL, -1)!='/' && $source === null) { 373 require_once "File/Archive/Reader/File.php"; 374 $result = new File_Archive_Reader_File($URL, $realSymbolic); 375 376 //Else, we will have to build a complex reader 377 } else { 378 require_once "File/Archive/Reader/File.php"; 379 380 $realPath = $std; 381 382 // Try to find a file with a known extension in the path ( 383 // (to manage URLs like archive.tar/directory/file) 384 $pos = 0; 385 do { 386 if ($pos+1<strlen($realPath)) { 387 $pos = strpos($realPath, '/', $pos+1); 388 } else { 389 $pos = false; 390 } 391 if ($pos === false) { 392 $pos = strlen($realPath); 393 } 394 395 $file = substr($realPath, 0, $pos); 396 $baseDir = substr($realPath, $pos+1); 397 $dotPos = strrpos($file, '.'); 398 $extension = ''; 399 if ($dotPos !== false) { 400 $extension = substr($file, $dotPos+1); 401 } 402 } while ($pos < strlen($realPath) && 403 (!File_Archive::isKnownExtension($extension) || 404 (is_dir($file) && $source==null))); 405 406 $reachable = $file; 407 408 //If we are reading from the file system 409 if ($source === null) { 410 //Create a file reader 411 $result = new File_Archive_Reader_File($file); 412 } else { 413 //Select in the source the file $file 414 415 require_once "File/Archive/Reader/Select.php"; 416 $result = new File_Archive_Reader_Select($file, $source); 417 } 418 419 require_once "File/Archive/Reader/Uncompress.php"; 420 $tmp = new File_Archive_Reader_Uncompress($result, $uncompressionLevel); 421 unset($result); 422 $result = $tmp; 423 424 //Select the requested folder in the uncompress reader 425 $isDir = $result->setBaseDir($std); 426 if (PEAR::isError($isDir)) { 427 return $isDir; 428 } 429 if ($isDir && $symbolic==null) { 430 //Default symbolic name for directories is empty 431 $realSymbolic = ''; 432 } 433 434 if ($directoryDepth >= 0) { 435 //Limit the maximum depth if necessary 436 require_once "File/Archive/Predicate/MaxDepth.php"; 437 438 $tmp = new File_Archive_Reader_Filter( 439 new File_Archive_Predicate( 440 $directoryDepth + 441 substr_count(substr($std, $pos+1), '/') 442 ), 443 $result 444 ); 445 unset($result); 446 $result =& $tmp; 447 } 448 449 if ($std != $realSymbolic) { 450 require_once "File/Archive/Reader/ChangeName.php"; 451 452 //Change the base name to the symbolic one if necessary 453 $tmp = new File_Archive_Reader_ChangeBaseName( 454 $std, 455 $realSymbolic, 456 $result 457 ); 458 unset($result); 459 $result =& $tmp; 460 } 461 } 462 463 $cacheCondition = File_Archive::getOption('cacheCondition'); 464 if ($cacheCondition !== false && 465 preg_match($cacheCondition, $URL)) { 466 $tmp =& File_Archive::cache($result); 467 unset($result); 468 $result =& $tmp; 469 } 470 471 return $result; 472 } 473 function read($URL, $symbolic = null, 474 $uncompression = 0, $directoryDepth = -1) 475 { 476 $source = null; 477 return File_Archive::readSource($source, $URL, $symbolic, $uncompression, $directoryDepth); 478 } 479 480 /** 481 * Create a file reader on an uploaded file. The reader will read 482 * $_FILES[$name]['tmp_name'] and will have $_FILES[$name]['name'] 483 * as a symbolic filename. 484 * 485 * A PEAR error is returned if one of the following happen 486 * - $_FILES[$name] is not set 487 * - $_FILES[$name]['error'] is not 0 488 * - is_uploaded_file returns false 489 * 490 * @param string $name Index of the file in the $_FILES array 491 * @return File_Archive_Reader File reader on the uploaded file 492 */ 493 function readUploadedFile($name) 494 { 495 if (!isset($_FILES[$name])) { 496 return PEAR::raiseError("File $name has not been uploaded"); 497 } 498 switch ($_FILES[$name]['error']) { 499 case 0: 500 //No error 501 break; 502 case 1: 503 return PEAR::raiseError( 504 "The upload size limit didn't allow to upload file ". 505 $_FILES[$name]['name'] 506 ); 507 case 2: 508 return PEAR::raiseError( 509 "The form size limit didn't allow to upload file ". 510 $_FILES[$name]['name'] 511 ); 512 case 3: 513 return PEAR::raiseError( 514 "The file was not entirely uploaded" 515 ); 516 case 4: 517 return PEAR::raiseError( 518 "The uploaded file is empty" 519 ); 520 default: 521 return PEAR::raiseError( 522 "Unknown error ".$_FILES[$name]['error']." in file upload. ". 523 "Please, report a bug" 524 ); 525 } 526 if (!is_uploaded_file($_FILES[$name]['tmp_name'])) { 527 return PEAR::raiseError("The file is not an uploaded file"); 528 } 529 530 require_once "File/Archive/Reader/File.php"; 531 return new File_Archive_Reader_File( 532 $_FILES[$name]['tmp_name'], 533 $_FILES[$name]['name'], 534 $_FILES[$name]['type'] 535 ); 536 } 537 538 /** 539 * Adds a cache layer above the specified reader 540 * The data of the reader is saved in a temporary file for future access. 541 * The cached reader will be read only once, even if you read it several times. 542 * This can be usefull to read compressed files or downloaded files (from http or ftp) 543 * 544 * @param mixed $toConvert The reader to cache 545 * It can be a File_Archive_Reader or a string, which will be converted using the 546 * read function 547 */ 548 function cache(&$toConvert) 549 { 550 $source =& File_Archive::_convertToReader($toConvert); 551 if (PEAR::isError($source)) { 552 return $source; 553 } 554 555 require_once 'File/Archive/Reader/Cache.php'; 556 return new File_Archive_Reader_Cache($source); 557 } 558 559 /** 560 * Try to interpret the object as a reader 561 * Strings are converted to readers using File_Archive::read 562 * Arrays are converted to readers using File_Archive::readMulti 563 * 564 * @access private 565 */ 566 function &_convertToReader(&$source) 567 { 568 if (is_string($source)) { 569 $cacheCondition = File_Archive::getOption('cacheCondition'); 570 if ($cacheCondition !== false && 571 preg_match($cacheCondition, $source)) { 572 return File_Archive::cache(File_Archive::read($source)); 573 } else { 574 return File_Archive::read($source); 575 } 576 } else if (is_array($source)) { 577 return File_Archive::readMulti($source); 578 } else { 579 return $source; 580 } 581 } 582 583 /** 584 * Try to interpret the object as a writer 585 * Strings are converted to writers using File_Archive::appender 586 * Arrays are converted to writers using a multi writer 587 * 588 * @access private 589 */ 590 function &_convertToWriter(&$dest) 591 { 592 if (is_string($dest)) { 593 return File_Archive::appender($dest); 594 } else if (is_array($dest)) { 595 require_once 'File/Archive/Writer/Multi.php'; 596 $writer = new File_Archive_Writer_Multi(); 597 foreach($dest as $key => $foo) { 598 $writer->addWriter($dest[$key]); 599 } 600 } else { 601 return $dest; 602 } 603 } 604 605 /** 606 * Check if a file with a specific extension can be read as an archive 607 * with File_Archive::read* 608 * This function is case sensitive. 609 * 610 * @param string $extension the checked extension 611 * @return bool whether this file can be understood reading its extension 612 * Currently, supported extensions are tar, zip, gz, tgz, tbz, bz2, 613 * bzip2, ar, deb 614 */ 615 function isKnownExtension($extension) 616 { 617 return $extension == 'tar' || 618 $extension == 'zip' || 619 $extension == 'gz' || 620 $extension == 'tgz' || 621 $extension == 'tbz' || 622 $extension == 'bz2' || 623 $extension == 'bzip2' || 624 $extension == 'ar' || 625 $extension == 'deb' /* || 626 $extension == 'cab' || 627 $extension == 'rar' */; 628 } 629 630 /** 631 * Create a reader that will read the single file source $source as 632 * a specific archive 633 * 634 * @param string $extension determines the kind of archive $source contains 635 * $extension is case sensitive 636 * @param File_Archive_Reader $source stores the archive 637 * @param bool $sourceOpened specifies if the archive is already opened 638 * if false, next will be called on source 639 * Closing the returned archive will close $source iif $sourceOpened 640 * is true 641 * @return A File_Archive_Reader that uncompresses the archive contained in 642 * $source interpreting it as a $extension archive 643 * If $extension is not handled return false 644 */ 645 function readArchive($extension, &$toConvert, $sourceOpened = false) 646 { 647 $source =& File_Archive::_convertToReader($toConvert); 648 if (PEAR::isError($source)) { 649 return $source; 650 } 651 652 switch($extension) { 653 case 'tgz': 654 return File_Archive::readArchive('tar', 655 File_Archive::readArchive('gz', $source, $sourceOpened) 656 ); 657 case 'tbz': 658 return File_Archive::readArchive('tar', 659 File_Archive::readArchive('bz2', $source, $sourceOpened) 660 ); 661 case 'tar': 662 require_once 'File/Archive/Reader/Tar.php'; 663 return new File_Archive_Reader_Tar($source, $sourceOpened); 664 665 case 'gz': 666 case 'gzip': 667 require_once 'File/Archive/Reader/Gzip.php'; 668 return new File_Archive_Reader_Gzip($source, $sourceOpened); 669 670 case 'zip': 671 require_once 'File/Archive/Reader/Zip.php'; 672 return new File_Archive_Reader_Zip($source, $sourceOpened); 673 674 case 'bz2': 675 case 'bzip2': 676 require_once 'File/Archive/Reader/Bzip2.php'; 677 return new File_Archive_Reader_Bzip2($source, $sourceOpened); 678 679 case 'deb': 680 case 'ar': 681 require_once 'File/Archive/Reader/Ar.php'; 682 return new File_Archive_Reader_Ar($source, $sourceOpened); 683 684/* case 'cab': 685 require_once 'File/Archive/Reader/Cab.php'; 686 return new File_Archive_Reader_Cab($source, $sourceOpened); 687 688 689 case 'rar': 690 require_once "File/Archive/Reader/Rar.php"; 691 return new File_Archive_Reader_Rar($source, $sourceOpened); */ 692 693 default: 694 return false; 695 } 696 } 697 698 /** 699 * Contains only one file with data read from a memory buffer 700 * 701 * @param string $memory content of the file 702 * @param string $filename public name of the file 703 * @param array $stat statistics of the file. Index 7 (size) will be 704 * overwritten to match the size of $memory 705 * @param string $mime mime type of the file. Default will determine the 706 * mime type thanks to the extension of $filename 707 * @see File_Archive_Reader_Memory 708 */ 709 function readMemory($memory, $filename, $stat=array(), $mime=null) 710 { 711 require_once "File/Archive/Reader/Memory.php"; 712 return new File_Archive_Reader_Memory($memory, $filename, $stat, $mime); 713 } 714 715 /** 716 * Contains several other sources. Take care the sources don't have several 717 * files with the same filename. The sources are given as a parameter, or 718 * can be added thanks to the reader addSource method 719 * 720 * @param array $sources Array of strings or readers that will be added to 721 * the multi reader. If the parameter is a string, a reader will be 722 * built thanks to the read function 723 * @see File_Archive_Reader_Multi, File_Archive::read() 724 */ 725 function readMulti($sources = array()) 726 { 727 require_once "File/Archive/Reader/Multi.php"; 728 $result = new File_Archive_Reader_Multi(); 729 foreach ($sources as $index => $foo) { 730 $s =& File_Archive::_convertToReader($sources[$index]); 731 if (PEAR::isError($s)) { 732 return $s; 733 } else { 734 $result->addSource($s); 735 } 736 } 737 return $result; 738 } 739 /** 740 * Make the files of a source appear as one large file whose content is the 741 * concatenation of the content of all the files 742 * 743 * @param File_Archive_Reader $source The source whose files must be 744 * concatened 745 * @param string $filename name of the only file of the created reader 746 * @param array $stat statistics of the file. Index 7 (size) will be 747 * overwritten to match the total size of the files 748 * @param string $mime mime type of the file. Default will determine the 749 * mime type thanks to the extension of $filename 750 * @see File_Archive_Reader_Concat 751 */ 752 function readConcat(&$toConvert, $filename, $stat=array(), $mime=null) 753 { 754 $source =& File_Archive::_convertToReader($toConvert); 755 if (PEAR::isError($source)) { 756 return $source; 757 } 758 759 require_once "File/Archive/Reader/Concat.php"; 760 return new File_Archive_Reader_Concat($source, $filename, $stat, $mime); 761 } 762 763 /** 764 * Removes from a source the files that do not follow a given predicat 765 * 766 * @param File_Archive_Predicate $predicate Only the files for which 767 * $predicate->isTrue() will be kept 768 * @param File_Archive_Reader $source Source that will be filtered 769 * @see File_Archive_Reader_Filter 770 */ 771 function filter($predicate, &$toConvert) 772 { 773 $source =& File_Archive::_convertToReader($toConvert); 774 if (PEAR::isError($source)) { 775 return $source; 776 } 777 778 require_once "File/Archive/Reader/Filter.php"; 779 return new File_Archive_Reader_Filter($predicate, $source); 780 } 781 /** 782 * Predicate that always evaluate to true 783 * 784 * @see File_Archive_Predicate_True 785 */ 786 function predTrue() 787 { 788 require_once "File/Archive/Predicate/True.php"; 789 return new File_Archive_Predicate_True(); 790 } 791 /** 792 * Predicate that always evaluate to false 793 * 794 * @see File_Archive_Predicate_False 795 */ 796 function predFalse() 797 { 798 require_once "File/Archive/Predicate/False.php"; 799 return new File_Archive_Predicate_False(); 800 } 801 /** 802 * Predicate that evaluates to the logical AND of the parameters 803 * You can add other predicates thanks to the 804 * File_Archive_Predicate_And::addPredicate() function 805 * 806 * @param File_Archive_Predicate (any number of them) 807 * @see File_Archive_Predicate_And 808 */ 809 function predAnd() 810 { 811 require_once "File/Archive/Predicate/And.php"; 812 $pred = new File_Archive_Predicate_And(); 813 $args = func_get_args(); 814 foreach ($args as $p) { 815 $pred->addPredicate($p); 816 } 817 return $pred; 818 } 819 /** 820 * Predicate that evaluates to the logical OR of the parameters 821 * You can add other predicates thanks to the 822 * File_Archive_Predicate_Or::addPredicate() function 823 * 824 * @param File_Archive_Predicate (any number of them) 825 * @see File_Archive_Predicate_Or 826 */ 827 function predOr() 828 { 829 require_once "File/Archive/Predicate/Or.php"; 830 $pred = new File_Archive_Predicate_Or(); 831 $args = func_get_args(); 832 foreach ($args as $p) { 833 $pred->addPredicate($p); 834 } 835 return $pred; 836 } 837 /** 838 * Negate a predicate 839 * 840 * @param File_Archive_Predicate $pred Predicate to negate 841 * @see File_Archive_Predicate_Not 842 */ 843 function predNot($pred) 844 { 845 require_once "File/Archive/Predicate/Not.php"; 846 return new File_Archive_Predicate_Not($pred); 847 } 848 /** 849 * Evaluates to true iif the file is larger than a given size 850 * 851 * @param int $size the minimal size of the files (in Bytes) 852 * @see File_Archive_Predicate_MinSize 853 */ 854 function predMinSize($size) 855 { 856 require_once "File/Archive/Predicate/MinSize.php"; 857 return new File_Archive_Predicate_MinSize($size); 858 } 859 /** 860 * Evaluates to true iif the file has been modified after a given time 861 * 862 * @param int $time Unix timestamp of the minimal modification time of the 863 * files 864 * @see File_Archive_Predicate_MinTime 865 */ 866 function predMinTime($time) 867 { 868 require_once "File/Archive/Predicate/MinTime.php"; 869 return new File_Archive_Predicate_MinTime($time); 870 } 871 /** 872 * Evaluates to true iif the file has less that a given number of 873 * directories in its path 874 * 875 * @param int $depth Maximal number of directories in path of the files 876 * @see File_Archive_Predicate_MaxDepth 877 */ 878 function predMaxDepth($depth) 879 { 880 require_once "File/Archive/Predicate/MaxDepth.php"; 881 return new File_Archive_Predicate_MaxDepth($depth); 882 } 883 /** 884 * Evaluates to true iif the extension of the file is in a given list 885 * 886 * @param array or string $list List or comma separated string of possible 887 * extension of the files 888 * @see File_Archive_Predicate_Extension 889 */ 890 function predExtension($list) 891 { 892 require_once "File/Archive/Predicate/Extension.php"; 893 return new File_Archive_Predicate_Extension($list); 894 } 895 /** 896 * Evaluates to true iif the MIME type of the file is in a given list 897 * 898 * @param array or string $list List or comma separated string of possible 899 * MIME types of the files. You may enter wildcards like "image/*" to 900 * select all the MIME in class image 901 * @see File_Archive_Predicate_MIME, MIME_Type::isWildcard() 902 */ 903 function predMIME($list) 904 { 905 require_once "File/Archive/Predicate/MIME.php"; 906 return new File_Archive_Predicate_MIME($list); 907 } 908 /** 909 * Evaluates to true iif the name of the file follow a given regular 910 * expression 911 * 912 * @param string $ereg regular expression that the filename must follow 913 * @see File_Archive_Predicate_Ereg, ereg() 914 */ 915 function predEreg($ereg) 916 { 917 require_once "File/Archive/Predicate/Ereg.php"; 918 return new File_Archive_Predicate_Ereg($ereg); 919 } 920 /** 921 * Evaluates to true iif the name of the file follow a given regular 922 * expression (case insensitive version) 923 * 924 * @param string $ereg regular expression that the filename must follow 925 * @see File_Archive_Predicate_Eregi, eregi 926 */ 927 function predEregi($ereg) 928 { 929 require_once "File/Archive/Predicate/Eregi.php"; 930 return new File_Archive_Predicate_Eregi($ereg); 931 } 932 /** 933 * Evaluates to true only after a given number of evaluations 934 * This can be used to select files by index since the evaluation is done 935 * once per file 936 * 937 * @param array The indexes for which the returned predicate will return true 938 * are the keys of the array 939 * The predicate will return true if isset($indexes[$pos]) 940 */ 941 function predIndex($indexes) 942 { 943 require_once "File/Archive/Predicate/Index.php"; 944 return new File_Archive_Predicate_Index($indexes); 945 } 946 /** 947 * Custom predicate built by supplying a string expression 948 * 949 * Here are different ways to create a predicate that keeps only files 950 * with names shorter than 100 chars 951 * <sample> 952 * File_Archive::predCustom("return strlen($name)<100;") 953 * File_Archive::predCustom("strlen($name)<100;") 954 * File_Archive::predCustom("strlen($name)<100") 955 * File_Archive::predCustom("strlen($source->getFilename())<100") 956 * </sample> 957 * 958 * @param string $expression String containing an expression that evaluates 959 * to a boolean. If the expression doesn't contain a return 960 * statement, it will be added at the begining of the expression 961 * A ';' will be added at the end of the expression so that you don't 962 * have to write it. You may use the $name variable to refer to the 963 * current filename (with path...), $time for the modification time 964 * (unix timestamp), $size for the size of the file in bytes, $mime 965 * for the MIME type of the file 966 * @see File_Archive_Predicate_Custom 967 */ 968 function predCustom($expression) 969 { 970 require_once "File/Archive/Predicate/Custom.php"; 971 return new File_Archive_Predicate_Custom($expression); 972 } 973 974 /** 975 * Send the files as a mail attachment 976 * 977 * @param Mail $mail Object used to send mail (see Mail::factory) 978 * @param array or String $to An array or a string with comma separated 979 * recipients 980 * @param array $headers The headers that will be passed to the Mail_mime 981 * object 982 * @param string $message Text body of the mail 983 * @see File_Archive_Writer_Mail 984 */ 985 function toMail($to, $headers, $message, $mail = null) 986 { 987 require_once "File/Archive/Writer/Mail.php"; 988 return new File_Archive_Writer_Mail($to, $headers, $message, $mail); 989 } 990 /** 991 * Write the files on the hard drive 992 * 993 * @param string $baseDir if specified, the files will be created in that 994 * directory. If they don't exist, the directories will automatically 995 * be created 996 * @see File_Archive_Writer_Files 997 */ 998 function toFiles($baseDir = "") 999 { 1000 require_once "File/Archive/Writer/Files.php"; 1001 return new File_Archive_Writer_Files($baseDir); 1002 } 1003 /** 1004 * Send the content of the files to a memory buffer 1005 * 1006 * toMemory returns a writer where the data will be written. 1007 * In this case, the data is accessible using the getData member 1008 * 1009 * toVariable returns a writer that will write into the given 1010 * variable 1011 * 1012 * @param out $data if specified, the data will be written to this buffer 1013 * Else, you can retrieve the buffer with the 1014 * File_Archive_Writer_Memory::getData() function 1015 * @see File_Archive_Writer_Memory 1016 */ 1017 function toMemory() 1018 { 1019 $v = ''; 1020 return File_Archive::toVariable($v); 1021 } 1022 function toVariable(&$v) 1023 { 1024 require_once "File/Archive/Writer/Memory.php"; 1025 return new File_Archive_Writer_Memory($v); 1026 } 1027 /** 1028 * Duplicate the writing operation on two writers 1029 * 1030 * @param File_Archive_Writer $a, $b writers where data will be duplicated 1031 * @see File_Archive_Writer_Multi 1032 */ 1033 function toMulti(&$aC, &$bC) 1034 { 1035 $a =& File_Archive::_convertToWriter($aC); 1036 $b =& File_Archive::_convertToWriter($bC); 1037 1038 if (PEAR::isError($a)) { 1039 return $a; 1040 } 1041 if (PEAR::isError($b)) { 1042 return $b; 1043 } 1044 1045 require_once "File/Archive/Writer/Multi.php"; 1046 $writer = new File_Archive_Writer_Multi(); 1047 $writer->addWriter($a); 1048 $writer->addWriter($b); 1049 return $writer; 1050 } 1051 /** 1052 * Send the content of the files to the standard output (so to the client 1053 * for a website) 1054 * 1055 * @param bool $sendHeaders If true some headers will be sent to force the 1056 * download of the file. Default value is true 1057 * @see File_Archive_Writer_Output 1058 */ 1059 function toOutput($sendHeaders = true) 1060 { 1061 require_once "File/Archive/Writer/Output.php"; 1062 return new File_Archive_Writer_Output($sendHeaders); 1063 } 1064 /** 1065 * Compress the data to a tar, gz, tar/gz or zip format 1066 * 1067 * @param string $filename name of the archive file 1068 * @param File_Archive_Writer $innerWriter writer where the archive will be 1069 * written 1070 * @param string $type can be one of tgz, tbz, tar, zip, gz, gzip, bz2, 1071 * bzip2 (default is the extension of $filename) or any composition 1072 * of them (for example tar.gz or tar.bz2). The case of this 1073 * parameter is not important. 1074 * @param array $stat Statistics of the archive (see stat function) 1075 * @param bool $autoClose If set to true, $innerWriter will be closed when 1076 * the returned archive is close. Default value is true. 1077 */ 1078 function toArchive($filename, &$toConvert, $type = null, 1079 $stat = array(), $autoClose = true) 1080 { 1081 $innerWriter =& File_Archive::_convertToWriter($toConvert); 1082 if (PEAR::isError($innerWriter)) { 1083 return $innerWriter; 1084 } 1085 $shortcuts = array("tgz" , "tbz" ); 1086 $reals = array("tar.gz", "tar.bz2"); 1087 1088 if ($type === null) { 1089 $extensions = strtolower($filename); 1090 } else { 1091 $extensions = strtolower($type); 1092 } 1093 $extensions = explode('.', str_replace($shortcuts, $reals, $extensions)); 1094 if ($innerWriter !== null) { 1095 $writer =& $innerWriter; 1096 } else { 1097 $writer = File_Archive::toFiles(); 1098 } 1099 $nbCompressions = 0; 1100 $currentFilename = $filename; 1101 while (($extension = array_pop($extensions)) !== null) { 1102 unset($next); 1103 switch($extension) { 1104 case "tar": 1105 require_once "File/Archive/Writer/Tar.php"; 1106 $next = new File_Archive_Writer_Tar( 1107 $currentFilename, $writer, $stat, $autoClose 1108 ); 1109 unset($writer); $writer =& $next; 1110 break; 1111 case "zip": 1112 require_once "File/Archive/Writer/Zip.php"; 1113 $next = new File_Archive_Writer_Zip( 1114 $currentFilename, $writer, $stat, $autoClose 1115 ); 1116 unset($writer); $writer =& $next; 1117 break; 1118 case "gz": 1119 case "gzip": 1120 require_once "File/Archive/Writer/Gzip.php"; 1121 $next = new File_Archive_Writer_Gzip( 1122 $currentFilename, $writer, $stat, $autoClose 1123 ); 1124 unset($writer); $writer =& $next; 1125 break; 1126 case "bz2": 1127 case "bzip2": 1128 require_once "File/Archive/Writer/Bzip2.php"; 1129 $next = new File_Archive_Writer_Bzip2( 1130 $currentFilename, $writer, $stat, $autoClose 1131 ); 1132 unset($writer); $writer =& $next; 1133 break; 1134 case "deb": 1135 case "ar": 1136 require_once "File/Archive/Writer/Ar.php"; 1137 $next = new File_Archive_Writer_Ar( 1138 $currentFilename, $writer, $stat, $autoClose 1139 ); 1140 unset($writer); $writer =& $next; 1141 break; 1142 default: 1143 if ($type !== null || $nbCompressions == 0) { 1144 return PEAR::raiseError("Archive $extension unknown"); 1145 } 1146 break; 1147 } 1148 $nbCompressions ++; 1149 $autoClose = true; 1150 $currentFilename = implode(".", $extensions); 1151 } 1152 return $writer; 1153 } 1154 1155 1156 /** 1157 * File_Archive::extract($source, $dest) is equivalent to $source->extract($dest) 1158 * If $source is a PEAR error, the error will be returned 1159 * It is thus easier to use this function than $source->extract, since it reduces the number of 1160 * error checking and doesn't force you to define a variable $source 1161 * 1162 * You may use strings as source and dest. In that case the source is automatically 1163 * converted to a reader using File_Archive::read and the dest is converted to a 1164 * writer using File_Archive::appender 1165 * Since PHP doesn't allow to pass literal strings by ref, you will have to use temporary 1166 * variables. 1167 * File_Archive::extract($src = 'archive.zip/', $dest = 'dir') will extract the archive to 'dir' 1168 * It is the same as 1169 * File_Archive::extract( 1170 * File_Archive::read('archive.zip/'), 1171 * File_Archive::appender('dir') 1172 * ); 1173 * You may use any variable in the extract function ($from/$to, $a/$b...). 1174 * 1175 * @param File_Archive_Reader $source The source that will be read 1176 * @param File_Archive_Writer $dest Where to copy $source files 1177 * @param bool $autoClose if true (default), $dest will be closed after the extraction 1178 * @param int $bufferSize Size of the buffer to use to move data from the reader to the buffer 1179 * If $bufferSize <= 0 (default), the blockSize option is used 1180 * You shouldn't need to change that 1181 * @return null or a PEAR error if an error occured 1182 */ 1183 function extract(&$sourceToConvert, &$destToConvert, $autoClose = true, $bufferSize = 0) 1184 { 1185 $source =& File_Archive::_convertToReader($sourceToConvert); 1186 if (PEAR::isError($source)) { 1187 return $source; 1188 } 1189 $dest =& File_Archive::_convertToWriter($destToConvert); 1190 return $source->extract($dest, $autoClose, $bufferSize); 1191 } 1192 1193 /** 1194 * Create a writer that can be used to append files to an archive inside a source 1195 * If the archive can't be found in the source, it will be created 1196 * If source is set to null, File_Archive::toFiles will be assumed 1197 * If type is set to null, the type of the archive will be determined looking at 1198 * the extension in the URL 1199 * stat is the array of stat (returned by stat() PHP function of Reader getStat()) 1200 * to use if the archive must be created 1201 * 1202 * This function allows to create or append data to nested archives. Only one 1203 * archive will be created and if your creation requires creating several nested 1204 * archives, a PEAR error will be returned 1205 * 1206 * After this call, $source will be closed and should not be used until the 1207 * returned writer is closed. 1208 * 1209 * @param File_Archive_Reader $source A reader where some files will be appended 1210 * @param string $URL URL to reach the archive in the source. 1211 * if $URL is null, a writer to append files to the $source reader will 1212 * be returned 1213 * @param bool $unique If true, the duplicate files will be deleted on close 1214 * Default is false (and setting it to true may have some performance 1215 * consequences) 1216 * @param string $type Extension of the archive (or null to use the one in the URL) 1217 * @param array $stat Used only if archive is created, array of stat as returned 1218 * by PHP stat function or Reader getStat function: stats of the archive) 1219 * Time (index 9) will be overwritten to current time 1220 * @return File_Archive_Writer a writer that you can use to append files to the reader 1221 */ 1222 function appenderFromSource(&$toConvert, $URL = null, $unique = null, 1223 $type = null, $stat = array()) 1224 { 1225 $source =& File_Archive::_convertToReader($toConvert); 1226 if (PEAR::isError($source)) { 1227 return $source; 1228 } 1229 if ($unique == null) { 1230 $unique = File_Archive::getOption("appendRemoveDuplicates"); 1231 } 1232 1233 //Do not report the fact that the archive does not exist as an error 1234 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1235 1236 if ($URL === null) { 1237 $result =& $source; 1238 } else { 1239 if ($type === null) { 1240 $result = File_Archive::_readSource($source, $URL.'/', $reachable, $baseDir); 1241 } else { 1242 $result = File_Archive::readArchive( 1243 $type, 1244 File_Archive::_readSource($source, $URL, $reachable, $baseDir) 1245 ); 1246 } 1247 } 1248 1249 PEAR::popErrorHandling(); 1250 1251 if (!PEAR::isError($result)) { 1252 if ($unique) { 1253 require_once "File/Archive/Writer/UniqueAppender.php"; 1254 return new File_Archive_Writer_UniqueAppender($result); 1255 } else { 1256 return $result->makeAppendWriter(); 1257 } 1258 } 1259 1260 //The source can't be found and has to be created 1261 $stat[9] = $stat['mtime'] = time(); 1262 1263 if (empty($baseDir)) { 1264 if ($source !== null) { 1265 $writer =& $source->makeWriter(); 1266 } else { 1267 $writer =& File_Archive::toFiles(); 1268 } 1269 if (PEAR::isError($writer)) { 1270 return $writer; 1271 } 1272 1273 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1274 $result = File_Archive::toArchive($reachable, $writer, $type); 1275 PEAR::popErrorHandling(); 1276 1277 if (PEAR::isError($result)) { 1278 $result = File_Archive::toFiles($reachable); 1279 } 1280 } else { 1281 $reachedSource = File_Archive::readSource($source, $reachable); 1282 if (PEAR::isError($reachedSource)) { 1283 return $reachedSource; 1284 } 1285 $writer = $reachedSource->makeWriter(); 1286 if (PEAR::isError($writer)) { 1287 return $writer; 1288 } 1289 1290 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1291 $result = File_Archive::toArchive($baseDir, $writer, $type); 1292 PEAR::popErrorHandling(); 1293 1294 if (PEAR::isError($result)) { 1295 require_once "File/Archive/Writer/AddBaseName.php"; 1296 $result = new File_Archive_Writer_AddBaseName( 1297 $baseDir, $writer); 1298 if (PEAR::isError($result)) { 1299 return $result; 1300 } 1301 } 1302 } 1303 return $result; 1304 } 1305 1306 /** 1307 * Create a writer that allows appending new files to an existing archive 1308 * This function actes as appendToSource with source being the system files 1309 * $URL can't be null here 1310 * 1311 * @param File_Archive_Reader $source A reader where some files will be appended 1312 * @return File_Archive_Writer a writer that you can use to append files to the reader 1313 */ 1314 function appender($URL, $unique = null, $type = null, $stat = array()) 1315 { 1316 $source = null; 1317 return File_Archive::appenderFromSource($source, $URL, $unique, $type, $stat); 1318 } 1319 1320 /** 1321 * Remove the files that follow a given predicate from the source 1322 * If URL is null, the files will be removed from the source directly 1323 * Else, URL must link to a source from which the files will be removed 1324 * 1325 * @param File_Archive_Predicate $pred The files that follow the predicate 1326 * (for which $pred->isTrue($source) is true) will be erased 1327 * @param File_Archive_Reader $source A reader that contains the files to remove 1328 */ 1329 function removeFromSource(&$pred, &$toConvert, $URL = null) 1330 { 1331 $source =& File_Archive::_convertToReader($toConvert); 1332 if (PEAR::isError($source)) { 1333 return $source; 1334 } 1335 if ($URL === null) { 1336 $result = &$source; 1337 } else { 1338 if (substr($URL, -1) !== '/') { 1339 $URL .= '/'; 1340 } 1341 $result = File_Archive::readSource($source, $URL); 1342 } 1343 1344 $writer = $result->makeWriterRemoveFiles($pred); 1345 if (PEAR::isError($writer)) { 1346 return $writer; 1347 } 1348 $writer->close(); 1349 } 1350 1351 /** 1352 * Remove the files that follow a given predicate from the archive specified 1353 * in $URL 1354 * 1355 * @param $URL URL of the archive where some files must be removed 1356 */ 1357 function remove($pred, $URL) 1358 { 1359 $source = null; 1360 return File_Archive::removeFromSource($pred, $source, $URL); 1361 } 1362 1363 /** 1364 * Remove duplicates from a source, keeping the most recent one (or the one that has highest pos in 1365 * the archive if the files have same date or no date specified) 1366 * 1367 * @param File_Archive_Reader a reader that may contain duplicates 1368 */ 1369 function removeDuplicatesFromSource(&$toConvert, $URL = null) 1370 { 1371 $source =& File_Archive::_convertToReader($toConvert); 1372 if (PEAR::isError($source)) { 1373 return $source; 1374 } 1375 if ($URL !== null && substr($URL, -1) != '/') { 1376 $URL .= '/'; 1377 } 1378 1379 if ($source === null) { 1380 $source = File_Archive::read($URL); 1381 } 1382 1383 require_once "File/Archive/Predicate/Duplicate.php"; 1384 $pred = new File_Archive_Predicate_Duplicate($source); 1385 $source->close(); 1386 return File_Archive::removeFromSource( 1387 $pred, 1388 $source, 1389 null 1390 ); 1391 } 1392 1393 /** 1394 * Remove duplicates from the archive specified in the URL 1395 */ 1396 function removeDuplicates($URL) 1397 { 1398 $source = null; 1399 return File_Archive::removeDuplicatesFromSource($source, $URL); 1400 } 1401} 1402 1403?>