1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-07-23 16:51:12 +02:00

Add support set zip alignment (alternative zipalign tool)

This commit is contained in:
Ne-Lexa
2016-09-26 16:56:06 +03:00
parent cf7f98b7c9
commit f0d90da75c
5 changed files with 110 additions and 12 deletions

View File

@@ -2,7 +2,7 @@
================ ================
`PhpZip` - is to create, update, opening and unpacking ZIP archives in pure PHP. `PhpZip` - is to create, update, opening and unpacking ZIP archives in pure PHP.
The library supports `ZIP64`, `Traditional PKWARE Encryption` and `WinZIP AES Encryption`. The library supports `ZIP64`, `zipalign`, `Traditional PKWARE Encryption` and `WinZIP AES Encryption`.
The library does not require extension `php-xml` and class `ZipArchive`. The library does not require extension `php-xml` and class `ZipArchive`.
@@ -415,6 +415,11 @@ while ($iterator->valid())
$iterator->next(); $iterator->next();
} }
``` ```
Set zip alignment (alternate program `zipalign`).
```php
// before save or output
$zipOutputFile->setAlign(4); // alternative cmd: zipalign -f -v 4 filename.zip
```
Close zip archive. Close zip archive.
```php ```php
$zipOutputFile->close(); $zipOutputFile->close();

View File

@@ -1,12 +1,13 @@
{ {
"name": "nelexa/zip", "name": "nelexa/zip",
"description": "Zip files CRUD. Open, create, update, extract and get info tool. Support read and write encrypted archives. Support ZIP64 ext. Alternative ZipArchive. It does not require php-zip extension.", "description": "Zip files CRUD. Open, create, update, extract and get info tool. Support read and write encrypted archives. Support ZIP64 ext and zip align. Alternative ZipArchive. It does not require php-zip extension.",
"type": "library", "type": "library",
"keywords": [ "keywords": [
"zip", "zip",
"archive", "archive",
"extract", "extract",
"winzip" "winzip",
"zipalign"
], ],
"require-dev": { "require-dev": {
"phpunit/phpunit": "4.8" "phpunit/phpunit": "4.8"

View File

@@ -103,6 +103,13 @@ class ZipOutputFile implements \Countable, \ArrayAccess, \Iterator, ZipConstants
*/ */
private $level = self::LEVEL_DEFAULT_COMPRESSION; private $level = self::LEVEL_DEFAULT_COMPRESSION;
/**
* ZipAlign setting
*
* @var int
*/
private $align;
/** /**
* ZipOutputFile constructor. * ZipOutputFile constructor.
* @param ZipFile|null $zipFile * @param ZipFile|null $zipFile
@@ -805,6 +812,18 @@ class ZipOutputFile implements \Countable, \ArrayAccess, \Iterator, ZipConstants
$this->level = $level; $this->level = $level;
} }
/**
* @param int|null $align
*/
public function setZipAlign($align = 4)
{
if ($align === null) {
$this->align = null;
return;
}
$this->align = (int)$align;
}
/** /**
* Save as file * Save as file
* *
@@ -999,6 +1018,10 @@ class ZipOutputFile implements \Countable, \ArrayAccess, \Iterator, ZipConstants
$offset = ftell($outputHandle); $offset = ftell($outputHandle);
// Commit changes.
$entry->setGeneralPurposeBitFlags($general);
$entry->setRawOffset($offset);
// Start changes. // Start changes.
// local file header signature 4 bytes (0x04034b50) // local file header signature 4 bytes (0x04034b50)
// version needed to extract 2 bytes // version needed to extract 2 bytes
@@ -1012,6 +1035,22 @@ class ZipOutputFile implements \Countable, \ArrayAccess, \Iterator, ZipConstants
// file name length 2 bytes // file name length 2 bytes
// extra field length 2 bytes // extra field length 2 bytes
$extra = $entry->getRawExtraFields(); $extra = $entry->getRawExtraFields();
// zip align
$padding = 0;
if ($this->align !== null && !$entry->isEncrypted() && $entry->getMethod() === ZipEntry::METHOD_STORED) {
$padding =
(
$this->align -
(
$offset +
self::LOCAL_FILE_HEADER_MIN_LEN +
strlen($entry->getName()) +
strlen($extra)
) % $this->align
) % $this->align;
}
fwrite($outputHandle, pack('VvvvVVVVvv', fwrite($outputHandle, pack('VvvvVVVVvv',
ZipConstants::LOCAL_FILE_HEADER_SIG, ZipConstants::LOCAL_FILE_HEADER_SIG,
$entry->getVersionNeededToExtract(), $entry->getVersionNeededToExtract(),
@@ -1022,15 +1061,16 @@ class ZipOutputFile implements \Countable, \ArrayAccess, \Iterator, ZipConstants
$dd ? 0 : (int)$entry->getRawCompressedSize(), $dd ? 0 : (int)$entry->getRawCompressedSize(),
$dd ? 0 : (int)$entry->getRawSize(), $dd ? 0 : (int)$entry->getRawSize(),
strlen($entry->getName()), strlen($entry->getName()),
strlen($extra) strlen($extra) + $padding
)); ));
// file name (variable size) // file name (variable size)
fwrite($outputHandle, $entry->getName()); fwrite($outputHandle, $entry->getName());
// extra field (variable size) // extra field (variable size)
fwrite($outputHandle, $extra); fwrite($outputHandle, $extra);
// Commit changes.
$entry->setGeneralPurposeBitFlags($general); if ($padding > 0) {
$entry->setRawOffset($offset); fwrite($outputHandle, str_repeat(chr(0), $padding));
}
fwrite($outputHandle, $entryContent); fwrite($outputHandle, $entryContent);
@@ -1059,7 +1099,6 @@ class ZipOutputFile implements \Countable, \ArrayAccess, \Iterator, ZipConstants
. " (expected compressed entry size of " . " (expected compressed entry size of "
. $entry->getCompressedSize() . " bytes, but is actually " . $compressedSize . " bytes)"); . $entry->getCompressedSize() . " bytes, but is actually " . $compressedSize . " bytes)");
} }
} }
/** /**
@@ -1321,13 +1360,12 @@ class ZipOutputFile implements \Countable, \ArrayAccess, \Iterator, ZipConstants
*/ */
public function offsetSet($entryName, $uncompressedDataContent) public function offsetSet($entryName, $uncompressedDataContent)
{ {
if(empty($entryName)){ if (empty($entryName)) {
throw new IllegalArgumentException('Entry name empty'); throw new IllegalArgumentException('Entry name empty');
} }
if($entryName[strlen($entryName)-1] === '/'){ if ($entryName[strlen($entryName) - 1] === '/') {
$this->addEmptyDir($entryName); $this->addEmptyDir($entryName);
} } else {
else{
$this->addFromString($entryName, $uncompressedDataContent); $this->addFromString($entryName, $uncompressedDataContent);
} }
} }

View File

@@ -1022,6 +1022,44 @@ class ZipTest extends ZipTestCase
$zipFile->close(); $zipFile->close();
} }
public function testZipAlign()
{
$zipOutputFile = ZipOutputFile::create();
for ($i = 0; $i < 100; $i++) {
$zipOutputFile->addFromString(
'entry' . $i . '.txt',
CryptoUtil::randomBytes(mt_rand(100, 4096)),
ZipEntry::METHOD_STORED
);
}
$zipOutputFile->saveAsFile($this->outputFilename);
$zipOutputFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$result = self::doZipAlignVerify($this->outputFilename);
if($result === null) return; // zip align not installed
// check not zip align
self::assertFalse($result, false);
$zipFile = ZipFile::openFromFile($this->outputFilename);
$zipOutputFile = ZipOutputFile::openFromZipFile($zipFile);
$zipOutputFile->setZipAlign(4);
$zipOutputFile->saveAsFile($this->outputFilename);
$zipOutputFile->close();
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$result = self::doZipAlignVerify($this->outputFilename);
self::assertNotNull($result);
// check zip align
self::assertTrue($result);
}
/** /**
* Test support ZIP64 ext (slow test - normal). * Test support ZIP64 ext (slow test - normal).
*/ */

View File

@@ -42,4 +42,20 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
self::assertEquals(file_get_contents($filename), $actualEmptyZipData); self::assertEquals(file_get_contents($filename), $actualEmptyZipData);
} }
/**
* @param string $filename
* @return bool|null If null - can not install zipalign
*/
public static function doZipAlignVerify($filename)
{
if (DIRECTORY_SEPARATOR !== '\\' && `which zipalign`) {
exec("zipalign -c -v 4 " . escapeshellarg($filename), $output, $returnCode);
return $returnCode === 0;
} else {
echo 'Can not find program "zipalign" for test' . PHP_EOL;
fwrite(STDERR, 'Can not find program "zipalign" for test');
return null;
}
}
} }