ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
ZipStream.php
Go to the documentation of this file.
1<?php
2declare(strict_types=1);
3
4namespace ZipStream;
5
8use ZipStream\Option\Archive as ArchiveOptions;
9use ZipStream\Option\File as FileOptions;
11
59{
83 const ZIP_VERSION_MADE_BY = 0x603;
84
90 const FILE_HEADER_SIGNATURE = 0x04034b50;
91 const CDR_FILE_SIGNATURE = 0x02014b50;
92 const CDR_EOF_SIGNATURE = 0x06054b50;
93 const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
94 const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50;
95 const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50;
96
102 public $opt;
103
107 public $files = [];
108
112 public $cdr_ofs;
113
117 public $ofs;
118
122 protected $need_headers;
123
127 protected $output_name;
128
171 public function __construct(?string $name = null, ?ArchiveOptions $opt = null)
172 {
173 $this->opt = $opt ?: new ArchiveOptions();
174
175 $this->output_name = $name;
176 $this->need_headers = $name && $this->opt->isSendHttpHeaders();
177
178 $this->cdr_ofs = new Bigint();
179 $this->ofs = new Bigint();
180 }
181
210 public function addFile(string $name, string $data, ?FileOptions $options = null): void
211 {
212 $options = $options ?: new FileOptions();
213 $options->defaultTo($this->opt);
214
215 $file = new File($this, $name, $options);
216 $file->processData($data);
217 }
218
256 public function addFileFromPath(string $name, string $path, ?FileOptions $options = null): void
257 {
258 $options = $options ?: new FileOptions();
259 $options->defaultTo($this->opt);
260
261 $file = new File($this, $name, $options);
262 $file->processPath($path);
263 }
264
290 public function addFileFromStream(string $name, $stream, ?FileOptions $options = null): void
291 {
292 $options = $options ?: new FileOptions();
293 $options->defaultTo($this->opt);
294
295 $file = new File($this, $name, $options);
296 $file->processStream(new DeflateStream($stream));
297 }
298
324 public function addFileFromPsr7Stream(
325 string $name,
327 ?FileOptions $options = null
328 ): void {
329 $options = $options ?: new FileOptions();
330 $options->defaultTo($this->opt);
331
332 $file = new File($this, $name, $options);
333 $file->processStream($stream);
334 }
335
354 public function finish(): void
355 {
356 // add trailing cdr file records
357 foreach ($this->files as $cdrFile) {
358 $this->send($cdrFile);
359 $this->cdr_ofs = $this->cdr_ofs->add(Bigint::init(strlen($cdrFile)));
360 }
361
362 // Add 64bit headers (if applicable)
363 if (count($this->files) >= 0xFFFF ||
364 $this->cdr_ofs->isOver32() ||
365 $this->ofs->isOver32()) {
366 if (!$this->opt->isEnableZip64()) {
367 throw new OverflowException();
368 }
369
370 $this->addCdr64Eof();
371 $this->addCdr64Locator();
372 }
373
374 // add trailing cdr eof record
375 $this->addCdrEof();
376
377 // The End
378 $this->clear();
379 }
380
386 protected function addCdr64Eof(): void
387 {
388 $num_files = count($this->files);
389 $cdr_length = $this->cdr_ofs;
390 $cdr_offset = $this->ofs;
391
392 $fields = [
393 ['V', static::ZIP64_CDR_EOF_SIGNATURE], // ZIP64 end of central file header signature
394 ['P', 44], // Length of data below this header (length of block - 12) = 44
395 ['v', static::ZIP_VERSION_MADE_BY], // Made by version
396 ['v', Version::ZIP64], // Extract by version
397 ['V', 0x00], // disk number
398 ['V', 0x00], // no of disks
399 ['P', $num_files], // no of entries on disk
400 ['P', $num_files], // no of entries in cdr
401 ['P', $cdr_length], // CDR size
402 ['P', $cdr_offset], // CDR offset
403 ];
404
405 $ret = static::packFields($fields);
406 $this->send($ret);
407 }
408
416 public static function packFields(array $fields): string
417 {
418 $fmt = '';
419 $args = [];
420
421 // populate format string and argument list
422 foreach ($fields as [$format, $value]) {
423 if ($format === 'P') {
424 $fmt .= 'VV';
425 if ($value instanceof Bigint) {
426 $args[] = $value->getLow32();
427 $args[] = $value->getHigh32();
428 } else {
429 $args[] = $value;
430 $args[] = 0;
431 }
432 } else {
433 if ($value instanceof Bigint) {
434 $value = $value->getLow32();
435 }
436 $fmt .= $format;
437 $args[] = $value;
438 }
439 }
440
441 // prepend format string to argument list
442 array_unshift($args, $fmt);
443
444 // build output string from header and compressed data
445 return pack(...$args);
446 }
447
455 public function send(string $str): void
456 {
457 if ($this->need_headers) {
458 $this->sendHttpHeaders();
459 }
460 $this->need_headers = false;
461
462 fwrite($this->opt->getOutputStream(), $str);
463
464 if ($this->opt->isFlushOutput()) {
465 // flush output buffer if it is on and flushable
466 $status = ob_get_status();
467 if (isset($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
468 ob_flush();
469 }
470
471 // Flush system buffers after flushing userspace output buffer
472 flush();
473 }
474 }
475
481 protected function sendHttpHeaders(): void
482 {
483 // grab content disposition
484 $disposition = $this->opt->getContentDisposition();
485
486 if ($this->output_name) {
487 // Various different browsers dislike various characters here. Strip them all for safety.
488 $safe_output = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->output_name));
489
490 // Check if we need to UTF-8 encode the filename
491 $urlencoded = rawurlencode($safe_output);
492 $disposition .= "; filename*=UTF-8''{$urlencoded}";
493 }
494
495 $headers = array(
496 'Content-Type' => $this->opt->getContentType(),
497 'Content-Disposition' => $disposition,
498 'Pragma' => 'public',
499 'Cache-Control' => 'public, must-revalidate',
500 'Content-Transfer-Encoding' => 'binary'
501 );
502
503 $call = $this->opt->getHttpHeaderCallback();
504 foreach ($headers as $key => $val) {
505 $call("$key: $val");
506 }
507 }
508
514 protected function addCdr64Locator(): void
515 {
516 $cdr_offset = $this->ofs->add($this->cdr_ofs);
517
518 $fields = [
519 ['V', static::ZIP64_CDR_LOCATOR_SIGNATURE], // ZIP64 end of central file header signature
520 ['V', 0x00], // Disc number containing CDR64EOF
521 ['P', $cdr_offset], // CDR offset
522 ['V', 1], // Total number of disks
523 ];
524
525 $ret = static::packFields($fields);
526 $this->send($ret);
527 }
528
534 protected function addCdrEof(): void
535 {
536 $num_files = count($this->files);
537 $cdr_length = $this->cdr_ofs;
538 $cdr_offset = $this->ofs;
539
540 // grab comment (if specified)
541 $comment = $this->opt->getComment();
542
543 $fields = [
544 ['V', static::CDR_EOF_SIGNATURE], // end of central file header signature
545 ['v', 0x00], // disk number
546 ['v', 0x00], // no of disks
547 ['v', min($num_files, 0xFFFF)], // no of entries on disk
548 ['v', min($num_files, 0xFFFF)], // no of entries in cdr
549 ['V', $cdr_length->getLowFF()], // CDR size
550 ['V', $cdr_offset->getLowFF()], // CDR offset
551 ['v', strlen($comment)], // Zip Comment size
552 ];
553
554 $ret = static::packFields($fields) . $comment;
555 $this->send($ret);
556 }
557
564 protected function clear(): void
565 {
566 $this->files = [];
567 $this->ofs = new Bigint();
568 $this->cdr_ofs = new Bigint();
569 $this->opt = new ArchiveOptions();
570 }
571
578 public function isLargeFile(string $path): bool
579 {
580 if (!$this->opt->isStatFiles()) {
581 return false;
582 }
583 $stat = stat($path);
584 return $stat['size'] > $this->opt->getLargeFileSize();
585 }
586
593 public function addToCdr(File $file): void
594 {
595 $file->ofs = $this->ofs;
596 $this->ofs = $this->ofs->add($file->getTotalLength());
597 $this->files[] = $file->getCdrFile();
598 }
599}
$path
Definition: aliased.php:25
$comment
Definition: buildRTE.php:83
while(count($oldTaskList) > 0) foreach(array_keys( $newTaskList) as $task) init()
Definition: build.php:77
An exception for terminatinating execution or to throw for unit testing.
This Exception gets invoked if a counter value exceeds storage size.
getTotalLength()
Definition: File.php:473
getCdrFile()
Send CDR record for specified file.
Definition: File.php:433
addToCdr(File $file)
Save file attributes for trailing CDR record.
Definition: ZipStream.php:593
__construct(?string $name=null, ?ArchiveOptions $opt=null)
Create a new ZipStream object.
Definition: ZipStream.php:171
clear()
Clear all internal variables.
Definition: ZipStream.php:564
addFileFromStream(string $name, $stream, ?FileOptions $options=null)
addFileFromStream
Definition: ZipStream.php:290
send(string $str)
Send string, sending HTTP headers if necessary.
Definition: ZipStream.php:455
isLargeFile(string $path)
Is this file larger than large_file_size?
Definition: ZipStream.php:578
addCdr64Locator()
Send ZIP64 CDR Locator (Central Directory Record Locator) record.
Definition: ZipStream.php:514
addCdr64Eof()
Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
Definition: ZipStream.php:386
addFileFromPsr7Stream(string $name, StreamInterface $stream, ?FileOptions $options=null)
addFileFromPsr7Stream
Definition: ZipStream.php:324
addFileFromPath(string $name, string $path, ?FileOptions $options=null)
addFileFromPath
Definition: ZipStream.php:256
sendHttpHeaders()
Send HTTP headers for this stream.
Definition: ZipStream.php:481
addCdrEof()
Send CDR EOF (Central Directory Record End-of-File) record.
Definition: ZipStream.php:534
addFile(string $name, string $data, ?FileOptions $options=null)
addFile
Definition: ZipStream.php:210
static packFields(array $fields)
Create a format string and argument list for pack(), then call pack() and return the result.
Definition: ZipStream.php:416
$key
Definition: croninfo.php:18
Describes a data stream.
$format
Definition: metadata.php:141
$files
Definition: metarefresh.php:49
$stream
PHP stream implementation.
Class Version \Option.
Definition: Bigint.php:4
$ret
Definition: parser.php:6
$data
Definition: bench.php:6