1<?php 2/** 3 * Minimal ZIP writer using the STORE method only. 4 * This avoids requiring ext-zip or PclZip. Suitable for generated Markdown packages. 5 */ 6class SkillForge_StoredZipWriter { 7 private $entries = array(); 8 9 public function addFileFromString($path, $data) { 10 $path = str_replace('\\', '/', trim($path, '/')); 11 if ($path === '') return; 12 $this->entries[] = array('path' => $path, 'data' => (string)$data); 13 } 14 15 public function addFile($pathInZip, $filePath) { 16 if (is_readable($filePath)) { 17 $this->addFileFromString($pathInZip, file_get_contents($filePath)); 18 } 19 } 20 21 public function save($targetFile) { 22 $fh = fopen($targetFile, 'wb'); 23 if (!$fh) return false; 24 25 $central = ''; 26 $offset = 0; 27 28 foreach ($this->entries as $entry) { 29 $name = $entry['path']; 30 $data = $entry['data']; 31 $crc = crc32($data); 32 if ($crc < 0) $crc += 4294967296; 33 $size = strlen($data); 34 list($time, $date) = $this->dosDateTime(); 35 36 $local = "PK\x03\x04"; 37 $local .= pack('v', 20); // version needed 38 $local .= pack('v', 0); // flags 39 $local .= pack('v', 0); // compression: store 40 $local .= pack('v', $time); 41 $local .= pack('v', $date); 42 $local .= pack('V', $crc); 43 $local .= pack('V', $size); 44 $local .= pack('V', $size); 45 $local .= pack('v', strlen($name)); 46 $local .= pack('v', 0); 47 $local .= $name; 48 49 fwrite($fh, $local); 50 fwrite($fh, $data); 51 52 $central .= "PK\x01\x02"; 53 $central .= pack('v', 20); // version made by 54 $central .= pack('v', 20); // version needed 55 $central .= pack('v', 0); 56 $central .= pack('v', 0); 57 $central .= pack('v', $time); 58 $central .= pack('v', $date); 59 $central .= pack('V', $crc); 60 $central .= pack('V', $size); 61 $central .= pack('V', $size); 62 $central .= pack('v', strlen($name)); 63 $central .= pack('v', 0); // extra length 64 $central .= pack('v', 0); // comment length 65 $central .= pack('v', 0); // disk start 66 $central .= pack('v', 0); // internal attrs 67 $central .= pack('V', 0); // external attrs 68 $central .= pack('V', $offset); 69 $central .= $name; 70 71 $offset += strlen($local) + $size; 72 } 73 74 $centralOffset = $offset; 75 fwrite($fh, $central); 76 $centralSize = strlen($central); 77 78 $end = "PK\x05\x06"; 79 $end .= pack('v', 0); 80 $end .= pack('v', 0); 81 $end .= pack('v', count($this->entries)); 82 $end .= pack('v', count($this->entries)); 83 $end .= pack('V', $centralSize); 84 $end .= pack('V', $centralOffset); 85 $end .= pack('v', 0); 86 fwrite($fh, $end); 87 fclose($fh); 88 return true; 89 } 90 91 private function dosDateTime() { 92 $t = getdate(); 93 $time = (($t['hours'] & 0x1f) << 11) | (($t['minutes'] & 0x3f) << 5) | (($t['seconds'] / 2) & 0x1f); 94 $date = ((($t['year'] - 1980) & 0x7f) << 9) | (($t['mon'] & 0x0f) << 5) | ($t['mday'] & 0x1f); 95 return array($time, $date); 96 } 97} 98