diff --git a/src/PhpZip/Stream/ZipInputStream.php b/src/PhpZip/Stream/ZipInputStream.php index dea05de..b14fd9c 100644 --- a/src/PhpZip/Stream/ZipInputStream.php +++ b/src/PhpZip/Stream/ZipInputStream.php @@ -144,8 +144,14 @@ class ZipInputStream implements ZipInputStreamInterface ); } // .ZIP file comment (variable size) - if (0 < $data['commentLength']) { - $comment = fread($this->in, $data['commentLength']); + if ($data['commentLength'] > 0) { + $comment = ''; + $offset = 0; + while ($offset < $data['commentLength']) { + $read = min(8192 /* chunk size */, $data['commentLength'] - $offset); + $comment .= fread($this->in, $read); + $offset += $read; + } } $this->preamble = $endOfCentralDirRecordPos; $this->postamble = $size - ftell($this->in); @@ -314,7 +320,13 @@ class ZipInputStream implements ZipInputStreamInterface // $utf8 = ($data['gpbf'] & ZipEntry::GPBF_UTF8) !== 0; // See appendix D of PKWARE's ZIP File Format Specification. - $name = fread($this->in, $data['fileLength']); + $name = ''; + $offset = 0; + while ($offset < $data['fileLength']) { + $read = min(8192 /* chunk size */, $data['fileLength'] - $offset); + $name .= fread($this->in, $read); + $offset += $read; + } $entry = new ZipSourceEntry($this); $entry->setName($name); @@ -329,10 +341,24 @@ class ZipInputStream implements ZipInputStreamInterface $entry->setExternalAttributes($data['rawExternalAttributes']); $entry->setOffset($data['lfhOff']); // must be unmapped! if ($data['extraLength'] > 0) { - $entry->setExtra(fread($this->in, $data['extraLength'])); + $extra = ''; + $offset = 0; + while ($offset < $data['extraLength']) { + $read = min(8192 /* chunk size */, $data['extraLength'] - $offset); + $extra .= fread($this->in, $read); + $offset += $read; + } + $entry->setExtra($extra); } if ($data['commentLength'] > 0) { - $entry->setComment(fread($this->in, $data['commentLength'])); + $comment = ''; + $offset = 0; + while ($offset < $data['commentLength']) { + $read = min(8192 /* chunk size */, $data['commentLength'] - $offset); + $comment .= fread($this->in, $read); + $offset += $read; + } + $entry->setComment($comment); } return $entry; } @@ -382,10 +408,14 @@ class ZipInputStream implements ZipInputStreamInterface // Get raw entry content $compressedSize = $entry->getCompressedSize(); $compressedSize = PHP_INT_SIZE === 4 ? sprintf('%u', $compressedSize) : $compressedSize; + $content = ''; if ($compressedSize > 0) { - $content = fread($this->in, $compressedSize); - } else { - $content = ''; + $offset = 0; + while ($offset < $compressedSize) { + $read = min(8192 /* chunk size */, $compressedSize - $offset); + $content .= fread($this->in, $read); + $offset += $read; + } } $skipCheckCrc = false; @@ -453,6 +483,14 @@ class ZipInputStream implements ZipInputStreamInterface throw new ZipUnsupportMethodException($entry->getName() . " (compression method " . $method . " is not supported)"); } + + if ($content === false) { + throw new ZipException(sprintf( + 'Failed to get the contents of the zip entry "%s"', + $entry->getName() + )); + } + if (!$skipCheckCrc) { $localCrc = crc32($content); $localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc; @@ -498,7 +536,13 @@ class ZipInputStream implements ZipInputStreamInterface 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); + $extra = ''; + $offset = 0; + while ($offset < $sourceExtraLength) { + $read = min(8192 /* chunk size */, $sourceExtraLength - $offset); + $extra .= fread($this->in, $read); + $offset += $read; + } $extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($extra, $entry); if (isset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]) && $this->zipModel->isZipAlign()) { unset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]); diff --git a/tests/PhpZip/Issue24Test.php b/tests/PhpZip/Issue24Test.php new file mode 100644 index 0000000..ddf040e --- /dev/null +++ b/tests/PhpZip/Issue24Test.php @@ -0,0 +1,104 @@ +addFromString( + 'file.txt', + $fileContents, + ZipFile::METHOD_DEFLATED + ); + $zip->saveAsFile($this->outputFilename); + $zip->close(); + + $this->assertCorrectZipArchive($this->outputFilename); + + $stream = fopen('dummyfs://localhost/' . $this->outputFilename, 'rb'); + $this->assertNotFalse($stream); + $zip->openFromStream($stream); + $this->assertEquals($zip->getListFiles(), ['file.txt']); + $this->assertEquals($zip['file.txt'], $fileContents); + $zip->close(); + } +} + +/** + * Try to load using dummy stream + */ +class DummyFileSystemStream +{ + /** + * @var resource + */ + private $fp; + + function stream_open($path, $mode, $options, &$opened_path) + { +// echo "DummyFileSystemStream->stream_open($path, $mode, $options)" . PHP_EOL; + + $parsedUrl = parse_url($path); + $path = $parsedUrl['path']; + $this->fp = fopen($path, $mode); + + return true; + } + + function stream_read($count) + { +// echo "DummyFileSystemStream->stream_read($count)" . PHP_EOL; + $position = ftell($this->fp); + +// echo "Loading chunk " . $position . " to " . ($position + $count - 1) . PHP_EOL; + $ret = fread($this->fp, $count); + +// echo "String length: " . strlen($ret) . PHP_EOL; + + return $ret; + } + + function stream_tell() + { +// echo "DummyFileSystemStream->stream_tell()" . PHP_EOL; + return ftell($this->fp); + } + + function stream_eof() + { +// echo "DummyFileSystemStream->stream_eof()" . PHP_EOL; + $isfeof = feof($this->fp); + return $isfeof; + } + + function stream_seek($offset, $whence) + { +// echo "DummyFileSystemStream->stream_seek($offset, $whence)" . PHP_EOL; + fseek($this->fp, $offset, $whence); + } + + function stream_stat() + { +// echo "DummyFileSystemStream->stream_stat()" . PHP_EOL; + return fstat($this->fp); + } +} \ No newline at end of file