| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- <?php
- namespace PhpZip\Stream;
- use PhpZip\Crypto\TraditionalPkwareEncryptionEngine;
- use PhpZip\Crypto\WinZipAesEngine;
- use PhpZip\Exception\Crc32Exception;
- use PhpZip\Exception\InvalidArgumentException;
- use PhpZip\Exception\RuntimeException;
- use PhpZip\Exception\ZipCryptoException;
- use PhpZip\Exception\ZipException;
- use PhpZip\Exception\ZipUnsupportMethod;
- use PhpZip\Extra\ExtraFieldsCollection;
- use PhpZip\Extra\ExtraFieldsFactory;
- use PhpZip\Extra\Fields\ApkAlignmentExtraField;
- use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
- use PhpZip\Mapper\OffsetPositionMapper;
- use PhpZip\Mapper\PositionMapper;
- use PhpZip\Model\EndOfCentralDirectory;
- use PhpZip\Model\Entry\ZipSourceEntry;
- use PhpZip\Model\ZipEntry;
- use PhpZip\Model\ZipModel;
- use PhpZip\Util\PackUtil;
- use PhpZip\Util\StringUtil;
- use PhpZip\ZipFileInterface;
- /**
- * Read zip file
- *
- * @author Ne-Lexa alexey@nelexa.ru
- * @license MIT
- */
- class ZipInputStream implements ZipInputStreamInterface
- {
- /**
- * @var resource
- */
- protected $in;
- /**
- * @var PositionMapper
- */
- protected $mapper;
- /**
- * @var int The number of bytes in the preamble of this ZIP file.
- */
- protected $preamble = 0;
- /**
- * @var int The number of bytes in the postamble of this ZIP file.
- */
- protected $postamble = 0;
- /**
- * @var ZipModel
- */
- protected $zipModel;
- /**
- * ZipInputStream constructor.
- * @param resource $in
- * @throws RuntimeException
- */
- public function __construct($in)
- {
- if (!is_resource($in)) {
- throw new RuntimeException('$in must be resource');
- }
- $this->in = $in;
- $this->mapper = new PositionMapper();
- }
- /**
- * @return ZipModel
- */
- public function readZip()
- {
- $this->checkZipFileSignature();
- $endOfCentralDirectory = $this->readEndOfCentralDirectory();
- $entries = $this->mountCentralDirectory($endOfCentralDirectory);
- $this->zipModel = ZipModel::newSourceModel($entries, $endOfCentralDirectory);
- return $this->zipModel;
- }
- /**
- * Check zip file signature
- *
- * @throws ZipException if this not .ZIP file.
- */
- protected function checkZipFileSignature()
- {
- rewind($this->in);
- // Constraint: A ZIP file must start with a Local File Header
- // or a (ZIP64) End Of Central Directory Record if it's empty.
- $signatureBytes = fread($this->in, 4);
- if (strlen($signatureBytes) < 4) {
- throw new ZipException("Invalid zip file.");
- }
- $signature = unpack('V', $signatureBytes)[1];
- if (
- ZipEntry::LOCAL_FILE_HEADER_SIG !== $signature
- && EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $signature
- && EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $signature
- ) {
- throw new ZipException("Expected Local File Header or (ZIP64) End Of Central Directory Record! Signature: " . $signature);
- }
- }
- /**
- * @return EndOfCentralDirectory
- * @throws ZipException
- */
- protected function readEndOfCentralDirectory()
- {
- $comment = null;
- // Search for End of central directory record.
- $stats = fstat($this->in);
- $size = $stats['size'];
- $max = $size - EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN;
- $min = $max >= 0xffff ? $max - 0xffff : 0;
- for ($endOfCentralDirRecordPos = $max; $endOfCentralDirRecordPos >= $min; $endOfCentralDirRecordPos--) {
- fseek($this->in, $endOfCentralDirRecordPos, SEEK_SET);
- // end of central dir signature 4 bytes (0x06054b50)
- if (EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== unpack('V', fread($this->in, 4))[1]) {
- continue;
- }
- // number of this disk - 2 bytes
- // number of the disk with the start of the
- // central directory - 2 bytes
- // total number of entries in the central
- // directory on this disk - 2 bytes
- // total number of entries in the central
- // directory - 2 bytes
- // size of the central directory - 4 bytes
- // offset of start of central directory with
- // respect to the starting disk number - 4 bytes
- // ZIP file comment length - 2 bytes
- $data = unpack(
- 'vdiskNo/vcdDiskNo/vcdEntriesDisk/vcdEntries/VcdSize/VcdPos/vcommentLength',
- fread($this->in, 18)
- );
- if (0 !== $data['diskNo'] || 0 !== $data['cdDiskNo'] || $data['cdEntriesDisk'] !== $data['cdEntries']) {
- throw new ZipException(
- "ZIP file spanning/splitting is not supported!"
- );
- }
- // .ZIP file comment (variable size)
- if (0 < $data['commentLength']) {
- $comment = fread($this->in, $data['commentLength']);
- }
- $this->preamble = $endOfCentralDirRecordPos;
- $this->postamble = $size - ftell($this->in);
- // Check for ZIP64 End Of Central Directory Locator.
- $endOfCentralDirLocatorPos = $endOfCentralDirRecordPos - EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_LEN;
- fseek($this->in, $endOfCentralDirLocatorPos, SEEK_SET);
- // zip64 end of central dir locator
- // signature 4 bytes (0x07064b50)
- if (
- 0 > $endOfCentralDirLocatorPos ||
- ftell($this->in) === $size ||
- EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG !== unpack('V', fread($this->in, 4))[1]
- ) {
- // Seek and check first CFH, probably requiring an offset mapper.
- $offset = $endOfCentralDirRecordPos - $data['cdSize'];
- fseek($this->in, $offset, SEEK_SET);
- $offset -= $data['cdPos'];
- if (0 !== $offset) {
- $this->mapper = new OffsetPositionMapper($offset);
- }
- $entryCount = $data['cdEntries'];
- return new EndOfCentralDirectory($entryCount, $comment);
- }
- // number of the disk with the
- // start of the zip64 end of
- // central directory 4 bytes
- $zip64EndOfCentralDirectoryRecordDisk = unpack('V', fread($this->in, 4))[1];
- // relative offset of the zip64
- // end of central directory record 8 bytes
- $zip64EndOfCentralDirectoryRecordPos = PackUtil::unpackLongLE(fread($this->in, 8));
- // total number of disks 4 bytes
- $totalDisks = unpack('V', fread($this->in, 4))[1];
- if (0 !== $zip64EndOfCentralDirectoryRecordDisk || 1 !== $totalDisks) {
- throw new ZipException("ZIP file spanning/splitting is not supported!");
- }
- fseek($this->in, $zip64EndOfCentralDirectoryRecordPos, SEEK_SET);
- // zip64 end of central dir
- // signature 4 bytes (0x06064b50)
- $zip64EndOfCentralDirSig = unpack('V', fread($this->in, 4))[1];
- if (EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $zip64EndOfCentralDirSig) {
- throw new ZipException("Expected ZIP64 End Of Central Directory Record!");
- }
- // size of zip64 end of central
- // directory record 8 bytes
- // version made by 2 bytes
- // version needed to extract 2 bytes
- fseek($this->in, 12, SEEK_CUR);
- // number of this disk 4 bytes
- $diskNo = unpack('V', fread($this->in, 4))[1];
- // number of the disk with the
- // start of the central directory 4 bytes
- $cdDiskNo = unpack('V', fread($this->in, 4))[1];
- // total number of entries in the
- // central directory on this disk 8 bytes
- $cdEntriesDisk = PackUtil::unpackLongLE(fread($this->in, 8));
- // total number of entries in the
- // central directory 8 bytes
- $cdEntries = PackUtil::unpackLongLE(fread($this->in, 8));
- if (0 !== $diskNo || 0 !== $cdDiskNo || $cdEntriesDisk !== $cdEntries) {
- throw new ZipException("ZIP file spanning/splitting is not supported!");
- }
- if ($cdEntries < 0 || 0x7fffffff < $cdEntries) {
- throw new ZipException("Total Number Of Entries In The Central Directory out of range!");
- }
- // size of the central directory 8 bytes
- fseek($this->in, 8, SEEK_CUR);
- // offset of start of central
- // directory with respect to
- // the starting disk number 8 bytes
- $cdPos = PackUtil::unpackLongLE(fread($this->in, 8));
- // zip64 extensible data sector (variable size)
- fseek($this->in, $cdPos, SEEK_SET);
- $this->preamble = $zip64EndOfCentralDirectoryRecordPos;
- $entryCount = $cdEntries;
- $zip64 = true;
- return new EndOfCentralDirectory($entryCount, $comment, $zip64);
- }
- // Start recovering file entries from min.
- $this->preamble = $min;
- $this->postamble = $size - $min;
- return new EndOfCentralDirectory(0, $comment);
- }
- /**
- * Reads the central directory from the given seekable byte channel
- * and populates the internal tables with ZipEntry instances.
- *
- * The ZipEntry's will know all data that can be obtained from the
- * central directory alone, but not the data that requires the local
- * file header or additional data to be read.
- *
- * @param EndOfCentralDirectory $endOfCentralDirectory
- * @return ZipEntry[]
- * @throws ZipException
- */
- protected function mountCentralDirectory(EndOfCentralDirectory $endOfCentralDirectory)
- {
- $numEntries = $endOfCentralDirectory->getEntryCount();
- $entries = [];
- for (; $numEntries > 0; $numEntries--) {
- $entry = $this->readEntry();
- // Re-load virtual offset after ZIP64 Extended Information
- // Extra Field may have been parsed, map it to the real
- // offset and conditionally update the preamble size from it.
- $lfhOff = $this->mapper->map($entry->getOffset());
- $lfhOff = PHP_INT_SIZE === 4 ? sprintf('%u', $lfhOff) : $lfhOff;
- if ($lfhOff < $this->preamble) {
- $this->preamble = $lfhOff;
- }
- $entries[$entry->getName()] = $entry;
- }
- if (0 !== $numEntries % 0x10000) {
- throw new ZipException("Expected " . abs($numEntries) .
- ($numEntries > 0 ? " more" : " less") .
- " entries in the Central Directory!");
- }
- if ($this->preamble + $this->postamble >= fstat($this->in)['size']) {
- assert(0 === $numEntries);
- $this->checkZipFileSignature();
- }
- return $entries;
- }
- /**
- * @return ZipEntry
- * @throws InvalidArgumentException
- */
- public function readEntry()
- {
- // central file header signature 4 bytes (0x02014b50)
- $fileHeaderSig = unpack('V', fread($this->in, 4))[1];
- if (ZipOutputStreamInterface::CENTRAL_FILE_HEADER_SIG !== $fileHeaderSig) {
- throw new InvalidArgumentException("Corrupt zip file. Can not read zip entry.");
- }
- // version made by 2 bytes
- // version needed to extract 2 bytes
- // general purpose bit flag 2 bytes
- // compression method 2 bytes
- // last mod file time 2 bytes
- // last mod file date 2 bytes
- // crc-32 4 bytes
- // compressed size 4 bytes
- // uncompressed size 4 bytes
- // file name length 2 bytes
- // extra field length 2 bytes
- // file comment length 2 bytes
- // disk number start 2 bytes
- // internal file attributes 2 bytes
- // external file attributes 4 bytes
- // relative offset of local header 4 bytes
- $data = unpack(
- 'vversionMadeBy/vversionNeededToExtract/vgpbf/' .
- 'vrawMethod/VrawTime/VrawCrc/VrawCompressedSize/' .
- 'VrawSize/vfileLength/vextraLength/vcommentLength/' .
- 'VrawInternalAttributes/VrawExternalAttributes/VlfhOff',
- fread($this->in, 42)
- );
- // $utf8 = 0 !== ($data['gpbf'] & self::GPBF_UTF8);
- // See appendix D of PKWARE's ZIP File Format Specification.
- $name = fread($this->in, $data['fileLength']);
- $entry = new ZipSourceEntry($this);
- $entry->setName($name);
- $entry->setVersionNeededToExtract($data['versionNeededToExtract']);
- $entry->setPlatform($data['versionMadeBy'] >> 8);
- $entry->setMethod($data['rawMethod']);
- $entry->setGeneralPurposeBitFlags($data['gpbf']);
- $entry->setDosTime($data['rawTime']);
- $entry->setCrc($data['rawCrc']);
- $entry->setCompressedSize($data['rawCompressedSize']);
- $entry->setSize($data['rawSize']);
- $entry->setExternalAttributes($data['rawExternalAttributes']);
- $entry->setOffset($data['lfhOff']); // must be unmapped!
- if (0 < $data['extraLength']) {
- $entry->setExtra(fread($this->in, $data['extraLength']));
- }
- if (0 < $data['commentLength']) {
- $entry->setComment(fread($this->in, $data['commentLength']));
- }
- return $entry;
- }
- /**
- * @param ZipEntry $entry
- * @return string
- * @throws ZipException
- */
- public function readEntryContent(ZipEntry $entry)
- {
- if ($entry->isDirectory()) {
- return null;
- }
- if (!($entry instanceof ZipSourceEntry)) {
- throw new InvalidArgumentException('entry must be ' . ZipSourceEntry::class);
- }
- $isEncrypted = $entry->isEncrypted();
- if ($isEncrypted && null === $entry->getPassword()) {
- throw new ZipException("Can not password from entry " . $entry->getName());
- }
- $pos = $entry->getOffset();
- assert(ZipEntry::UNKNOWN !== $pos);
- $pos = PHP_INT_SIZE === 4 ? sprintf('%u', $pos) : $pos;
- $startPos = $pos = $this->mapper->map($pos);
- fseek($this->in, $startPos);
- // local file header signature 4 bytes (0x04034b50)
- if (ZipEntry::LOCAL_FILE_HEADER_SIG !== unpack('V', fread($this->in, 4))[1]) {
- throw new ZipException($entry->getName() . " (expected Local File Header)");
- }
- fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_FILE_NAME_LENGTH_POS);
- // file name length 2 bytes
- // extra field length 2 bytes
- $data = unpack('vfileLength/vextraLength', fread($this->in, 4));
- $pos += ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $data['fileLength'] + $data['extraLength'];
- assert(ZipEntry::UNKNOWN !== $entry->getCrc());
- $method = $entry->getMethod();
- fseek($this->in, $pos);
- // Get raw entry content
- $compressedSize = $entry->getCompressedSize();
- $compressedSize = PHP_INT_SIZE === 4 ? sprintf('%u', $compressedSize) : $compressedSize;
- if ($compressedSize > 0) {
- $content = fread($this->in, $compressedSize);
- } else {
- $content = '';
- }
- $skipCheckCrc = false;
- if ($isEncrypted) {
- if (ZipEntry::METHOD_WINZIP_AES === $method) {
- // Strong Encryption Specification - WinZip AES
- $winZipAesEngine = new WinZipAesEngine($entry);
- $content = $winZipAesEngine->decrypt($content);
- /**
- * @var WinZipAesEntryExtraField $field
- */
- $field = $entry->getExtraFieldsCollection()->get(WinZipAesEntryExtraField::getHeaderId());
- $method = $field->getMethod();
- $entry->setEncryptionMethod($field->getEncryptionMethod());
- $skipCheckCrc = true;
- } else {
- // Traditional PKWARE Decryption
- $zipCryptoEngine = new TraditionalPkwareEncryptionEngine($entry);
- $content = $zipCryptoEngine->decrypt($content);
- $entry->setEncryptionMethod(ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL);
- }
- if (!$skipCheckCrc) {
- // Check CRC32 in the Local File Header or Data Descriptor.
- $localCrc = null;
- if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
- // The CRC32 is in the Data Descriptor after the compressed size.
- // Note the Data Descriptor's Signature is optional:
- // All newer apps should write it (and so does TrueVFS),
- // but older apps might not.
- fseek($this->in, $pos + $compressedSize);
- $localCrc = unpack('V', fread($this->in, 4))[1];
- if (ZipEntry::DATA_DESCRIPTOR_SIG === $localCrc) {
- $localCrc = unpack('V', fread($this->in, 4))[1];
- }
- } else {
- fseek($this->in, $startPos + 14);
- // The CRC32 in the Local File Header.
- $localCrc = sprintf('%u', fread($this->in, 4)[1]);
- $localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc;
- }
- $crc = PHP_INT_SIZE === 4 ? sprintf('%u', $entry->getCrc()) : $entry->getCrc();
- if ($crc != $localCrc) {
- throw new Crc32Exception($entry->getName(), $crc, $localCrc);
- }
- }
- }
- switch ($method) {
- case ZipFileInterface::METHOD_STORED:
- break;
- case ZipFileInterface::METHOD_DEFLATED:
- $content = gzinflate($content);
- break;
- case ZipFileInterface::METHOD_BZIP2:
- if (!extension_loaded('bz2')) {
- throw new ZipException('Extension bzip2 not install');
- }
- $content = bzdecompress($content);
- break;
- default:
- throw new ZipUnsupportMethod($entry->getName() .
- " (compression method " . $method . " is not supported)");
- }
- if (!$skipCheckCrc) {
- $localCrc = crc32($content);
- $localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc;
- $crc = PHP_INT_SIZE === 4 ? sprintf('%u', $entry->getCrc()) : $entry->getCrc();
- if ($crc != $localCrc) {
- if ($isEncrypted) {
- throw new ZipCryptoException("Wrong password");
- }
- throw new Crc32Exception($entry->getName(), $crc, $localCrc);
- }
- }
- return $content;
- }
- /**
- * @return resource
- */
- public function getStream()
- {
- return $this->in;
- }
- /**
- * Copy the input stream of the LOC entry zip and the data into
- * the output stream and zip the alignment if necessary.
- *
- * @param ZipEntry $entry
- * @param ZipOutputStreamInterface $out
- */
- public function copyEntry(ZipEntry $entry, ZipOutputStreamInterface $out)
- {
- $pos = $entry->getOffset();
- assert(ZipEntry::UNKNOWN !== $pos);
- $pos = PHP_INT_SIZE === 4 ? sprintf('%u', $pos) : $pos;
- $pos = $this->mapper->map($pos);
- $nameLength = strlen($entry->getName());
- fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, SEEK_SET);
- $sourceExtraLength = $destExtraLength = unpack('v', fread($this->in, 2))[1];
- if ($sourceExtraLength > 0) {
- // read Local File Header extra fields
- fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength, SEEK_SET);
- $extra = fread($this->in, $sourceExtraLength);
- $extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($extra, $entry);
- if (isset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]) && $this->zipModel->isZipAlign()) {
- unset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]);
- $destExtraLength = strlen(ExtraFieldsFactory::createSerializedData($extraFieldsCollection));
- }
- } else {
- $extraFieldsCollection = new ExtraFieldsCollection();
- }
- $dataAlignmentMultiple = $this->zipModel->getZipAlign();
- $copyInToOutLength = $entry->getCompressedSize();
- fseek($this->in, $pos, SEEK_SET);
- if (
- $this->zipModel->isZipAlign() &&
- !$entry->isEncrypted() &&
- $entry->getMethod() === ZipFileInterface::METHOD_STORED
- ) {
- if (StringUtil::endsWith($entry->getName(), '.so')) {
- $dataAlignmentMultiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
- }
- $dataMinStartOffset =
- ftell($out->getStream()) +
- ZipEntry::LOCAL_FILE_HEADER_MIN_LEN +
- $destExtraLength +
- $nameLength +
- ApkAlignmentExtraField::ALIGNMENT_ZIP_EXTRA_MIN_SIZE_BYTES;
- $padding =
- ($dataAlignmentMultiple - ($dataMinStartOffset % $dataAlignmentMultiple))
- % $dataAlignmentMultiple;
- $alignExtra = new ApkAlignmentExtraField();
- $alignExtra->setMultiple($dataAlignmentMultiple);
- $alignExtra->setPadding($padding);
- $extraFieldsCollection->add($alignExtra);
- $extra = ExtraFieldsFactory::createSerializedData($extraFieldsCollection);
- // copy Local File Header without extra field length
- // from input stream to output stream
- stream_copy_to_stream($this->in, $out->getStream(), ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2);
- // write new extra field length (2 bytes) to output stream
- fwrite($out->getStream(), pack('v', strlen($extra)));
- // skip 2 bytes to input stream
- fseek($this->in, 2, SEEK_CUR);
- // copy name from input stream to output stream
- stream_copy_to_stream($this->in, $out->getStream(), $nameLength);
- // write extra field to output stream
- fwrite($out->getStream(), $extra);
- // skip source extraLength from input stream
- fseek($this->in, $sourceExtraLength, SEEK_CUR);
- } else {
- $copyInToOutLength += ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $sourceExtraLength + $nameLength;
- ;
- }
- if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
- // crc-32 4 bytes
- // compressed size 4 bytes
- // uncompressed size 4 bytes
- $copyInToOutLength += 12;
- if ($entry->isZip64ExtensionsRequired()) {
- // compressed size +4 bytes
- // uncompressed size +4 bytes
- $copyInToOutLength += 8;
- }
- }
- // copy loc, data, data descriptor from input to output stream
- stream_copy_to_stream($this->in, $out->getStream(), $copyInToOutLength);
- }
- /**
- * @param ZipEntry $entry
- * @param ZipOutputStreamInterface $out
- */
- public function copyEntryData(ZipEntry $entry, ZipOutputStreamInterface $out)
- {
- $offset = $entry->getOffset();
- $offset = PHP_INT_SIZE === 4 ? sprintf('%u', $offset) : $offset;
- $offset = $this->mapper->map($offset);
- $nameLength = strlen($entry->getName());
- fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, SEEK_SET);
- $extraLength = unpack('v', fread($this->in, 2))[1];
- fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength + $extraLength, SEEK_SET);
- // copy raw data from input stream to output stream
- stream_copy_to_stream($this->in, $out->getStream(), $entry->getCompressedSize());
- }
- public function __destruct()
- {
- $this->close();
- }
- public function close()
- {
- if ($this->in != null) {
- fclose($this->in);
- $this->in = null;
- }
- }
- }
|