1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-08-15 11:44:56 +02:00

22 Commits

Author SHA1 Message Date
Ne-Lexa
19e17fb730 Merge branch 'hotfix/3.1.11' 2019-03-13 15:41:56 +03:00
Ne-Lexa
d8f913ac67 removed nightly PHP (8.0-dev) from travis 2019-03-13 15:37:01 +03:00
Ne-Lexa
1d1c8559cd update travis config 2019-03-13 15:29:40 +03:00
Ne-Lexa
e1108f9a24 Added .gitattributes to exclude tests from the release version #34 2019-03-13 15:28:38 +03:00
Ne-Lexa
53da7053ba Merge branch 'hotfix/3.1.10' 2019-03-13 15:16:08 +03:00
Ne-Lexa
e8745e0379 fix #34 2019-03-13 15:15:52 +03:00
Ne-Lexa
b3277fcc5c Replaced the execution operator backticks with the function call exec in tests 2019-03-13 15:14:01 +03:00
Ne-Lexa
c9871c9f80 Merge branch 'release-3.1.9' 2018-11-02 15:15:08 +03:00
Ne-Lexa
650fab4bad Merge branch 'master' into develop 2018-11-02 15:14:52 +03:00
Ne-Lexa
fd9750c4f3 Merge pull request #26 from belgattitude/fix/non-seekable-streams
Re-enable support for remote streams, fix #25
2018-11-02 15:11:54 +03:00
Sébastien Vanvelthem
e903642893 Re-enable support for remote streams, fix #25 2018-11-02 11:53:35 +01:00
wapplay
516d0c1e77 Merge tag '3.1.8' into develop
Tagging version 3.1.8 3.1.8
2018-10-21 19:31:21 +03:00
wapplay
9934a860c1 Merge branch 'release/3.1.8' 2018-10-21 19:31:21 +03:00
wapplay
c9f597308e cs fix 2018-10-21 19:30:45 +03:00
wapplay
837454ba7e Added additional check for correct decompression 2018-10-21 19:25:13 +03:00
wapplay
680a9d92c1 Merge branch 'hotfix/3.1.7' 2018-10-21 01:54:45 +03:00
wapplay
04a92e7904 Merge tag '3.1.7' into develop
Tagging hotfix 3.1.7 3.1.7
2018-10-21 01:54:45 +03:00
wapplay
e4e3a7504e Test streamWrapper extract (issue #24) 2018-10-21 01:54:30 +03:00
wapplay
d8bb1be43b Fix #24 Change fread for variable size reads 2018-10-21 01:32:01 +03:00
Ne-Lexa
c163f0583e update pull request template 2018-10-18 10:46:49 +03:00
Ne-Lexa
116a617744 added github templates 2018-10-18 10:42:57 +03:00
Ne-Lexa
e207086a75 Merge tag '3.1.6' into develop
Tagging version 3.1.6 3.1.6
2018-10-11 10:54:30 +03:00
20 changed files with 334 additions and 49 deletions

6
.gitattributes vendored Normal file
View File

@@ -0,0 +1,6 @@
.gitattributes export-ignore
.github export-ignore
.gitignore export-ignore
.travis.yml export-ignore
phpunit.xml export-ignore
tests export-ignore

23
.github/ISSUE_TEMPLATE/1_Bug_report.md vendored Normal file
View File

@@ -0,0 +1,23 @@
---
name: 🔴 Bug Report
about: Report errors and problems
---
| Q | A
| -----------------------------| ---
| Library version(s) affected: | x.y.z
| PHP version(s): | x.y.z
| OS (with bit depth): | <!-- ex. Ubuntu 18.04 64-bit -->
**Description**
<!-- A clear and concise description of the problem. -->
**How to reproduce**
<!-- Code to reproduce the problem. -->
**Possible Solution**
<!--- Optional: only if you have suggestions on a fix/reason for the bug -->
**Additional context**
<!-- Optional: any other context about the problem: error messages, stack trace, zip files, etc. -->

View File

@@ -0,0 +1,12 @@
---
name: 🚀 Feature Request
about: Ideas for new features and improvements
---
**Description**
<!-- A clear and concise description of the new feature. -->
**Example**
<!-- A simple example of the new feature in action (include PHP code)
If the new feature changes an existing feature, include a simple before/after comparison. -->

12
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,12 @@
| Q | A
| ------------- | ---
| Bug fix? | yes/no
| New feature? | yes/no <!-- don't forget to update CHANGELOG.md file -->
<!--
Write a short README entry for your feature/bugfix here (replace this comment block.)
Do NOT send pull request to `master` branch.
Please send to `develop` branch instead.
Any PR to `master` branch will NOT be merged.
-->

View File

@@ -5,7 +5,7 @@ php:
- '7.0'
- '7.1'
- '7.2'
- nightly
- '7.3'
# cache vendor dirs
cache:
@@ -15,7 +15,7 @@ cache:
install:
- travis_retry composer self-update && composer --version
- travis_retry composer install --prefer-dist --no-interaction
- travis_retry composer install --no-interaction
before_script:
- sudo apt-get install p7zip-full

View File

@@ -148,7 +148,10 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
$checkByte = ($this->entry->getCrc() >> 24) & 0xff;
}
if ($byte !== $checkByte) {
throw new ZipAuthenticationException("Bad password for entry " . $this->entry->getName());
throw new ZipAuthenticationException(sprintf(
'Invalid password for zip entry "%s"',
$this->entry->getName()
));
}
$outputContent = "";

View File

@@ -41,7 +41,9 @@ class ZipNewEntry extends ZipAbstractEntry
public function getEntryContent()
{
if (is_resource($this->content)) {
rewind($this->content);
if (stream_get_meta_data($this->content)['seekable']) {
rewind($this->content);
}
return stream_get_contents($this->content);
}
return $this->content;

View File

@@ -25,14 +25,14 @@ class ZipNewFileEntry extends ZipAbstractEntry
public function __construct($file)
{
parent::__construct();
if ($file === null){
if ($file === null) {
throw new InvalidArgumentException("file is null");
}
$file = (string)$file;
if (!is_file($file)){
if (!is_file($file)) {
throw new ZipException("File $file does not exist.");
}
if (!is_readable($file)){
if (!is_readable($file)) {
throw new ZipException("The '$file' file could not be read. Check permissions.");
}
$this->file = $file;
@@ -45,9 +45,9 @@ class ZipNewFileEntry extends ZipAbstractEntry
*/
public function getEntryContent()
{
if (!is_file($this->file)){
if (!is_file($this->file)) {
throw new RuntimeException("File {$this->file} does not exist.");
}
return file_get_contents($this->file);
}
}
}

View File

@@ -7,7 +7,7 @@ use PhpZip\Crypto\WinZipAesEngine;
use PhpZip\Exception\Crc32Exception;
use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\RuntimeException;
use PhpZip\Exception\ZipCryptoException;
use PhpZip\Exception\ZipAuthenticationException;
use PhpZip\Exception\ZipException;
use PhpZip\Exception\ZipUnsupportMethodException;
use PhpZip\Extra\ExtraFieldsCollection;
@@ -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;
@@ -440,7 +470,7 @@ class ZipInputStream implements ZipInputStreamInterface
case ZipFileInterface::METHOD_STORED:
break;
case ZipFileInterface::METHOD_DEFLATED:
$content = gzinflate($content);
$content = @gzinflate($content);
break;
case ZipFileInterface::METHOD_BZIP2:
if (!extension_loaded('bz2')) {
@@ -448,18 +478,38 @@ class ZipInputStream implements ZipInputStreamInterface
}
/** @noinspection PhpComposerExtensionStubsInspection */
$content = bzdecompress($content);
if (is_int($content)) { // decompress error
$content = false;
}
break;
default:
throw new ZipUnsupportMethodException($entry->getName() .
" (compression method " . $method . " is not supported)");
}
if ($content === false) {
if ($isEncrypted) {
throw new ZipAuthenticationException(sprintf(
'Invalid password for zip entry "%s"',
$entry->getName()
));
}
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;
$crc = PHP_INT_SIZE === 4 ? sprintf('%u', $entry->getCrc()) : $entry->getCrc();
if ($crc != $localCrc) {
if ($isEncrypted) {
throw new ZipCryptoException("Wrong password");
throw new ZipAuthenticationException(sprintf(
'Invalid password for zip entry "%s"',
$entry->getName()
));
}
throw new Crc32Exception($entry->getName(), $crc, $localCrc);
}
@@ -498,7 +548,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()]);

View File

@@ -2,6 +2,7 @@
namespace PhpZip\Stream;
use PhpZip\Exception\ZipException;
use PhpZip\Model\ZipEntry;
use PhpZip\Model\ZipModel;
@@ -26,6 +27,7 @@ interface ZipInputStreamInterface
/**
* @param ZipEntry $entry
* @return string
* @throws ZipException
*/
public function readEntryContent(ZipEntry $entry);

View File

@@ -562,7 +562,7 @@ class ZipFile implements ZipFileInterface
throw new InvalidArgumentException('The input directory is not specified');
}
if (!is_dir($inputDir)) {
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
}
$inputDir = rtrim($inputDir, '/\\') . DIRECTORY_SEPARATOR;
@@ -594,7 +594,7 @@ class ZipFile implements ZipFileInterface
throw new InvalidArgumentException('The input directory is not specified');
}
if (!is_dir($inputDir)) {
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
}
$inputDir = rtrim($inputDir, '/\\') . DIRECTORY_SEPARATOR;
@@ -713,7 +713,7 @@ class ZipFile implements ZipFileInterface
throw new InvalidArgumentException('The input directory is not specified');
}
if (!is_dir($inputDir)) {
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
}
$globPattern = (string)$globPattern;
if (empty($globPattern)) {
@@ -813,7 +813,7 @@ class ZipFile implements ZipFileInterface
throw new InvalidArgumentException('The input directory is not specified');
}
if (!is_dir($inputDir)) {
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
throw new InvalidArgumentException(sprintf('The "%s" directory does not exist.', $inputDir));
}
$inputDir = rtrim($inputDir, '/\\') . DIRECTORY_SEPARATOR;

View File

@@ -0,0 +1,104 @@
<?php
namespace PhpZip;
use PhpZip\Exception\ZipException;
use PhpZip\Util\CryptoUtil;
class Issue24Test extends ZipTestCase
{
/**
* This method is called before the first test of this test class is run.
*/
public static function setUpBeforeClass()
{
stream_wrapper_register("dummyfs", DummyFileSystemStream::class);
}
/**
* @throws ZipException
*/
public function testDummyFS()
{
$fileContents = str_repeat(base64_encode(CryptoUtil::randomBytes(12000)), 100);
// create zip file
$zip = new ZipFile();
$zip->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;
public 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;
}
public 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;
}
public function stream_tell()
{
// echo "DummyFileSystemStream->stream_tell()" . PHP_EOL;
return ftell($this->fp);
}
public function stream_eof()
{
// echo "DummyFileSystemStream->stream_eof()" . PHP_EOL;
$isfeof = feof($this->fp);
return $isfeof;
}
public function stream_seek($offset, $whence)
{
// echo "DummyFileSystemStream->stream_seek($offset, $whence)" . PHP_EOL;
fseek($this->fp, $offset, $whence);
}
public function stream_stat()
{
// echo "DummyFileSystemStream->stream_stat()" . PHP_EOL;
return fstat($this->fp);
}
}

View File

@@ -101,7 +101,7 @@ class PhpZipExtResourceTest extends ZipTestCase
* Bug #70752 (Depacking with wrong password leaves 0 length files)
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/bug70752.phpt
* @expectedException \PhpZip\Exception\ZipAuthenticationException
* @expectedExceptionMessage Bad password for entry bug70752.txt
* @expectedExceptionMessage nvalid password for zip entry "bug70752.txt"
* @throws ZipException
*/
public function testBug70752()

View File

@@ -15,7 +15,7 @@ class ZipAlignTest extends ZipTestCase
*/
public function testApkAlignedAndSetZipAlignAndReSave()
{
$filename = __DIR__ . '/resources/test.apk';
$filename = __DIR__ . '/resources/apk.zip';
$this->assertCorrectZipArchive($filename);
$result = $this->assertVerifyZipAlign($filename);

View File

@@ -22,7 +22,7 @@ class ZipEventTest extends ZipTestCase
public function testBeforeSave()
{
$zipFile = new ZipFileExtended();
$zipFile->openFile(__DIR__ . '/resources/test.apk');
$zipFile->openFile(__DIR__ . '/resources/apk.zip');
$this->assertTrue(isset($zipFile['META-INF/MANIFEST.MF']));
$this->assertTrue(isset($zipFile['META-INF/CERT.SF']));
$this->assertTrue(isset($zipFile['META-INF/CERT.RSA']));

View File

@@ -43,7 +43,7 @@ class ZipPasswordTest extends ZipFileAddDirTest
$zipFile[$entryName];
$this->fail("Expected Exception has not been raised.");
} catch (ZipAuthenticationException $ae) {
$this->assertContains('Bad password for entry', $ae->getMessage());
$this->assertContains('Invalid password for zip entry', $ae->getMessage());
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace PhpZip;
use PhpZip\Exception\ZipException;
use PhpZip\Util\Iterator\IgnoreFilesFilterIterator;
use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
/**
* Test add remote files to zip archive
*/
class ZipRemoteFileTest extends ZipTestCase
{
protected function setUp()
{
parent::setUp();
}
/**
* @throws ZipException
*/
public function testAddRemoteFileFromStream()
{
$zipFile = new ZipFile();
$outputZip = $this->outputFilename;
$fileUrl = 'https://raw.githubusercontent.com/Ne-Lexa/php-zip/master/README.md';
$fp = @fopen($fileUrl, 'rb', false, stream_context_create([
'http' => [
'timeout' => 3,
]
]));
if ($fp === false) {
self::markTestSkipped(sprintf(
"Could not fetch remote file: %s",
$fileUrl
));
return;
}
$fileName = 'remote-file-from-http-stream.md';
$zipFile->addFromStream($fp, $fileName);
$zipFile->saveAsFile($outputZip);
$zipFile->close();
$zipFile = new ZipFile();
$zipFile->openFile($outputZip);
$files = $zipFile->getListFiles();
self::assertCount(1, $files);
self::assertSame($fileName, $files[0]);
$zipFile->close();
}
}

View File

@@ -10,7 +10,6 @@ use PhpZip\Util\FilesUtil;
*/
class ZipTestCase extends \PHPUnit_Framework_TestCase
{
/**
* @var string
*/
@@ -27,12 +26,10 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
{
parent::setUp();
$id = uniqid('phpzip');
$id = uniqid('phpzip', true);
$tempDir = sys_get_temp_dir() . '/phpunit-phpzip';
if (!is_dir($tempDir)) {
if (!mkdir($tempDir, 0755, true)) {
throw new \RuntimeException("Dir " . $tempDir . " can't created");
}
if (!is_dir($tempDir) && !mkdir($tempDir, 0755, true) && !is_dir($tempDir)) {
throw new \RuntimeException('Dir ' . $tempDir . " can't created");
}
$this->outputFilename = $tempDir . '/' . $id . '.zip';
$this->outputDirname = $tempDir . '/' . $id;
@@ -61,23 +58,23 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
*/
public static function assertCorrectZipArchive($filename, $password = null)
{
if (DIRECTORY_SEPARATOR !== '\\' && `which unzip`) {
$command = "unzip";
if (self::existsProgram('unzip')) {
$command = 'unzip';
if ($password !== null) {
$command .= " -P " . escapeshellarg($password);
$command .= ' -P ' . escapeshellarg($password);
}
$command .= " -t " . escapeshellarg($filename);
$command .= ' -t ' . escapeshellarg($filename);
exec($command, $output, $returnCode);
$output = implode(PHP_EOL, $output);
if ($password !== null && $returnCode === 81) {
if (`which 7z`) {
if (self::existsProgram('7z')) {
// WinZip 99-character limit
// @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
$password = substr($password, 0, 99);
$command = "7z t -p" . escapeshellarg($password) . " " . escapeshellarg($filename);
$command = '7z t -p' . escapeshellarg($password) . ' ' . escapeshellarg($filename);
exec($command, $output, $returnCode);
/**
* @var array $output
@@ -100,6 +97,19 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
}
}
/**
* @param string $program
* @return bool
*/
private static function existsProgram($program){
if (DIRECTORY_SEPARATOR !== '\\') {
exec('which ' . escapeshellarg($program), $output, $returnCode);
return $returnCode === 0;
}
// false for Windows
return false;
}
/**
* Assert correct empty zip archive.
*
@@ -107,15 +117,15 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
*/
public static function assertCorrectEmptyZip($filename)
{
if (DIRECTORY_SEPARATOR !== '\\' && `which zipinfo`) {
exec("zipinfo " . escapeshellarg($filename), $output, $returnCode);
if (self::existsProgram('zipinfo')) {
exec('zipinfo ' . escapeshellarg($filename), $output, $returnCode);
$output = implode(PHP_EOL, $output);
self::assertContains('Empty zipfile', $output);
}
$actualEmptyZipData = pack('VVVVVv', EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG, 0, 0, 0, 0, 0);
self::assertEquals(file_get_contents($filename), $actualEmptyZipData);
self::assertStringEqualsFile($filename, $actualEmptyZipData);
}
/**
@@ -125,15 +135,15 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
*/
public static function assertVerifyZipAlign($filename, $showErrors = false)
{
if (DIRECTORY_SEPARATOR !== '\\' && `which zipalign`) {
exec("zipalign -c -v 4 " . escapeshellarg($filename), $output, $returnCode);
if (self::existsProgram('zipalign')) {
exec('zipalign -c -v 4 ' . escapeshellarg($filename), $output, $returnCode);
if ($showErrors && $returnCode !== 0) {
fwrite(STDERR, implode(PHP_EOL, $output));
}
return $returnCode === 0;
} else {
fwrite(STDERR, 'Can not find program "zipalign" for test' . PHP_EOL);
return null;
}
fwrite(STDERR, 'Can not find program "zipalign" for test' . PHP_EOL);
return null;
}
}

Binary file not shown.

Binary file not shown.