ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
ZipStreamTest.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types=1);
3 
4 namespace ZipStreamTest;
5 
11 use ZipStream\Option\File as FileOptions;
14 
18 class ZipStreamTest extends TestCase
19 {
20  const OSX_ARCHIVE_UTILITY =
21  '/System/Library/CoreServices/Applications/Archive Utility.app/Contents/MacOS/Archive Utility';
22 
23  public function testFileNotFoundException(): void
24  {
25  $this->expectException(\ZipStream\Exception\FileNotFoundException::class);
26  // Get ZipStream Object
27  $zip = new ZipStream();
28 
29  // Trigger error by adding a file which doesn't exist
30  $zip->addFileFromPath('foobar.php', '/foo/bar/foobar.php');
31  }
32 
33  public function testFileNotReadableException(): void
34  {
35  // create new virtual filesystem
36  $root = vfsStream::setup('vfs');
37  // create a virtual file with no permissions
38  $file = vfsStream::newFile('foo.txt', 0000)->at($root)->setContent('bar');
39  $zip = new ZipStream();
40  $this->expectException(\ZipStream\Exception\FileNotReadableException::class);
41  $zip->addFileFromPath('foo.txt', $file->url());
42  }
43 
44  public function testDostime(): void
45  {
46  // Allows testing of protected method
47  $class = new \ReflectionClass(File::class);
48  $method = $class->getMethod('dostime');
49  $method->setAccessible(true);
50 
51  $this->assertSame($method->invoke(null, 1416246368), 1165069764);
52 
53  // January 1 1980 - DOS Epoch.
54  $this->assertSame($method->invoke(null, 315532800), 2162688);
55 
56  // January 1 1970 -> January 1 1980 due to minimum DOS Epoch. @todo Throw Exception?
57  $this->assertSame($method->invoke(null, 0), 2162688);
58  }
59 
60  public function testAddFile(): void
61  {
62  [$tmp, $stream] = $this->getTmpFileStream();
63 
64  $options = new ArchiveOptions();
65  $options->setOutputStream($stream);
66 
67  $zip = new ZipStream(null, $options);
68 
69  $zip->addFile('sample.txt', 'Sample String Data');
70  $zip->addFile('test/sample.txt', 'More Simple Sample Data');
71 
72  $zip->finish();
73  fclose($stream);
74 
75  $tmpDir = $this->validateAndExtractZip($tmp);
76 
77  $files = $this->getRecursiveFileList($tmpDir);
78  $this->assertEquals(['sample.txt', 'test/sample.txt'], $files);
79 
80  $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
81  $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
82  }
83 
87  protected function getTmpFileStream(): array
88  {
89  $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
90  $stream = fopen($tmp, 'wb+');
91 
92  return array($tmp, $stream);
93  }
94 
99  protected function validateAndExtractZip($tmp): string
100  {
101  $tmpDir = $this->getTmpDir();
102 
103  $zipArch = new \ZipArchive;
104  $res = $zipArch->open($tmp);
105 
106  if ($res !== true) {
107  $this->fail("Failed to open {$tmp}. Code: $res");
108 
109  return $tmpDir;
110  }
111 
112  $this->assertEquals(0, $zipArch->status);
113  $this->assertEquals(0, $zipArch->statusSys);
114 
115  $zipArch->extractTo($tmpDir);
116  $zipArch->close();
117 
118  return $tmpDir;
119  }
120 
121  protected function getTmpDir(): string
122  {
123  $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
124  unlink($tmp);
125  mkdir($tmp) or $this->fail('Failed to make directory');
126 
127  return $tmp;
128  }
129 
134  protected function getRecursiveFileList(string $path): array
135  {
136  $data = array();
137  $path = (string)realpath($path);
138  $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
139 
140  $pathLen = strlen($path);
141  foreach ($files as $file) {
142  $filePath = $file->getRealPath();
143  if (!is_dir($filePath)) {
144  $data[] = substr($filePath, $pathLen + 1);
145  }
146  }
147 
148  sort($data);
149 
150  return $data;
151  }
152 
153  public function testAddFileUtf8NameComment(): void
154  {
155  [$tmp, $stream] = $this->getTmpFileStream();
156 
157  $options = new ArchiveOptions();
158  $options->setOutputStream($stream);
159 
160  $zip = new ZipStream(null, $options);
161 
162  $name = 'árvíztűrő tükörfúrógép.txt';
163  $content = 'Sample String Data';
164  $comment =
165  'Filename has every special characters ' .
166  'from Hungarian language in lowercase. ' .
167  'In uppercase: ÁÍŰŐÜÖÚÓÉ';
168 
169  $fileOptions = new FileOptions();
170  $fileOptions->setComment($comment);
171 
172  $zip->addFile($name, $content, $fileOptions);
173  $zip->finish();
174  fclose($stream);
175 
176  $tmpDir = $this->validateAndExtractZip($tmp);
177 
178  $files = $this->getRecursiveFileList($tmpDir);
179  $this->assertEquals(array($name), $files);
180  $this->assertStringEqualsFile($tmpDir . '/' . $name, $content);
181 
182  $zipArch = new \ZipArchive();
183  $zipArch->open($tmp);
184  $this->assertEquals($comment, $zipArch->getCommentName($name));
185  }
186 
187  public function testAddFileUtf8NameNonUtfComment(): void
188  {
189  $this->expectException(\ZipStream\Exception\EncodingException::class);
190 
191  $stream = $this->getTmpFileStream()[1];
192 
193  $options = new ArchiveOptions();
194  $options->setOutputStream($stream);
195 
196  $zip = new ZipStream(null, $options);
197 
198  $name = 'á.txt';
199  $content = 'any';
200  $comment = 'á';
201 
202  $fileOptions = new FileOptions();
203  $fileOptions->setComment(mb_convert_encoding($comment, 'ISO-8859-2', 'UTF-8'));
204 
205  $zip->addFile($name, $content, $fileOptions);
206  }
207 
208  public function testAddFileNonUtf8NameUtfComment(): void
209  {
210  $this->expectException(\ZipStream\Exception\EncodingException::class);
211 
212  $stream = $this->getTmpFileStream()[1];
213 
214  $options = new ArchiveOptions();
215  $options->setOutputStream($stream);
216 
217  $zip = new ZipStream(null, $options);
218 
219  $name = 'á.txt';
220  $content = 'any';
221  $comment = 'á';
222 
223  $fileOptions = new FileOptions();
224  $fileOptions->setComment($comment);
225 
226  $zip->addFile(mb_convert_encoding($name, 'ISO-8859-2', 'UTF-8'), $content, $fileOptions);
227  }
228 
229  public function testAddFileWithStorageMethod(): void
230  {
231  [$tmp, $stream] = $this->getTmpFileStream();
232 
233  $options = new ArchiveOptions();
234  $options->setOutputStream($stream);
235 
236  $zip = new ZipStream(null, $options);
237 
238  $fileOptions = new FileOptions();
239  $fileOptions->setMethod(Method::STORE());
240 
241  $zip->addFile('sample.txt', 'Sample String Data', $fileOptions);
242  $zip->addFile('test/sample.txt', 'More Simple Sample Data');
243  $zip->finish();
244  fclose($stream);
245 
246  $zipArch = new \ZipArchive();
247  $zipArch->open($tmp);
248 
249  $sample1 = $zipArch->statName('sample.txt');
250  $sample12 = $zipArch->statName('test/sample.txt');
251  $this->assertEquals($sample1['comp_method'], Method::STORE);
252  $this->assertEquals($sample12['comp_method'], Method::DEFLATE);
253 
254  $zipArch->close();
255  }
256 
257  public function testDecompressFileWithMacUnarchiver(): void
258  {
259  if (!file_exists(self::OSX_ARCHIVE_UTILITY)) {
260  $this->markTestSkipped('The Mac OSX Archive Utility is not available.');
261  }
262 
263  [$tmp, $stream] = $this->getTmpFileStream();
264 
265  $options = new ArchiveOptions();
266  $options->setOutputStream($stream);
267 
268  $zip = new ZipStream(null, $options);
269 
270  $folder = uniqid('', true);
271 
272  $zip->addFile($folder . '/sample.txt', 'Sample Data');
273  $zip->finish();
274  fclose($stream);
275 
276  exec(escapeshellarg(self::OSX_ARCHIVE_UTILITY) . ' ' . escapeshellarg($tmp), $output, $returnStatus);
277 
278  $this->assertEquals(0, $returnStatus);
279  $this->assertCount(0, $output);
280 
281  $this->assertFileExists(dirname($tmp) . '/' . $folder . '/sample.txt');
282  $this->assertStringEqualsFile(dirname($tmp) . '/' . $folder . '/sample.txt', 'Sample Data');
283  }
284 
285  public function testAddFileFromPath(): void
286  {
287  [$tmp, $stream] = $this->getTmpFileStream();
288 
289  $options = new ArchiveOptions();
290  $options->setOutputStream($stream);
291 
292  $zip = new ZipStream(null, $options);
293 
294  [$tmpExample, $streamExample] = $this->getTmpFileStream();
295  fwrite($streamExample, 'Sample String Data');
296  fclose($streamExample);
297  $zip->addFileFromPath('sample.txt', $tmpExample);
298 
299  [$tmpExample, $streamExample] = $this->getTmpFileStream();
300  fwrite($streamExample, 'More Simple Sample Data');
301  fclose($streamExample);
302  $zip->addFileFromPath('test/sample.txt', $tmpExample);
303 
304  $zip->finish();
305  fclose($stream);
306 
307  $tmpDir = $this->validateAndExtractZip($tmp);
308 
309  $files = $this->getRecursiveFileList($tmpDir);
310  $this->assertEquals(array('sample.txt', 'test/sample.txt'), $files);
311 
312  $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
313  $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
314  }
315 
316  public function testAddFileFromPathWithStorageMethod(): void
317  {
318  [$tmp, $stream] = $this->getTmpFileStream();
319 
320  $options = new ArchiveOptions();
321  $options->setOutputStream($stream);
322 
323  $zip = new ZipStream(null, $options);
324 
325  $fileOptions = new FileOptions();
326  $fileOptions->setMethod(Method::STORE());
327 
328  [$tmpExample, $streamExample] = $this->getTmpFileStream();
329  fwrite($streamExample, 'Sample String Data');
330  fclose($streamExample);
331  $zip->addFileFromPath('sample.txt', $tmpExample, $fileOptions);
332 
333  [$tmpExample, $streamExample] = $this->getTmpFileStream();
334  fwrite($streamExample, 'More Simple Sample Data');
335  fclose($streamExample);
336  $zip->addFileFromPath('test/sample.txt', $tmpExample);
337 
338  $zip->finish();
339  fclose($stream);
340 
341  $zipArch = new \ZipArchive();
342  $zipArch->open($tmp);
343 
344  $sample1 = $zipArch->statName('sample.txt');
345  $this->assertEquals(Method::STORE, $sample1['comp_method']);
346 
347  $sample2 = $zipArch->statName('test/sample.txt');
348  $this->assertEquals(Method::DEFLATE, $sample2['comp_method']);
349 
350  $zipArch->close();
351  }
352 
353  public function testAddLargeFileFromPath(): void
354  {
355  $methods = [Method::DEFLATE(), Method::STORE()];
356  $falseTrue = [false, true];
357  foreach ($methods as $method) {
358  foreach ($falseTrue as $zeroHeader) {
359  foreach ($falseTrue as $zip64) {
360  if ($zeroHeader && $method->equals(Method::DEFLATE())) {
361  continue;
362  }
363  $this->addLargeFileFileFromPath($method, $zeroHeader, $zip64);
364  }
365  }
366  }
367  }
368 
369  protected function addLargeFileFileFromPath($method, $zeroHeader, $zip64): void
370  {
371  [$tmp, $stream] = $this->getTmpFileStream();
372 
373  $options = new ArchiveOptions();
374  $options->setOutputStream($stream);
375  $options->setLargeFileMethod($method);
376  $options->setLargeFileSize(5);
377  $options->setZeroHeader($zeroHeader);
378  $options->setEnableZip64($zip64);
379 
380  $zip = new ZipStream(null, $options);
381 
382  [$tmpExample, $streamExample] = $this->getTmpFileStream();
383  for ($i = 0; $i <= 10000; $i++) {
384  fwrite($streamExample, sha1((string)$i));
385  if ($i % 100 === 0) {
386  fwrite($streamExample, "\n");
387  }
388  }
389  fclose($streamExample);
390  $shaExample = sha1_file($tmpExample);
391  $zip->addFileFromPath('sample.txt', $tmpExample);
392  unlink($tmpExample);
393 
394  $zip->finish();
395  fclose($stream);
396 
397  $tmpDir = $this->validateAndExtractZip($tmp);
398 
399  $files = $this->getRecursiveFileList($tmpDir);
400  $this->assertEquals(array('sample.txt'), $files);
401 
402  $this->assertEquals(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$method}");
403  }
404 
405  public function testAddFileFromStream(): void
406  {
407  [$tmp, $stream] = $this->getTmpFileStream();
408 
409  $options = new ArchiveOptions();
410  $options->setOutputStream($stream);
411 
412  $zip = new ZipStream(null, $options);
413 
414  // In this test we can't use temporary stream to feed data
415  // because zlib.deflate filter gives empty string before PHP 7
416  // it works fine with file stream
417  $streamExample = fopen(__FILE__, 'rb');
418  $zip->addFileFromStream('sample.txt', $streamExample);
419 // fclose($streamExample);
420 
421  $fileOptions = new FileOptions();
422  $fileOptions->setMethod(Method::STORE());
423 
424  $streamExample2 = fopen('php://temp', 'wb+');
425  fwrite($streamExample2, 'More Simple Sample Data');
426  rewind($streamExample2); // move the pointer back to the beginning of file.
427  $zip->addFileFromStream('test/sample.txt', $streamExample2, $fileOptions);
428 // fclose($streamExample2);
429 
430  $zip->finish();
431  fclose($stream);
432 
433  $tmpDir = $this->validateAndExtractZip($tmp);
434 
435  $files = $this->getRecursiveFileList($tmpDir);
436  $this->assertEquals(array('sample.txt', 'test/sample.txt'), $files);
437 
438  $this->assertStringEqualsFile(__FILE__, file_get_contents($tmpDir . '/sample.txt'));
439  $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
440  }
441 
443  {
444  [$tmp, $stream] = $this->getTmpFileStream();
445 
446  $options = new ArchiveOptions();
447  $options->setOutputStream($stream);
448 
449  $zip = new ZipStream(null, $options);
450 
451  $fileOptions = new FileOptions();
452  $fileOptions->setMethod(Method::STORE());
453 
454  $streamExample = fopen('php://temp', 'wb+');
455  fwrite($streamExample, 'Sample String Data');
456  rewind($streamExample); // move the pointer back to the beginning of file.
457  $zip->addFileFromStream('sample.txt', $streamExample, $fileOptions);
458 // fclose($streamExample);
459 
460  $streamExample2 = fopen('php://temp', 'bw+');
461  fwrite($streamExample2, 'More Simple Sample Data');
462  rewind($streamExample2); // move the pointer back to the beginning of file.
463  $zip->addFileFromStream('test/sample.txt', $streamExample2);
464 // fclose($streamExample2);
465 
466  $zip->finish();
467  fclose($stream);
468 
469  $zipArch = new \ZipArchive();
470  $zipArch->open($tmp);
471 
472  $sample1 = $zipArch->statName('sample.txt');
473  $this->assertEquals(Method::STORE, $sample1['comp_method']);
474 
475  $sample2 = $zipArch->statName('test/sample.txt');
476  $this->assertEquals(Method::DEFLATE, $sample2['comp_method']);
477 
478  $zipArch->close();
479  }
480 
481  public function testAddFileFromPsr7Stream(): void
482  {
483  [$tmp, $stream] = $this->getTmpFileStream();
484 
485  $options = new ArchiveOptions();
486  $options->setOutputStream($stream);
487 
488  $zip = new ZipStream(null, $options);
489 
490  $body = 'Sample String Data';
491  $response = new Response(200, [], $body);
492 
493  $fileOptions = new FileOptions();
494  $fileOptions->setMethod(Method::STORE());
495 
496  $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
497  $zip->finish();
498  fclose($stream);
499 
500  $tmpDir = $this->validateAndExtractZip($tmp);
501 
502  $files = $this->getRecursiveFileList($tmpDir);
503  $this->assertEquals(array('sample.json'), $files);
504  $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
505  }
506 
508  {
509  [$tmp, $stream] = $this->getTmpFileStream();
510 
511  $options = new ArchiveOptions();
512  $options->setOutputStream($stream);
513 
514  $zip = new ZipStream(null, $options);
515 
516  $body = 'Sample String Data';
517  $fileSize = strlen($body);
518  // Add fake padding
519  $fakePadding = "\0\0\0\0\0\0";
520  $response = new Response(200, [], $body . $fakePadding);
521 
522  $fileOptions = new FileOptions();
523  $fileOptions->setMethod(Method::STORE());
524  $fileOptions->setSize($fileSize);
525  $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
526  $zip->finish();
527  fclose($stream);
528 
529  $tmpDir = $this->validateAndExtractZip($tmp);
530 
531  $files = $this->getRecursiveFileList($tmpDir);
532  $this->assertEquals(array('sample.json'), $files);
533  $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
534  }
535 
536  public function testCreateArchiveWithFlushOptionSet(): void
537  {
538  [$tmp, $stream] = $this->getTmpFileStream();
539 
540  $options = new ArchiveOptions();
541  $options->setOutputStream($stream);
542  $options->setFlushOutput(true);
543 
544  $zip = new ZipStream(null, $options);
545 
546  $zip->addFile('sample.txt', 'Sample String Data');
547  $zip->addFile('test/sample.txt', 'More Simple Sample Data');
548 
549  $zip->finish();
550  fclose($stream);
551 
552  $tmpDir = $this->validateAndExtractZip($tmp);
553 
554  $files = $this->getRecursiveFileList($tmpDir);
555  $this->assertEquals(['sample.txt', 'test/sample.txt'], $files);
556 
557  $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
558  $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
559  }
560 
562  {
563  // WORKAROUND (1/2): remove phpunit's output buffer in order to run test without any buffering
564  ob_end_flush();
565  $this->assertEquals(0, ob_get_level());
566 
567  [$tmp, $stream] = $this->getTmpFileStream();
568 
569  $options = new ArchiveOptions();
570  $options->setOutputStream($stream);
571  $options->setFlushOutput(true);
572 
573  $zip = new ZipStream(null, $options);
574 
575  $zip->addFile('sample.txt', 'Sample String Data');
576 
577  $zip->finish();
578  fclose($stream);
579 
580  $tmpDir = $this->validateAndExtractZip($tmp);
581  $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
582 
583  // WORKAROUND (2/2): add back output buffering so that PHPUnit doesn't complain that it is missing
584  ob_start();
585  }
586 }
Class Version .
Definition: Bigint.php:4
$path
Definition: aliased.php:25
$files
Definition: metarefresh.php:49
addLargeFileFileFromPath($method, $zeroHeader, $zip64)
$stream
PHP stream implementation.
getRecursiveFileList(string $path)
foreach($_POST as $key=> $value) $res
$tmpDir
Definition: fileserver.php:15
$sc Method
$root
Definition: sabredav.php:45
testCreateArchiveWithOutputBufferingOffAndFlushOptionSet()
$comment
Definition: buildRTE.php:83
$i
Definition: disco.tpl.php:19
$response
PSR-7 response implementation.
Definition: Response.php:10
$data
Definition: bench.php:6