PageRenderTime 37ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ezc/Archive/src/archive.php

https://bitbucket.org/crevillo/enetcall
PHP | 951 lines | 561 code | 76 blank | 314 comment | 67 complexity | a0741d2027e99b0e003e9818b6e81cd3 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * File containing the abstract ezcArchive class.
  4. *
  5. * @package Archive
  6. * @version //autogentag//
  7. * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
  8. * @license http://ez.no/licenses/new_bsd New BSD License
  9. */
  10. /**
  11. * The ezcArchive class provides the common interface for reading and writing
  12. * the archive formats Tar and Zip.
  13. *
  14. * ezcArchive provides the main API for reading and writing to an archive. The
  15. * archive itself can be compressed with GZip or BZip2 and will be handled
  16. * transparently.
  17. *
  18. * The {@link open()} method creates a new archive instance. For
  19. * existing archives, ezcArchive determines the correct archive format by the
  20. * mime-type and returns an instance of a subclass handling this format.
  21. * New archives should force a format type via a parameter in the open()
  22. * method.
  23. *
  24. * The instance of an ezcArchive class is also an iterator, which
  25. * points to the first file in the archive by default. Moving this pointer can
  26. * be done via the iterator methods: {@link rewind()}, {@link next()},
  27. * {@link valid()}, {@link key()}, and {@link current()}. This iterator is
  28. * defined as an object iterator and allows, for example, the {@link
  29. * http://www.php.net/foreach foreach} statement to iterate through the files.
  30. *
  31. * Extra methods that operate on the current iterator are: {@link
  32. * extractCurrent()} and {@link appendToCurrent()}. Which can be used,
  33. * respectively, to extract the files and to append a new file to the archive.
  34. * To append a directory to an archive you need to add a slash '/' at the end
  35. * of the directory name.
  36. *
  37. * The following example will open an existing tar.gz file and will append each
  38. * file to a zip archive:
  39. * <code>
  40. * $tar = ezcArchive::open( "/tmp/archive.tar.gz" );
  41. * $newZip = ezcArchive::open( "/tmp/new_archive.zip", ezcArchive::ZIP );
  42. *
  43. * foreach ( $tar as $entry )
  44. * {
  45. * // $entry contains the information of the current entry in the archive.
  46. * $tar->extractCurrent( "/tmp/" );
  47. * $newZip->appendToCurrent( $entry->getPath(), "/tmp/" );
  48. * $newZip->next();
  49. * }
  50. * </code>
  51. *
  52. * In order to extract an entire archive at once, use the {@link extract()}
  53. * method.
  54. *
  55. * @package Archive
  56. * @version //autogentag//
  57. * @mainclass
  58. */
  59. abstract class ezcArchive implements Iterator
  60. {
  61. /**
  62. * Normal tar archive.
  63. */
  64. const TAR = 0;
  65. /**
  66. * Tar version 7 archive.
  67. */
  68. const TAR_V7 = 1;
  69. /**
  70. * USTAR tar archive.
  71. */
  72. const TAR_USTAR = 2;
  73. /**
  74. * PAX tar archive.
  75. */
  76. const TAR_PAX = 3;
  77. /**
  78. * GNU tar archive.
  79. */
  80. const TAR_GNU = 4;
  81. /**
  82. * ZIP archive.
  83. */
  84. const ZIP = 10;
  85. /**
  86. * Gnu ZIP compression format.
  87. */
  88. const GZIP = 20;
  89. /**
  90. * BZIP2 compression format.
  91. */
  92. const BZIP2 = 30;
  93. /**
  94. * The entry or file number to which the iterator points.
  95. *
  96. * The first $fileNumber starts with 0.
  97. *
  98. * @var int
  99. */
  100. protected $fileNumber = 0;
  101. /**
  102. * The number of entries currently read from the archive.
  103. *
  104. * @var int
  105. */
  106. protected $entriesRead = 0;
  107. /**
  108. * Is true when the archive is read until the end, otherwise false.
  109. *
  110. * @var bool
  111. */
  112. protected $completed = false;
  113. /**
  114. * Stores the entries read from the archive.
  115. *
  116. * The array is not complete when the {@link $completed} variable is set to
  117. * false. The array may be over-complete, so the {@link $entriesRead}
  118. * should be checked if the {@link $completed} variable is set to true.
  119. *
  120. * @var array(ezcArchiveEntry)
  121. */
  122. protected $entries;
  123. /**
  124. * Direct access to the archive file.
  125. *
  126. * @var ezcArchiveFile
  127. */
  128. protected $file = null;
  129. /**
  130. * Holds the options if passed to the open method.
  131. *
  132. * @var ezcArchiveOptions
  133. */
  134. protected $options;
  135. /**
  136. * Use the {@link open()} method to get an instance of this class.
  137. */
  138. private function __construct()
  139. {
  140. }
  141. /**
  142. * Returns a new ezcArchive instance.
  143. *
  144. * This method returns a new instance according to the mime-type or
  145. * the given $forceType.
  146. *
  147. * - If $forceType is set to null, this method will try to determine the
  148. * archive format via the file data. Therefore the $forceType can only be
  149. * null when the archive contains data.
  150. * - If $forceType is set, it will use the specified algorithm. Even when
  151. * the given archive is from another type than specified.
  152. *
  153. * @throws ezcArchiveUnknownTypeException if the type of the archive cannot be determined.
  154. *
  155. * @param string $archiveName Absolute or relative path to the archive.
  156. * @param int $forceType Open the archive with the $forceType
  157. * algorithm. Possible values are: {@link ezcArchive::ZIP},
  158. * {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7}, {@link ezcArchive::TAR_USTAR},
  159. * {@link ezcArchive::TAR_PAX}, {@link ezcArchive::TAR_GNU}.
  160. * TAR will use the TAR_USTAR algorithm by default.
  161. * @param ezcArchiveOptions $options
  162. *
  163. * @return ezcArchive
  164. */
  165. public static function open( $archiveName, $forceType = null, ezcArchiveOptions $options = null )
  166. {
  167. $options = self::initOptions( $options );
  168. if ( !ezcArchiveFile::fileExists( $archiveName ) && $forceType === null )
  169. {
  170. throw new ezcArchiveUnknownTypeException( $archiveName );
  171. }
  172. if ( $forceType !== null )
  173. {
  174. return self::createInstance( $archiveName, $forceType, $options );
  175. }
  176. $h = ezcArchiveFileType::detect( $archiveName );
  177. while ( $h == ezcArchive::GZIP || $h == ezcArchive::BZIP2 )
  178. {
  179. if ( $h == ezcArchive::GZIP )
  180. {
  181. $archiveName = "compress.zlib://$archiveName";
  182. $h = ezcArchiveFileType::detect( $archiveName );
  183. }
  184. if ( $h == ezcArchive::BZIP2 )
  185. {
  186. $archiveName = "compress.bzip2://$archiveName";
  187. $h = ezcArchiveFileType::detect( $archiveName );
  188. }
  189. }
  190. return self::createInstance( $archiveName, $h, $options );
  191. }
  192. /**
  193. * Close the current archive.
  194. */
  195. public function close()
  196. {
  197. }
  198. /**
  199. * Sets the property $name to $value.
  200. *
  201. * Because there are no properties available, this method will always
  202. * throw an {@link ezcBasePropertyNotFoundException}.
  203. *
  204. * @throws ezcBasePropertyNotFoundException if the property does not exist.
  205. * @param string $name
  206. * @param mixed $value
  207. * @ignore
  208. */
  209. public function __set( $name, $value )
  210. {
  211. throw new ezcBasePropertyNotFoundException( $name );
  212. }
  213. /**
  214. * Returns the property $name.
  215. *
  216. * Because there are no properties available, this method will always
  217. * throw an {@link ezcBasePropertyNotFoundException}.
  218. *
  219. * @throws ezcBasePropertyNotFoundException if the property does not exist.
  220. * @param string $name
  221. * @ignore
  222. */
  223. public function __get( $name )
  224. {
  225. throw new ezcBasePropertyNotFoundException( $name );
  226. }
  227. /**
  228. * Returns the algorithm that is used currently.
  229. *
  230. * @return int Possible values are: {@link ezcArchive::ZIP}, {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7},
  231. * {@link ezcArchive::TAR_USTAR}, {@link ezcArchive::TAR_PAX}, or {@link ezcArchive::TAR_GNU}.
  232. */
  233. public abstract function getAlgorithm();
  234. /**
  235. * Returns true if writing to the archive is implemented, otherwise false.
  236. *
  237. * @see isWritable()
  238. *
  239. * @return bool
  240. */
  241. public abstract function algorithmCanWrite();
  242. /**
  243. * Returns an instance of the archive with the given type.
  244. *
  245. * Similar to {@link open()}, but the type is required.
  246. *
  247. * @param string $archiveName The path of the archive.
  248. * @param int $type Open the archive with the $forceType
  249. * algorithm. Possible values are: {@link ezcArchive::ZIP},
  250. * {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7}, {@link ezcArchive::TAR_USTAR},
  251. * {@link ezcArchive::TAR_PAX}, {@link ezcArchive::TAR_GNU}.
  252. * TAR will use the TAR_USTAR algorithm by default.
  253. *
  254. * @return ezcArchive Subclass of ezcArchive: {@link ezcArchiveZip},
  255. * {@link ezcArchiveV7Tar}, {@link ezcArchivePax},
  256. * {@link ezcArchiveGnuTar}, or {@link ezcArchiveUstar}.
  257. */
  258. protected static function createInstance( $archiveName, $type, ezcArchiveOptions $options = null )
  259. {
  260. $options = self::initOptions( $options );
  261. if ( $type == self::ZIP )
  262. {
  263. $af = new ezcArchiveCharacterFile( $archiveName, true, $options->readOnly );
  264. return self::getZipInstance( $af );
  265. }
  266. $af = new ezcArchiveBlockFile( $archiveName, true, 512, $options->readOnly );
  267. $instance = self::getTarInstance( $af, $type );
  268. $instance->options = $options;
  269. return $instance;
  270. }
  271. /**
  272. * This methods initializes the options by generating an options object if $options is null.
  273. *
  274. * @param null|ezcArchiveOptions $options
  275. *
  276. * @return ezcArchiveOptions
  277. */
  278. private static function initOptions( $options )
  279. {
  280. if ( $options === null )
  281. {
  282. $options = new ezcArchiveOptions;
  283. }
  284. return $options;
  285. }
  286. /**
  287. * This method associates a new $options object with this archive.
  288. *
  289. * @param ezcArchiveOptions $options
  290. */
  291. public function setOptions( ezcArchiveOptions $options )
  292. {
  293. $this->options = $options;
  294. }
  295. /**
  296. * Open a tar instance.
  297. *
  298. * This method is made public for testing purposes, and should not be used.
  299. *
  300. * @param ezcArchiveBlockFile $blockFile
  301. * @param int $type
  302. * The algorithm type. Possible values are:
  303. * {@link ezcArchive::TAR}, {@link ezcArchive::TAR_V7}, {@link ezcArchive::TAR_USTAR},
  304. * {@link ezcArchive::TAR_PAX}, {@link ezcArchive::TAR_GNU}.
  305. * TAR will use the TAR_USTAR algorithm by default.
  306. *
  307. * @return ezcArchive Subclass of ezcArchive:
  308. * {@link ezcArchiveV7Tar}, {@link ezcArchivePax},
  309. * {@link ezcArchiveGnuTar}, or {@link ezcArchiveUstar}.
  310. * @access private
  311. */
  312. public static function getTarInstance( ezcArchiveBlockFile $blockFile, $type )
  313. {
  314. switch ( $type )
  315. {
  316. case self::TAR_V7:
  317. return new ezcArchiveV7Tar( $blockFile );
  318. case self::TAR_USTAR:
  319. return new ezcArchiveUstarTar( $blockFile );
  320. case self::TAR_PAX:
  321. return new ezcArchivePaxTar( $blockFile );
  322. case self::TAR_GNU:
  323. return new ezcArchiveGnuTar( $blockFile );
  324. case self::TAR:
  325. return new ezcArchiveUstarTar( $blockFile ); // Default type.
  326. }
  327. return null;
  328. }
  329. /**
  330. * Open a zip instance. This method is made public for testing purposes, and
  331. * should not be used.
  332. *
  333. * @param ezcArchiveCharacterFile $charFile The character file which
  334. * contains the archive.
  335. * @return ezcArchive Subclass of ezcArchive: {@link ezcArchiveZip}.
  336. * @access private
  337. */
  338. public static function getZipInstance( ezcArchiveCharacterFile $charFile )
  339. {
  340. return new ezcArchiveZip( $charFile );
  341. }
  342. /**
  343. * Returns true if the iterator points to a valid entry, otherwise false.
  344. *
  345. * @return bool
  346. */
  347. public function valid()
  348. {
  349. return ( $this->fileNumber >= 0 && $this->fileNumber < $this->entriesRead );
  350. }
  351. /**
  352. * Rewinds the iterator to the first entry.
  353. *
  354. * @return void
  355. */
  356. public function rewind()
  357. {
  358. $this->fileNumber = 0;
  359. }
  360. /**
  361. * Returns the current ezcArchiveEntry if it is valid, otherwise false is returned.
  362. *
  363. * @return ezcArchiveEntry
  364. */
  365. public function current()
  366. {
  367. return ( $this->valid() ? $this->entries[$this->fileNumber] : false );
  368. }
  369. /**
  370. * Returns the current key, entry number, if it is valid, otherwise false is returned.
  371. *
  372. * @return int
  373. */
  374. public function key()
  375. {
  376. return ( $this->valid() ? $this->fileNumber : false );
  377. }
  378. /**
  379. * Forwards the iterator to the next entry.
  380. *
  381. * If there is no next entry all iterator methods except for {@link
  382. * rewind()} will return false.
  383. *
  384. * @see rewind()
  385. *
  386. * @return ezcArchiveEntry The next entry if it exists, otherwise false.
  387. */
  388. public function next()
  389. {
  390. if ( $this->valid() )
  391. {
  392. $this->fileNumber++;
  393. if ( $this->valid() )
  394. {
  395. return $this->current();
  396. }
  397. if ( !$this->completed )
  398. {
  399. if ( $this->readCurrentFromArchive() )
  400. {
  401. return $this->current();
  402. }
  403. }
  404. }
  405. return false;
  406. }
  407. /**
  408. * Extract the current entry to which the iterator points.
  409. *
  410. * Extract the current entry to which the iterator points, and return true if the current entry is extracted.
  411. * If the iterator doesn't point to a valid entry, this method returns false.
  412. *
  413. * True if the file is extracted correctly, otherwise false.
  414. *
  415. * @param string $target
  416. * The full path to which the target should be extracted.
  417. * @param bool $keepExisting
  418. * True if the file shouldn't be overwritten if they already exist.
  419. * For the opposite behaviour, false should be given.
  420. *
  421. * @throws ezcArchiveValueException if the archive contains invalid values.
  422. * @throws ezcBaseFileNotFoundException if the link cannot be found.
  423. *
  424. * @return bool
  425. */
  426. public function extractCurrent( $target, $keepExisting = false )
  427. {
  428. if ( $this->file === null )
  429. {
  430. throw new ezcArchiveException( "The archive is closed" );
  431. }
  432. if ( !$this->valid() )
  433. {
  434. return false;
  435. }
  436. $isWindows = ( substr( php_uname( 's' ), 0, 7 ) == 'Windows' ) ? true : false;
  437. $entry = $this->current();
  438. $type = $entry->getType();
  439. $fileName = $target . DIRECTORY_SEPARATOR. $entry->getPath();
  440. if ( $type == ezcArchiveEntry::IS_LINK )
  441. {
  442. $linkName = $target . DIRECTORY_SEPARATOR . $entry->getLink();
  443. if ( !file_exists( $linkName ) )
  444. {
  445. throw new ezcBaseFileNotFoundException( $linkName, "link", "Hard link could not be created." );
  446. }
  447. }
  448. $this->createDefaultDirectory( $fileName );
  449. if ( !$keepExisting || ( !is_link( $fileName ) && !file_exists( $fileName ) ) )
  450. {
  451. if ( ( file_exists( $fileName ) || is_link( $fileName ) ) && !is_dir( $fileName ) )
  452. {
  453. unlink ( $fileName );
  454. }
  455. if ( !file_exists( $fileName ) ) // For example, directories are not removed.
  456. {
  457. switch ( $type )
  458. {
  459. case ezcArchiveEntry::IS_CHARACTER_DEVICE:
  460. if ( ezcBaseFeatures::hasFunction( 'posix_mknod' ) )
  461. {
  462. posix_mknod( $fileName, POSIX_S_IFCHR, $entry->getMajor(), $entry->getMinor() );
  463. }
  464. else
  465. {
  466. throw new ezcArchiveValueException( $type );
  467. }
  468. break;
  469. case ezcArchiveEntry::IS_BLOCK_DEVICE:
  470. if ( ezcBaseFeatures::hasFunction( 'posix_mknod' ) )
  471. {
  472. posix_mknod( $fileName, POSIX_S_IFBLK, $entry->getMajor(), $entry->getMinor() );
  473. }
  474. else
  475. {
  476. throw new ezcArchiveValueException( $type );
  477. }
  478. break;
  479. case ezcArchiveEntry::IS_FIFO:
  480. if ( ezcBaseFeatures::hasFunction( 'posix_mknod' ) )
  481. {
  482. posix_mknod( $fileName, POSIX_S_IFIFO );
  483. }
  484. else
  485. {
  486. throw new ezcArchiveValueException( $type );
  487. }
  488. break;
  489. case ezcArchiveEntry::IS_SYMBOLIC_LINK:
  490. if ( $isWindows )
  491. {
  492. // FIXME.. need to be sure that target file
  493. // already extracted before copying it to link destination.
  494. $sourcePath = dirname( $fileName ) . '/' . $entry->getLink();
  495. $fileName = str_replace( '/', '\\', $fileName );
  496. copy( $sourcePath, $fileName );
  497. }
  498. else
  499. {
  500. symlink( $entry->getLink(), $fileName );
  501. }
  502. break;
  503. case ezcArchiveEntry::IS_LINK:
  504. if ( $isWindows )
  505. {
  506. copy( $target . DIRECTORY_SEPARATOR . $entry->getLink(), $fileName );
  507. }
  508. else
  509. {
  510. link( $target . DIRECTORY_SEPARATOR . $entry->getLink(), $fileName );
  511. }
  512. break;
  513. case ezcArchiveEntry::IS_DIRECTORY:
  514. $permissions = $entry->getPermissions();
  515. if ( $permissions === null || $permissions === false )
  516. {
  517. $permissions = '0777';
  518. }
  519. mkdir( $fileName, octdec( $permissions ), true );
  520. break;
  521. case ezcArchiveEntry::IS_FILE:
  522. $this->writeCurrentDataToFile( $fileName );
  523. break;
  524. default:
  525. throw new ezcArchiveValueException( $type );
  526. }
  527. if ( $type == ezcArchiveEntry::IS_SYMBOLIC_LINK &&
  528. ezcBaseFeatures::hasFunction( 'posix_geteuid' ) &&
  529. posix_geteuid() == 0 )
  530. {
  531. $user = $entry->getUserId();
  532. $group = $entry->getGroupId();
  533. @lchown( $fileName, $user );
  534. @lchgrp( $fileName, $group );
  535. }
  536. // Change the username and group if the filename exists and if
  537. // the intention is to keep it as a file. A zip archive
  538. // stores the symlinks in a file; thus don't change these.
  539. if ( file_exists( $fileName ) && ( $type == ezcArchiveEntry::IS_FILE || $type == ezcArchiveEntry::IS_DIRECTORY ) )
  540. {
  541. $group = $entry->getGroupId();
  542. $user = $entry->getUserId();
  543. $time = $entry->getModificationTime();
  544. $perms = octdec( $entry->getPermissions() );
  545. if ( $this->options && $this->options->extractCallback )
  546. {
  547. $this->options->extractCallback->{$type == ezcArchiveEntry::IS_DIRECTORY ? 'createDirectoryCallback' : 'createFileCallback'}( $fileName, $perms, $user, $group );
  548. }
  549. if ( ezcBaseFeatures::hasFunction( 'posix_geteuid' ) &&
  550. posix_geteuid() === 0 )
  551. {
  552. @chgrp( $fileName, $group );
  553. @chown( $fileName, $user );
  554. }
  555. if ( $perms != false )
  556. {
  557. chmod( $fileName, $perms );
  558. }
  559. touch( $fileName, $time );
  560. }
  561. }
  562. return true;
  563. }
  564. return false;
  565. }
  566. /**
  567. * Search for the entry number.
  568. *
  569. * The two parameters here are the same as the PHP {@link http://www.php.net/fseek fseek()} method.
  570. * The internal iterator position will be set by $offset added to $whence iterations forward.
  571. * Where $whence is:
  572. * - SEEK_SET, Set the position equal to $offset.
  573. * - SEEK_CUR, Set the current position plus $offset.
  574. * - SEEK_END, Set the last file in archive position plus $offset.
  575. *
  576. * This method returns true if the new position is valid, otherwise false.
  577. *
  578. * @throws ezcArchiveException
  579. * if the archive is closed
  580. * @param int $offset
  581. * @param int $whence
  582. * @return bool
  583. */
  584. public function seek( $offset, $whence = SEEK_SET )
  585. {
  586. if ( $this->file === null )
  587. {
  588. throw new ezcArchiveException( "The archive is closed" );
  589. }
  590. // Cannot trust the current position if the current position is invalid.
  591. if ( $whence == SEEK_CUR && $this->valid() == false )
  592. {
  593. return false;
  594. }
  595. if ( $whence == SEEK_END && !$this->completed )
  596. {
  597. // read the entire archive.
  598. $this->fileNumber = $this->entriesRead;
  599. while ( $this->readCurrentFromArchive() )
  600. {
  601. $this->fileNumber++;
  602. }
  603. }
  604. switch ( $whence )
  605. {
  606. case SEEK_SET:
  607. $requestedFileNumber = $offset;
  608. break;
  609. case SEEK_CUR:
  610. $requestedFileNumber = $offset + $this->fileNumber;
  611. break;
  612. case SEEK_END:
  613. $requestedFileNumber = $offset + $this->entriesRead - 1;
  614. break;
  615. default:
  616. return false; // Invalid whence.
  617. }
  618. $this->fileNumber = $requestedFileNumber;
  619. if ( $this->valid() )
  620. {
  621. return true;
  622. }
  623. if ( !$this->completed )
  624. {
  625. $this->fileNumber = $this->entriesRead - 1;
  626. while ( $this->fileNumber != $requestedFileNumber )
  627. {
  628. $this->fileNumber++;
  629. if ( !$this->readCurrentFromArchive() )
  630. {
  631. break;
  632. }
  633. }
  634. return $this->valid();
  635. }
  636. return false;
  637. }
  638. /**
  639. * Creates all the directories needed to create the file $file.
  640. *
  641. * @param string $file Path to a file, where all the base directory names will be created.
  642. */
  643. protected function createDefaultDirectory( $file )
  644. {
  645. // Does the directory exist?
  646. $dirName = dirname( $file );
  647. if ( !file_exists( $dirName ) )
  648. {
  649. // Try to create the directory.
  650. if ( substr( php_uname( 's' ), 0, 7 ) == 'Windows' )
  651. {
  652. // make all slashes to be '/'
  653. $dirName = str_replace( '/', '\\', $dirName );
  654. }
  655. // Call the callback, to see whether we need to change permissions
  656. $permissions = 0777;
  657. $dummy = null;
  658. if ( $this->options && $this->options->extractCallback )
  659. {
  660. $this->options->extractCallback->createDirectoryCallback( $dirName, $permissions, $dummy, $dummy );
  661. }
  662. mkdir( $dirName, $permissions, true );
  663. }
  664. }
  665. /**
  666. * Appends a file to the archive after the current entry.
  667. *
  668. * One or multiple files can be added directly after the current file.
  669. * The remaining entries after the current are removed from the archive!
  670. *
  671. * The $files can either be a string or an array of strings. Which, respectively, represents a
  672. * single file or multiple files.
  673. *
  674. * $prefix specifies the begin part of the $files path that should not be included in the archive.
  675. * The files in the archive are always stored relatively.
  676. *
  677. * Example:
  678. * <code>
  679. * $tar = ezcArchive( "/tmp/my_archive.tar", ezcArchive::TAR );
  680. *
  681. * // Append two files to the end of the archive.
  682. * $tar->seek( 0, SEEK_END );
  683. * $tar->appendToCurrent( array( "/home/rb/file1.txt", "/home/rb/file2.txt" ), "/home/rb/" );
  684. * </code>
  685. *
  686. * When multiple files are added to the archive at the same time, thus using an array, does not
  687. * necessarily produce the same archive as repeatively adding one file to the archive.
  688. * For example, the Tar archive format, can detect that files hardlink to each other and will store
  689. * it in a more efficient way.
  690. *
  691. * @throws ezcArchiveWriteException if one of the files cannot be written to the archive.
  692. * @throws ezcFileReadException if one of the files cannot be read from the local filesystem.
  693. *
  694. * @param string|array(string) $files Array or a single path to a file.
  695. * @param string $prefix First part of the path used in $files.
  696. * @return bool
  697. */
  698. public abstract function appendToCurrent( $files, $prefix );
  699. /**
  700. * Appends a file or directory to the end of the archive. Multiple files or directory can
  701. * be added to the archive when an array is used as input parameter.
  702. *
  703. * @see appendToCurrent()
  704. *
  705. * @throws ezcArchiveWriteException if one of the files cannot be written to the archive.
  706. * @throws ezcFileReadException if one of the files cannot be read from the local filesystem.
  707. *
  708. * @param string|array(string) $files Array or a single path to a file.
  709. * @param string $prefix First part of the path used in $files.
  710. * @return bool
  711. */
  712. public abstract function append( $files, $prefix );
  713. /**
  714. * Truncates the archive to $fileNumber of files.
  715. *
  716. * The $fileNumber parameter specifies the amount of files that should remain.
  717. * If the default value, zero, is used then the entire archive file is cleared.
  718. *
  719. * @param int $fileNumber
  720. * @return bool
  721. */
  722. public abstract function truncate( $fileNumber = 0 );
  723. /**
  724. * Writes the file data from the current entry to the given file.
  725. *
  726. * @param string $targetPath The absolute or relative path of the target file.
  727. * @return void
  728. */
  729. protected abstract function writeCurrentDataToFile( $targetPath );
  730. /**
  731. * Returns an array that lists the content of the archive.
  732. *
  733. * Use the getArchiveEntry method to get more information about an entry.
  734. *
  735. * @see __toString()
  736. *
  737. * @throws ezcArchiveException
  738. * if the archive is closed
  739. * @return array(string)
  740. */
  741. public function getListing()
  742. {
  743. if ( $this->file === null )
  744. {
  745. throw new ezcArchiveException( "The archive is closed" );
  746. }
  747. $result = array();
  748. $this->rewind();
  749. do
  750. {
  751. $entry = $this->current();
  752. $result[] = rtrim( $entry->__toString(), "\n" ); // remove newline.
  753. } while ( $this->next() );
  754. return $result;
  755. }
  756. /**
  757. * Returns a string which represents all the entries from the archive.
  758. *
  759. * @throws ezcArchiveException
  760. * if the archive is closed
  761. * @return string
  762. */
  763. public function __toString()
  764. {
  765. if ( $this->file === null )
  766. {
  767. throw new ezcArchiveException( "The archive is closed" );
  768. }
  769. $result = "";
  770. $this->rewind();
  771. while ( $this->valid() )
  772. {
  773. $result .= $this->current()->__toString() . "\n";
  774. $this->next();
  775. }
  776. return $result;
  777. }
  778. /**
  779. * Extract entries from the archive to the target directory.
  780. *
  781. * All entries from the archive are extracted to the target directory.
  782. * By default the files in the target directory are overwritten.
  783. * If the $keepExisting is set to true, the files from the archive will not overwrite existing files.
  784. *
  785. * @see extractCurrent()
  786. *
  787. * @throws ezcArchiveException
  788. * if the archive is closed
  789. * @throws ezcArchiveEmptyException
  790. * if the archive is invalid
  791. * @param string $target Absolute or relative path of the directory.
  792. * @param bool $keepExisting If set to true then the file will be overwritten, otherwise not.
  793. * @return void
  794. */
  795. public function extract( $target, $keepExisting = false )
  796. {
  797. if ( $this->file === null )
  798. {
  799. throw new ezcArchiveException( "The archive is closed" );
  800. }
  801. $this->rewind();
  802. if ( !$this->valid() )
  803. {
  804. throw new ezcArchiveEmptyException( );
  805. }
  806. while ( $this->valid() )
  807. {
  808. $this->extractCurrent( $target, $keepExisting );
  809. $this->next();
  810. }
  811. }
  812. /**
  813. * Returns true if the current archive is empty, otherwise false.
  814. *
  815. * @return bool
  816. */
  817. public function isEmpty()
  818. {
  819. return ( $this->entriesRead == 0 );
  820. }
  821. /**
  822. * Get the file entries from the archive.
  823. *
  824. * @param string|array(string) $files Array or a single path to a file.
  825. * @param string $prefix First part of the path used in $files.
  826. *
  827. * @return ezcArchiveEntry
  828. */
  829. protected function getEntries( $files, $prefix )
  830. {
  831. if ( !is_array( $files ) )
  832. {
  833. $files = array( $files );
  834. }
  835. // Check whether the files are correct.
  836. foreach ( $files as $file )
  837. {
  838. if ( !file_exists( $file ) && !is_link( $file ) )
  839. {
  840. throw new ezcBaseFileNotFoundException( $file );
  841. }
  842. }
  843. // Search for all the entries, because otherwise hardlinked files show up as an ordinary file.
  844. return ezcArchiveEntry::getEntryFromFile( $files, $prefix );
  845. }
  846. }
  847. ?>