Skip to content

Commit b817449

Browse files
authored
Only enable Zip64 if it is required to maximize compatibility (#269)
1 parent ab4a596 commit b817449

File tree

3 files changed

+130
-20
lines changed

3 files changed

+130
-20
lines changed

src/File.php

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function __construct(
6666
$this->generalPurposeBitFlag |= GeneralPurposeBitFlag::ZERO_HEADER;
6767
}
6868

69-
$this->selectVersion();
69+
$this->version = $this->compressionMethod === CompressionMethod::DEFLATE ? Version::DEFLATE : Version::STORE;
7070
}
7171

7272
public function cloneSimulationExecution(): self
@@ -178,25 +178,32 @@ private function forecastSize(): ?int
178178
*/
179179
private function addFileHeader(): void
180180
{
181-
$footer = $this->buildZip64ExtraBlock($this->enableZeroHeader && $this->enableZip64);
181+
$forceEnableZip64 = $this->enableZeroHeader && $this->enableZip64;
182+
183+
$footer = $this->buildZip64ExtraBlock($forceEnableZip64);
184+
185+
$zip64Enabled = $footer !== '';
186+
187+
if($zip64Enabled) {
188+
$this->version = Version::ZIP64;
189+
}
182190

183191
if ($this->generalPurposeBitFlag & GeneralPurposeBitFlag::EFS) {
184192
// Put the tricky entry to
185193
// force Linux unzip to lookup EFS flag.
186194
$footer .= Zs\ExtendedInformationExtraField::generate();
187195
}
188196

189-
190197
$data = LocalFileHeader::generate(
191198
versionNeededToExtract: $this->version->value,
192199
generalPurposeBitFlag: $this->generalPurposeBitFlag,
193200
compressionMethod: $this->compressionMethod,
194201
lastModificationDateTime: $this->lastModificationDateTime,
195202
crc32UncompressedData: $this->crc,
196-
compressedSize: ($this->enableZip64 || $this->enableZeroHeader || $this->compressedSize > 0xFFFFFFFF)
203+
compressedSize: $zip64Enabled
197204
? 0xFFFFFFFF
198205
: $this->compressedSize,
199-
uncompressedSize: ($this->enableZip64 || $this->enableZeroHeader || $this->uncompressedSize > 0xFFFFFFFF)
206+
uncompressedSize: $zip64Enabled
200207
? 0xFFFFFFFF
201208
: $this->uncompressedSize,
202209
fileName: $this->fileName,
@@ -235,20 +242,6 @@ private function checkEncoding(): void
235242
}
236243
}
237244

238-
private function selectVersion(): void
239-
{
240-
if ($this->enableZip64) {
241-
$this->version = Version::ZIP64;
242-
return;
243-
}
244-
if ($this->compressionMethod === CompressionMethod::DEFLATE) {
245-
$this->version = Version::DEFLATE;
246-
return;
247-
}
248-
249-
$this->version = Version::STORE;
250-
}
251-
252245
private function buildZip64ExtraBlock(bool $force = false): string
253246
{
254247
$outputZip64ExtraBlock = false;
@@ -278,7 +271,7 @@ private function buildZip64ExtraBlock(bool $force = false): string
278271
return '';
279272
}
280273

281-
if ($this->version !== Version::ZIP64) {
274+
if (!$this->enableZip64) {
282275
throw new OverflowException();
283276
}
284277

test/Assertions.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ZipStream\Test;
6+
7+
trait Assertions
8+
{
9+
protected function assertFileContains(string $filePath, string $needle): void
10+
{
11+
$last = '';
12+
13+
$handle = fopen($filePath, 'r');
14+
while (!feof($handle)) {
15+
$line = fgets($handle, 1024);
16+
17+
if(str_contains($last . $line, $needle)) {
18+
fclose($handle);
19+
return;
20+
}
21+
22+
$last = $line;
23+
}
24+
25+
fclose($handle);
26+
27+
$this->fail("File {$filePath} must contain {$needle}");
28+
}
29+
30+
protected function assertFileDoesNotContain(string $filePath, string $needle): void
31+
{
32+
$last = '';
33+
34+
$handle = fopen($filePath, 'r');
35+
while (!feof($handle)) {
36+
$line = fgets($handle, 1024);
37+
38+
if(str_contains($last . $line, $needle)) {
39+
fclose($handle);
40+
41+
$this->fail("File {$filePath} must not contain {$needle}");
42+
}
43+
44+
$last = $line;
45+
}
46+
47+
fclose($handle);
48+
}
49+
}

test/ZipStreamTest.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
use ZipStream\Exception\StreamNotReadableException;
2323
use ZipStream\Exception\StreamNotSeekableException;
2424
use ZipStream\OperationMode;
25+
use ZipStream\PackField;
2526
use ZipStream\ZipStream;
2627

2728
class ZipStreamTest extends TestCase
2829
{
2930
use Util;
31+
use Assertions;
3032

3133
public function testAddFile(): void
3234
{
@@ -638,6 +640,72 @@ public function testAddLargeFileWithoutZip64WithZeroHeader(): void
638640
);
639641
}
640642

643+
/**
644+
* @group slow
645+
*/
646+
public function testAddsZip64HeaderWhenNeeded(): void
647+
{
648+
[$tmp, $stream] = $this->getTmpFileStream();
649+
650+
$zip = new ZipStream(
651+
outputStream: $stream,
652+
sendHttpHeaders: false,
653+
enableZip64: true,
654+
defaultEnableZeroHeader: false,
655+
);
656+
657+
$zip->addFileFromPsr7Stream(
658+
fileName: 'sample.json',
659+
stream: new EndlessCycleStream('0'),
660+
maxSize: 0x100000000,
661+
compressionMethod: CompressionMethod::STORE,
662+
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
663+
);
664+
665+
$zip->finish();
666+
667+
$tmpDir = $this->validateAndExtractZip($tmp);
668+
$files = $this->getRecursiveFileList($tmpDir);
669+
670+
$this->assertSame(['sample.json'], $files);
671+
$this->assertFileContains($tmp, PackField::pack(
672+
new PackField(format: 'V', value: 0x06064b50)
673+
));
674+
}
675+
676+
/**
677+
* @group slow
678+
*/
679+
public function testDoesNotAddZip64HeaderWhenNotNeeded(): void
680+
{
681+
[$tmp, $stream] = $this->getTmpFileStream();
682+
683+
$zip = new ZipStream(
684+
outputStream: $stream,
685+
sendHttpHeaders: false,
686+
enableZip64: true,
687+
defaultEnableZeroHeader: false,
688+
);
689+
690+
$zip->addFileFromPsr7Stream(
691+
fileName: 'sample.json',
692+
stream: new EndlessCycleStream('0'),
693+
maxSize: 0x10,
694+
compressionMethod: CompressionMethod::STORE,
695+
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
696+
);
697+
698+
$zip->finish();
699+
700+
$tmpDir = $this->validateAndExtractZip($tmp);
701+
$files = $this->getRecursiveFileList($tmpDir);
702+
703+
$this->assertSame(['sample.json'], $files);
704+
$this->assertFileDoesNotContain($tmp, PackField::pack(
705+
new PackField(format: 'V', value: 0x06064b50)
706+
));
707+
}
708+
641709
/**
642710
* @group slow
643711
*/

0 commit comments

Comments
 (0)