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:
@@ -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();
|
||||||
|
@@ -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"
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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).
|
||||||
*/
|
*/
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user