1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852: 853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: 880: 881: 882: 883: 884: 885: 886: 887: 888: 889: 890: 891: 892: 893: 894: 895: 896: 897: 898: 899: 900: 901: 902: 903: 904: 905: 906: 907: 908: 909: 910: 911: 912: 913: 914: 915: 916: 917: 918: 919: 920: 921: 922: 923: 924: 925: 926: 927: 928: 929: 930: 931: 932: 933: 934: 935: 936: 937: 938: 939: 940: 941: 942: 943: 944: 945: 946: 947: 948: 949: 950: 951: 952: 953: 954: 955: 956: 957: 958: 959: 960: 961: 962: 963: 964: 965: 966: 967: 968: 969: 970: 971: 972: 973: 974: 975: 976: 977: 978: 979: 980: 981: 982: 983: 984: 985: 986: 987: 988: 989: 990: 991: 992: 993: 994: 995: 996: 997: 998: 999: 1000: 1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027:
<?php
namespace splitbrain\PHPArchive;
class Zip extends Archive
{
const LOCAL_FILE_HEADER_CRC_OFFSET = 14;
protected $file = '';
protected $fh;
protected $memory = '';
protected $closed = true;
protected $writeaccess = false;
protected $ctrl_dir;
protected $complevel = 9;
public function setCompression($level = 9, $type = Archive::COMPRESS_AUTO)
{
if ($level < -1 || $level > 9) {
throw new ArchiveIllegalCompressionException('Compression level should be between -1 and 9');
}
$this->complevel = $level;
}
public function open($file)
{
$this->file = $file;
$this->fh = @fopen($this->file, 'rb');
if (!$this->fh) {
throw new ArchiveIOException('Could not open file for reading: '.$this->file);
}
$this->closed = false;
}
public function contents()
{
$result = array();
foreach ($this->yieldContents() as $fileinfo) {
$result[] = $fileinfo;
}
return $result;
}
public function yieldContents()
{
if ($this->closed || !$this->file) {
throw new ArchiveIOException('Can not read from a closed archive');
}
$centd = $this->readCentralDir();
@rewind($this->fh);
@fseek($this->fh, $centd['offset']);
for ($i = 0; $i < $centd['entries']; $i++) {
yield $this->header2fileinfo($this->readCentralFileHeader());
}
$this->close();
}
public function extract($outdir, $strip = '', $exclude = '', $include = '')
{
if ($this->closed || !$this->file) {
throw new ArchiveIOException('Can not read from a closed archive');
}
$outdir = rtrim($outdir, '/');
@mkdir($outdir, 0777, true);
$extracted = array();
$cdir = $this->readCentralDir();
$pos_entry = $cdir['offset'];
for ($i = 0; $i < $cdir['entries']; $i++) {
@fseek($this->fh, $pos_entry);
$header = $this->readCentralFileHeader();
$header['index'] = $i;
$pos_entry = ftell($this->fh);
fseek($this->fh, $header['offset']);
$header = $this->readFileHeader($header);
$fileinfo = $this->header2fileinfo($header);
$fileinfo->strip($strip);
if (!strlen($fileinfo->getPath()) || !$fileinfo->matchExpression($include, $exclude)) {
continue;
}
$extracted[] = $fileinfo;
$output = $outdir.'/'.$fileinfo->getPath();
$directory = ($header['folder']) ? $output : dirname($output);
@mkdir($directory, 0777, true);
if ($fileinfo->getIsdir()) {
if(is_callable($this->callback)) {
call_user_func($this->callback, $fileinfo);
}
continue;
}
if ($header['compression'] == 0) {
$extractto = $output;
} else {
$extractto = $output.'.gz';
}
$fp = @fopen($extractto, "wb");
if (!$fp) {
throw new ArchiveIOException('Could not open file for writing: '.$extractto);
}
if ($header['compression'] != 0) {
$binary_data = pack(
'va1a1Va1a1',
0x8b1f,
chr($header['compression']),
chr(0x00),
time(),
chr(0x00),
chr(3)
);
fwrite($fp, $binary_data, 10);
}
$size = $header['compressed_size'];
while ($size != 0) {
$read_size = ($size < 2048 ? $size : 2048);
$buffer = fread($this->fh, $read_size);
$binary_data = pack('a'.$read_size, $buffer);
fwrite($fp, $binary_data, $read_size);
$size -= $read_size;
}
if ($header['compression'] != 0) {
$binary_data = pack('VV', $header['crc'], $header['size']);
fwrite($fp, $binary_data, 8);
}
fclose($fp);
if ($header['compression'] != 0) {
$gzp = @gzopen($extractto, 'rb');
if (!$gzp) {
@unlink($extractto);
throw new ArchiveIOException('Failed file extracting. gzip support missing?');
}
$fp = @fopen($output, 'wb');
if (!$fp) {
throw new ArchiveIOException('Could not open file for writing: '.$extractto);
}
$size = $header['size'];
while ($size != 0) {
$read_size = ($size < 2048 ? $size : 2048);
$buffer = gzread($gzp, $read_size);
$binary_data = pack('a'.$read_size, $buffer);
@fwrite($fp, $binary_data, $read_size);
$size -= $read_size;
}
fclose($fp);
gzclose($gzp);
unlink($extractto);
}
@touch($output, $fileinfo->getMtime());
if(is_callable($this->callback)) {
call_user_func($this->callback, $fileinfo);
}
}
$this->close();
return $extracted;
}
public function create($file = '')
{
$this->file = $file;
$this->memory = '';
$this->fh = 0;
if ($this->file) {
$this->fh = @fopen($this->file, 'wb');
if (!$this->fh) {
throw new ArchiveIOException('Could not open file for writing: '.$this->file);
}
}
$this->writeaccess = true;
$this->closed = false;
$this->ctrl_dir = array();
}
public function addFile($file, $fileinfo = '')
{
if (is_string($fileinfo)) {
$fileinfo = FileInfo::fromPath($file, $fileinfo);
}
if ($this->closed) {
throw new ArchiveIOException('Archive has been closed, files can no longer be added');
}
$fp = @fopen($file, 'rb');
if ($fp === false) {
throw new ArchiveIOException('Could not open file for reading: '.$file);
}
$offset = $this->dataOffset();
$name = $fileinfo->getPath();
$time = $fileinfo->getMtime();
$this->writebytes($this->makeLocalFileHeader(
$time,
0,
0,
0,
$name,
(bool) $this->complevel
));
$deflate_context = deflate_init(ZLIB_ENCODING_DEFLATE, ['level' => $this->complevel]);
$crc_context = hash_init('crc32b');
$size = $csize = 0;
while (!feof($fp)) {
$block = fread($fp, 512);
if ($this->complevel) {
$is_first_block = $size === 0;
$is_last_block = feof($fp);
if ($is_last_block) {
$c_block = deflate_add($deflate_context, $block, ZLIB_FINISH);
$c_block = substr($c_block, 0, -4);
} else {
$c_block = deflate_add($deflate_context, $block, ZLIB_NO_FLUSH);
}
if ($is_first_block) {
$c_block = substr($c_block, 2);
}
$csize += strlen($c_block);
$this->writebytes($c_block);
} else {
$this->writebytes($block);
}
$size += strlen($block);
hash_update($crc_context, $block);
}
fclose($fp);
$crc = hexdec(hash_final($crc_context));
$csize = $this->complevel ? $csize : $size;
$this->writebytesAt($this->makeCrcAndSize(
$crc,
$size,
$csize
), $offset + self::LOCAL_FILE_HEADER_CRC_OFFSET);
$this->ctrl_dir[] = $this->makeCentralFileRecord(
$offset,
$time,
$crc,
$size,
$csize,
$name,
(bool) $this->complevel
);
if(is_callable($this->callback)) {
call_user_func($this->callback, $fileinfo);
}
}
public function addData($fileinfo, $data)
{
if (is_string($fileinfo)) {
$fileinfo = new FileInfo($fileinfo);
}
if ($this->closed) {
throw new ArchiveIOException('Archive has been closed, files can no longer be added');
}
$size = strlen($data);
$crc = crc32($data);
if ($this->complevel) {
$data = gzcompress($data, $this->complevel);
$data = substr($data, 2, -4);
}
$csize = strlen($data);
$offset = $this->dataOffset();
$name = $fileinfo->getPath();
$time = $fileinfo->getMtime();
$this->writebytes($this->makeLocalFileHeader(
$time,
$crc,
$size,
$csize,
$name,
(bool) $this->complevel
));
$this->writebytes($data);
$this->ctrl_dir[] = $this->makeCentralFileRecord(
$offset,
$time,
$crc,
$size,
$csize,
$name,
(bool) $this->complevel
);
if(is_callable($this->callback)) {
call_user_func($this->callback, $fileinfo);
}
}
public function close()
{
if ($this->closed) {
return;
}
if ($this->writeaccess) {
$offset = $this->dataOffset();
$ctrldir = join('', $this->ctrl_dir);
$this->writebytes($ctrldir);
$this->writebytes("\x50\x4b\x05\x06");
$this->writebytes(pack('v', 0));
$this->writebytes(pack('v', 0));
$this->writebytes(pack('v',
count($this->ctrl_dir)));
$this->writebytes(pack('v', count($this->ctrl_dir)));
$this->writebytes(pack('V', strlen($ctrldir)));
$this->writebytes(pack('V',
$offset));
$this->writebytes(pack('v', 0));
$this->ctrl_dir = array();
}
if ($this->file) {
fclose($this->fh);
$this->file = '';
$this->fh = 0;
}
$this->writeaccess = false;
$this->closed = true;
}
public function getArchive()
{
$this->close();
return $this->memory;
}
public function save($file)
{
if (!@file_put_contents($file, $this->getArchive())) {
throw new ArchiveIOException('Could not write to file: '.$file);
}
}
protected function readCentralDir()
{
$size = filesize($this->file);
if ($size < 277) {
$maximum_size = $size;
} else {
$maximum_size = 277;
}
@fseek($this->fh, $size - $maximum_size);
$pos = ftell($this->fh);
$bytes = 0x00000000;
while ($pos < $size) {
$byte = @fread($this->fh, 1);
$bytes = (($bytes << 8) & 0xFFFFFFFF) | ord($byte);
if ($bytes == 0x504b0506) {
break;
}
$pos++;
}
$data = unpack(
'vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size',
fread($this->fh, 18)
);
if ($data['comment_size'] != 0) {
$centd['comment'] = fread($this->fh, $data['comment_size']);
} else {
$centd['comment'] = '';
}
$centd['entries'] = $data['entries'];
$centd['disk_entries'] = $data['disk_entries'];
$centd['offset'] = $data['offset'];
$centd['disk_start'] = $data['disk_start'];
$centd['size'] = $data['size'];
$centd['disk'] = $data['disk'];
return $centd;
}
protected function readCentralFileHeader()
{
$binary_data = fread($this->fh, 46);
$header = unpack(
'vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset',
$binary_data
);
if ($header['filename_len'] != 0) {
$header['filename'] = fread($this->fh, $header['filename_len']);
} else {
$header['filename'] = '';
}
if ($header['extra_len'] != 0) {
$header['extra'] = fread($this->fh, $header['extra_len']);
$header['extradata'] = $this->parseExtra($header['extra']);
} else {
$header['extra'] = '';
$header['extradata'] = array();
}
if ($header['comment_len'] != 0) {
$header['comment'] = fread($this->fh, $header['comment_len']);
} else {
$header['comment'] = '';
}
$header['mtime'] = $this->makeUnixTime($header['mdate'], $header['mtime']);
$header['stored_filename'] = $header['filename'];
$header['status'] = 'ok';
if (substr($header['filename'], -1) == '/') {
$header['external'] = 0x41FF0010;
}
$header['folder'] = ($header['external'] == 0x41FF0010 || $header['external'] == 16) ? 1 : 0;
return $header;
}
protected function readFileHeader($header)
{
$binary_data = fread($this->fh, 30);
$data = unpack(
'vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len',
$binary_data
);
$header['filename'] = fread($this->fh, $data['filename_len']);
if ($data['extra_len'] != 0) {
$header['extra'] = fread($this->fh, $data['extra_len']);
$header['extradata'] = array_merge($header['extradata'], $this->parseExtra($header['extra']));
} else {
$header['extra'] = '';
$header['extradata'] = array();
}
$header['compression'] = $data['compression'];
foreach (array(
'size',
'compressed_size',
'crc'
) as $hd) {
if ($data[$hd] != 0) {
$header[$hd] = $data[$hd];
}
}
$header['flag'] = $data['flag'];
$header['mtime'] = $this->makeUnixTime($data['mdate'], $data['mtime']);
$header['stored_filename'] = $header['filename'];
$header['status'] = "ok";
$header['folder'] = ($header['external'] == 0x41FF0010 || $header['external'] == 16) ? 1 : 0;
return $header;
}
protected function parseExtra($header)
{
$extra = array();
while (strlen($header) !== 0) {
$set = unpack('vid/vlen', $header);
$header = substr($header, 4);
$value = substr($header, 0, $set['len']);
$header = substr($header, $set['len']);
$extra[$set['id']] = $value;
}
if(isset($extra[0x6375])) {
$extra['utf8comment'] = substr($extra[0x7075], 5);
}
if(isset($extra[0x7075])) {
$extra['utf8path'] = substr($extra[0x7075], 5);
}
return $extra;
}
protected function header2fileinfo($header)
{
$fileinfo = new FileInfo();
$fileinfo->setSize($header['size']);
$fileinfo->setCompressedSize($header['compressed_size']);
$fileinfo->setMtime($header['mtime']);
$fileinfo->setComment($header['comment']);
$fileinfo->setIsdir($header['external'] == 0x41FF0010 || $header['external'] == 16);
if(isset($header['extradata']['utf8path'])) {
$fileinfo->setPath($header['extradata']['utf8path']);
} else {
$fileinfo->setPath($this->cpToUtf8($header['filename']));
}
if(isset($header['extradata']['utf8comment'])) {
$fileinfo->setComment($header['extradata']['utf8comment']);
} else {
$fileinfo->setComment($this->cpToUtf8($header['comment']));
}
return $fileinfo;
}
protected function cpToUtf8($string)
{
if (function_exists('iconv') && @iconv_strlen('', 'CP437') !== false) {
return iconv('CP437', 'UTF-8', $string);
} elseif (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($string, 'UTF-8', 'CP850');
} else {
return $string;
}
}
protected function utf8ToCp($string)
{
if (function_exists('iconv')) {
$conv = @iconv('UTF-8', 'CP437//IGNORE', $string);
if($conv) return $conv;
}
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($string, 'CP850', 'UTF-8');
} else {
return $string;
}
}
protected function writebytes($data)
{
if (!$this->file) {
$this->memory .= $data;
$written = strlen($data);
} else {
$written = @fwrite($this->fh, $data);
}
if ($written === false) {
throw new ArchiveIOException('Failed to write to archive stream');
}
return $written;
}
protected function writebytesAt($data, $offset) {
if (!$this->file) {
$this->memory .= substr_replace($this->memory, $data, $offset);
$written = strlen($data);
} else {
@fseek($this->fh, $offset);
$written = @fwrite($this->fh, $data);
@fseek($this->fh, 0, SEEK_END);
}
if ($written === false) {
throw new ArchiveIOException('Failed to write to archive stream');
}
return $written;
}
protected function dataOffset()
{
if ($this->file) {
return ftell($this->fh);
} else {
return strlen($this->memory);
}
}
protected function makeDosTime($time)
{
$timearray = getdate($time);
if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
}
return (($timearray['year'] - 1980) << 25) |
($timearray['mon'] << 21) |
($timearray['mday'] << 16) |
($timearray['hours'] << 11) |
($timearray['minutes'] << 5) |
($timearray['seconds'] >> 1);
}
protected function makeUnixTime($mdate = null, $mtime = null)
{
if ($mdate && $mtime) {
$year = (($mdate & 0xFE00) >> 9) + 1980;
$month = ($mdate & 0x01E0) >> 5;
$day = $mdate & 0x001F;
$hour = ($mtime & 0xF800) >> 11;
$minute = ($mtime & 0x07E0) >> 5;
$seconde = ($mtime & 0x001F) << 1;
$mtime = mktime($hour, $minute, $seconde, $month, $day, $year);
} else {
$mtime = time();
}
return $mtime;
}
protected function makeCentralFileRecord($offset, $ts, $crc, $len, $clen, $name, $comp = null)
{
if(is_null($comp)) $comp = $len != $clen;
$comp = $comp ? 8 : 0;
$dtime = dechex($this->makeDosTime($ts));
list($name, $extra) = $this->encodeFilename($name);
$header = "\x50\x4b\x01\x02";
$header .= pack('v', 14);
$header .= pack('v', 20);
$header .= pack('v', 0);
$header .= pack('v', $comp);
$header .= pack(
'H*',
$dtime[6] . $dtime[7] .
$dtime[4] . $dtime[5] .
$dtime[2] . $dtime[3] .
$dtime[0] . $dtime[1]
);
$header .= pack('V', $crc);
$header .= pack('V', $clen);
$header .= pack('V', $len);
$header .= pack('v', strlen($name));
$header .= pack('v', strlen($extra));
$header .= pack('v', 0);
$header .= pack('v', 0);
$header .= pack('v', 0);
$header .= pack('V', 0);
$header .= pack('V', $offset);
$header .= $name;
$header .= $extra;
return $header;
}
protected function makeLocalFileHeader($ts, $crc, $len, $clen, $name, $comp = null)
{
if(is_null($comp)) $comp = $len != $clen;
$comp = $comp ? 8 : 0;
$dtime = dechex($this->makeDosTime($ts));
list($name, $extra) = $this->encodeFilename($name);
$header = "\x50\x4b\x03\x04";
$header .= pack('v', 20);
$header .= pack('v', 0);
$header .= pack('v', $comp);
$header .= pack(
'H*',
$dtime[6] . $dtime[7] .
$dtime[4] . $dtime[5] .
$dtime[2] . $dtime[3] .
$dtime[0] . $dtime[1]
);
$header .= pack('V', $crc);
$header .= pack('V', $clen);
$header .= pack('V', $len);
$header .= pack('v', strlen($name));
$header .= pack('v', strlen($extra));
$header .= $name;
$header .= $extra;
return $header;
}
protected function makeCrcAndSize($crc, $len, $clen) {
$header = pack('V', $crc);
$header .= pack('V', $clen);
$header .= pack('V', $len);
return $header;
}
protected function encodeFilename($original)
{
$cp437 = $this->utf8ToCp($original);
if ($cp437 === $original) {
return array($original, '');
}
$extra = pack(
'vvCV',
0x7075,
strlen($original) + 5,
1,
crc32($original)
);
$extra .= $original;
return array($cp437, $extra);
}
}