Skip to content

Commit 7d73ac4

Browse files
committed
Merge branch 'hotfix/3.1.2'
2 parents d67fc4d + 4979829 commit 7d73ac4

13 files changed

+381
-102
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
# 3.1.2 (2017-11-17)
4+
- Changed the algorithm for adding paddings to zipalign.
5+
Now we will use the special field ExtraField c ID 0xD935,
6+
which was implemented by Google in the apksigner library.
7+
Now this field corresponds to the ZIP standard for storing
8+
ExtraField records, and not just filling with zero bytes,
9+
as in the zipalign console utility.
10+
11+
## 3.1.1 (2017-11-15)
12+
- Fix resave zip aligned archive
13+
314
## 3.1.0 (2017-11-14)
415
- Added class `ZipModel` for all changes.
516
- All manipulations with incoming and outgoing streams are in separate files: `ZipInputStream` and `ZipOutputStream`.

README.RU.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@
5151
- Поддержка `ZIP64` (размер файла более 4 GB или количество записей в архиве более 65535).
5252
- Встроенная поддержка выравнивания архива для оптимизации Android пакетов (APK) [`zipalign`](https://developer.android.com/studio/command-line/zipalign.html).
5353
- Работа с паролями для PHP 5.5
54-
> **Внимание!**
55-
>
56-
> Для 32-bit систем, в данный момент не поддерживается метод шифрование `Traditional PKWARE Encryption (ZipCrypto)`.
57-
> Используйте метод шифрования `WinZIP AES Encryption`, когда это возможно.
54+
> **Внимание!**
55+
>
56+
> Для 32-bit систем, в данный момент не поддерживается метод шифрование `Traditional PKWARE Encryption (ZipCrypto)`.
57+
> Используйте метод шифрования `WinZIP AES Encryption`, когда это возможно.
5858
+ Установка пароля для чтения архива глобально или для некоторых записей.
5959
+ Изменение пароля архива, в том числе и для отдельных записей.
6060
+ Удаление пароля архива глобально или для отдельных записей.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ Table of contents
5151
- Support for `ZIP64` (file size is more than 4 GB or the number of entries in the archive is more than 65535).
5252
- Built-in support for aligning the archive to optimize Android packages (APK) [`zipalign`](https://developer.android.com/studio/command-line/zipalign.html).
5353
- Working with passwords for PHP 5.5
54-
> **Attention!**
55-
>
56-
> For 32-bit systems, the `Traditional PKWARE Encryption (ZipCrypto)` encryption method is not currently supported.
57-
> Use the encryption method `WinZIP AES Encryption`, whenever possible.
54+
> **Attention!**
55+
>
56+
> For 32-bit systems, the `Traditional PKWARE Encryption (ZipCrypto)` encryption method is not currently supported.
57+
> Use the encryption method `WinZIP AES Encryption`, whenever possible.
5858
+ Set the password to read the archive for all entries or only for some.
5959
+ Change the password for the archive, including for individual entries.
6060
+ Delete the archive password for all or individual entries.

src/PhpZip/Extra/ExtraFieldsFactory.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
namespace PhpZip\Extra;
44

55
use PhpZip\Exception\ZipException;
6+
use PhpZip\Extra\Fields\ApkAlignmentExtraField;
67
use PhpZip\Extra\Fields\DefaultExtraField;
8+
use PhpZip\Extra\Fields\JarMarkerExtraField;
79
use PhpZip\Extra\Fields\NtfsExtraField;
810
use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
911
use PhpZip\Extra\Fields\Zip64ExtraField;
1012
use PhpZip\Model\ZipEntry;
13+
use PhpZip\Util\StringUtil;
1114

1215
/**
1316
* Extra Fields Factory
@@ -26,6 +29,56 @@ private function __construct()
2629
{
2730
}
2831

32+
/**
33+
* @param string $extra
34+
* @param ZipEntry|null $entry
35+
* @return ExtraFieldsCollection
36+
* @throws ZipException
37+
*/
38+
public static function createExtraFieldCollections($extra, ZipEntry $entry = null)
39+
{
40+
$extraFieldsCollection = new ExtraFieldsCollection();
41+
if (null !== $extra) {
42+
$extraLength = strlen($extra);
43+
if ($extraLength > 0xffff) {
44+
throw new ZipException("Extra Fields too large: " . $extraLength);
45+
}
46+
$pos = 0;
47+
$endPos = $extraLength;
48+
49+
while ($endPos - $pos >= 4) {
50+
$unpack = unpack('vheaderId/vdataSize', substr($extra, $pos, 4));
51+
$pos += 4;
52+
$headerId = (int)$unpack['headerId'];
53+
$dataSize = (int)$unpack['dataSize'];
54+
$extraField = ExtraFieldsFactory::create($headerId);
55+
if ($extraField instanceof Zip64ExtraField && $entry !== null) {
56+
$extraField->setEntry($entry);
57+
}
58+
$extraField->deserialize(substr($extra, $pos, $dataSize));
59+
$pos += $dataSize;
60+
$extraFieldsCollection[$headerId] = $extraField;
61+
}
62+
}
63+
return $extraFieldsCollection;
64+
}
65+
66+
public static function createSerializedData(ExtraFieldsCollection $extraFieldsCollection)
67+
{
68+
$extraData = '';
69+
foreach ($extraFieldsCollection as $extraField) {
70+
$data = $extraField->serialize();
71+
$extraData .= pack('vv', $extraField::getHeaderId(), strlen($data));
72+
$extraData .= $data;
73+
}
74+
75+
$size = strlen($extraData);
76+
if (0x0000 > $size || $size > 0xffff) {
77+
throw new ZipException('Size extra out of range: ' . $size . '. Extra data: ' . $extraData);
78+
}
79+
return $extraData;
80+
}
81+
2982
/**
3083
* A static factory method which creates a new Extra Field based on the
3184
* given Header ID.
@@ -69,6 +122,8 @@ protected static function getRegistry()
69122
self::$registry[WinZipAesEntryExtraField::getHeaderId()] = WinZipAesEntryExtraField::class;
70123
self::$registry[NtfsExtraField::getHeaderId()] = NtfsExtraField::class;
71124
self::$registry[Zip64ExtraField::getHeaderId()] = Zip64ExtraField::class;
125+
self::$registry[ApkAlignmentExtraField::getHeaderId()] = ApkAlignmentExtraField::class;
126+
self::$registry[JarMarkerExtraField::getHeaderId()] = JarMarkerExtraField::class;
72127
}
73128
return self::$registry;
74129
}
@@ -97,4 +152,22 @@ public static function createZip64Extra(ZipEntry $entry)
97152
{
98153
return new Zip64ExtraField($entry);
99154
}
155+
156+
/**
157+
* @param ZipEntry $entry
158+
* @param int $padding
159+
* @return ApkAlignmentExtraField
160+
*/
161+
public static function createApkAlignExtra(ZipEntry $entry, $padding)
162+
{
163+
$padding = (int)$padding;
164+
$multiple = 4;
165+
if (StringUtil::endsWith($entry->getName(), '.so')) {
166+
$multiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
167+
}
168+
$extraField = new ApkAlignmentExtraField();
169+
$extraField->setMultiple($multiple);
170+
$extraField->setPadding($padding);
171+
return $extraField;
172+
}
100173
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
namespace PhpZip\Extra\Fields;
4+
5+
use PhpZip\Exception\InvalidArgumentException;
6+
use PhpZip\Extra\ExtraField;
7+
8+
/**
9+
* Apk Alignment Extra Field
10+
*
11+
* @author Ne-Lexa alexey@nelexa.ru
12+
* @license MIT
13+
*/
14+
class ApkAlignmentExtraField implements ExtraField
15+
{
16+
/**
17+
* Minimum size (in bytes) of the extensible data block/field used
18+
* for alignment of uncompressed entries.
19+
*/
20+
const ALIGNMENT_ZIP_EXTRA_MIN_SIZE_BYTES = 6;
21+
22+
const ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096;
23+
24+
/**
25+
* @var int
26+
*/
27+
private $multiple;
28+
/**
29+
* @var int
30+
*/
31+
private $padding;
32+
33+
/**
34+
* Returns the Header ID (type) of this Extra Field.
35+
* The Header ID is an unsigned short integer (two bytes)
36+
* which must be constant during the life cycle of this object.
37+
*
38+
* @return int
39+
*/
40+
public static function getHeaderId()
41+
{
42+
return 0xD935;
43+
}
44+
45+
/**
46+
* Serializes a Data Block.
47+
* @return string
48+
*/
49+
public function serialize()
50+
{
51+
if ($this->padding > 0) {
52+
$args = array_merge(
53+
['vc*', $this->multiple],
54+
array_fill(2, $this->padding, 0)
55+
);
56+
return call_user_func_array('pack', $args);
57+
}
58+
return pack('v', $this->multiple);
59+
}
60+
61+
/**
62+
* Initializes this Extra Field by deserializing a Data Block.
63+
* @param string $data
64+
* @throws InvalidArgumentException
65+
*/
66+
public function deserialize($data)
67+
{
68+
$length = strlen($data);
69+
if ($length < 2) {
70+
// This is APK alignment field.
71+
// FORMAT:
72+
// * uint16 alignment multiple (in bytes)
73+
// * remaining bytes -- padding to achieve alignment of data which starts after
74+
// the extra field
75+
throw new InvalidArgumentException("Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.");
76+
}
77+
$this->multiple = unpack('v', $data)[1];
78+
$this->padding = $length - 2;
79+
}
80+
81+
/**
82+
* @return mixed
83+
*/
84+
public function getMultiple()
85+
{
86+
return $this->multiple;
87+
}
88+
89+
/**
90+
* @return int
91+
*/
92+
public function getPadding()
93+
{
94+
return $this->padding;
95+
}
96+
97+
/**
98+
* @param int $multiple
99+
*/
100+
public function setMultiple($multiple)
101+
{
102+
$this->multiple = $multiple;
103+
}
104+
105+
/**
106+
* @param int $padding
107+
*/
108+
public function setPadding($padding)
109+
{
110+
$this->padding = $padding;
111+
}
112+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace PhpZip\Extra\Fields;
4+
5+
use PhpZip\Exception\ZipException;
6+
use PhpZip\Extra\ExtraField;
7+
8+
/**
9+
* Jar Marker Extra Field
10+
* An executable Java program can be packaged in a JAR file with all the libraries it uses.
11+
* Executable JAR files can easily be distinguished from the files packed in the JAR file
12+
* by the extra field in the first file, which is hexadecimal in the 0xCAFE bytes series.
13+
*
14+
* @author Ne-Lexa alexey@nelexa.ru
15+
* @license MIT
16+
*/
17+
class JarMarkerExtraField implements ExtraField
18+
{
19+
/**
20+
* Returns the Header ID (type) of this Extra Field.
21+
* The Header ID is an unsigned short integer (two bytes)
22+
* which must be constant during the life cycle of this object.
23+
*
24+
* @return int
25+
*/
26+
public static function getHeaderId()
27+
{
28+
return 0xCAFE;
29+
}
30+
31+
/**
32+
* Serializes a Data Block.
33+
* @return string
34+
*/
35+
public function serialize()
36+
{
37+
return '';
38+
}
39+
40+
/**
41+
* Initializes this Extra Field by deserializing a Data Block.
42+
* @param string $data
43+
* @throws ZipException
44+
*/
45+
public function deserialize($data)
46+
{
47+
if (0 !== strlen($data)) {
48+
throw new ZipException("JarMarker doesn't expect any data");
49+
}
50+
}
51+
}

src/PhpZip/Model/Entry/ZipAbstractEntry.php

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use PhpZip\Extra\ExtraFieldsCollection;
88
use PhpZip\Extra\ExtraFieldsFactory;
99
use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
10-
use PhpZip\Extra\Fields\Zip64ExtraField;
1110
use PhpZip\Model\ZipEntry;
1211
use PhpZip\Util\DateTimeConverter;
1312
use PhpZip\Util\StringUtil;
@@ -585,18 +584,7 @@ public function &getExtraFieldsCollection()
585584
*/
586585
public function getExtra()
587586
{
588-
$extraData = '';
589-
foreach ($this->getExtraFieldsCollection() as $extraField) {
590-
$data = $extraField->serialize();
591-
$extraData .= pack('vv', $extraField::getHeaderId(), strlen($data));
592-
$extraData .= $data;
593-
}
594-
595-
$size = strlen($extraData);
596-
if (0x0000 > $size || $size > 0xffff) {
597-
throw new ZipException('Size extra out of range: ' . $size . '. Extra data: ' . $extraData);
598-
}
599-
return $extraData;
587+
return ExtraFieldsFactory::createSerializedData($this->extraFieldsCollection);
600588
}
601589

602590
/**
@@ -612,28 +600,7 @@ public function getExtra()
612600
*/
613601
public function setExtra($data)
614602
{
615-
$this->extraFieldsCollection = new ExtraFieldsCollection();
616-
if (null !== $data) {
617-
$extraLength = strlen($data);
618-
if (0x0000 > $extraLength || $extraLength > 0xffff) {
619-
throw new ZipException("Extra Fields too large: " . $extraLength);
620-
}
621-
$pos = 0;
622-
$endPos = $extraLength;
623-
while ($pos < $endPos) {
624-
$unpack = unpack('vheaderId/vdataSize', substr($data, $pos, 4));
625-
$pos += 4;
626-
$headerId = (int)$unpack['headerId'];
627-
$dataSize = (int)$unpack['dataSize'];
628-
$extraField = ExtraFieldsFactory::create($headerId);
629-
if ($extraField instanceof Zip64ExtraField) {
630-
$extraField->setEntry($this);
631-
}
632-
$extraField->deserialize(substr($data, $pos, $dataSize));
633-
$pos += $dataSize;
634-
$this->extraFieldsCollection[$headerId] = $extraField;
635-
}
636-
}
603+
$this->extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($data, $this);
637604
}
638605

639606
/**

src/PhpZip/Model/Entry/ZipNewEntry.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ public function getVersionNeededToExtract()
6262
$method = $this->getMethod();
6363
return self::METHOD_WINZIP_AES === $method ? 51 :
6464
(
65-
ZipFileInterface::METHOD_BZIP2 === $method ? 46 :
65+
ZipFileInterface::METHOD_BZIP2 === $method ? 46 :
6666
(
67-
$this->isZip64ExtensionsRequired() ? 45 :
67+
$this->isZip64ExtensionsRequired() ? 45 :
6868
(ZipFileInterface::METHOD_DEFLATED === $method || $this->isDirectory() ? 20 : 10)
6969
)
7070
);

src/PhpZip/Model/ZipEntry.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace PhpZip\Model;
44

55
use PhpZip\Exception\ZipException;
6-
76
use PhpZip\Extra\ExtraFieldsCollection;
87
use PhpZip\ZipFileInterface;
98

0 commit comments

Comments
 (0)