PageRenderTime 46ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/Archive/src/archive.php

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