1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4/** 5 * ZIP archive writer 6 * 7 * PHP versions 4 and 5 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA 22 * 23 * @category File Formats 24 * @package File_Archive 25 * @author Vincent Lascaux <vincentlascaux@php.net> 26 * @copyright 1997-2005 The PHP Group 27 * @license http://www.gnu.org/copyleft/lesser.html LGPL 28 * @version CVS: $Id$ 29 * @link http://pear.php.net/package/File_Archive 30 */ 31 32require_once "File/Archive/Writer/MemoryArchive.php"; 33 34/** 35 * ZIP archive writer 36 */ 37class File_Archive_Writer_Zip extends File_Archive_Writer_MemoryArchive 38{ 39 /** 40 * @var int Compression level 41 * @access private 42 */ 43 var $compressionLevel; 44 45 /** 46 * @var int Current position in the writer 47 * @access private 48 */ 49 var $offset = 0; 50 51 /** 52 * @var string Optionnal comment to add to the zip 53 * @access private 54 */ 55 var $comment = ""; 56 57 /** 58 * @var string Data written at the end of the ZIP file 59 * @access private 60 */ 61 var $central = ""; 62 63 function File_Archive_Writer_Zip($filename, &$innerWriter, 64 $stat=array(), $autoClose = true) 65 { 66 global $_File_Archive_Options; 67 parent::File_Archive_Writer_MemoryArchive( 68 $filename, $innerWriter, $stat, $autoClose 69 ); 70 71 $this->compressionLevel = File_Archive::getOption('zipCompressionLevel', 9); 72 } 73 74 /** 75 * Change the level of the compression. This may be done between two files 76 * 77 * @param Int $compressionLevel New compression level from 0 to 9 78 */ 79 function setCompressionLevel($compressionLevel) 80 { 81 $this->compressionLevel = $compressionLevel; 82 } 83 84 /** 85 * Set a comment on the ZIP file 86 */ 87 function setComment($comment) { $this->comment = $comment; } 88 89 /** 90 * @param int $time Unix timestamp of the date to convert 91 * @return the date formated as a ZIP date 92 */ 93 function getMTime($time) 94 { 95 $mtime = ($time !== null ? getdate($time) : getdate()); 96 $mtime = preg_replace( 97 "/(..){1}(..){1}(..){1}(..){1}/", 98 "\\x\\4\\x\\3\\x\\2\\x\\1", 99 dechex(($mtime['year']-1980<<25)| 100 ($mtime['mon' ]<<21)| 101 ($mtime['mday' ]<<16)| 102 ($mtime['hours' ]<<11)| 103 ($mtime['minutes']<<5)| 104 ($mtime['seconds']>>1))); 105 eval('$mtime = "'.$mtime.'";'); 106 return $mtime; 107 } 108 109 /** 110 * Inform the archive that $filename is present. 111 * Consequences are the same as appendFileData, but no data is output 112 * to the inner writer. 113 * This is used by File_Archive_Reader_Zip::makeWriter() 114 * 115 * @param string $filename name of the file 116 * @param array $stat stats of the file, indexes 9 and 7 must be present 117 * @param int $crc32 checksum of the file 118 * @param int $compLength length of the compressed data 119 */ 120 function alreadyWrittenFile($filename, $stat, $crc32, $complength) 121 { 122 $filename = preg_replace("/^(\.{1,2}(\/|\\\))+/","",$filename); 123 124 $mtime = $this->getMTime(isset($stat[9]) ? $stat[9] : null); 125 $normlength = $stat[7]; 126 127 $this->nbFiles++; 128 129 $this->central .= "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00". 130 $mtime. 131 pack("VVVvvvvvVV", 132 $crc32, $complength, $normlength, 133 strlen($filename), 0x00,0x00,0x00,0x00, 134 0x0000,$this->offset). 135 $filename; 136 137 $this->offset += 30 + strlen($filename) + $complength; 138 } 139 140 /** 141 * @see File_Archive_Writer_MemoryArchive::appendFileData() 142 * @access protected 143 */ 144 function appendFileData($filename, $stat, $data) 145 { 146 $crc32 = crc32($data); 147 $normlength = strlen($data); 148 $data = gzcompress($data,$this->compressionLevel); 149 $data = substr($data,2,strlen($data)-6); 150 151 return $this->appendCompressedData($filename, $stat, $data, $crc32, $normlength); 152 } 153 154 function appendCompressedData($filename, $stat, $data, $crc32, $normlength) 155 { 156 $filename = preg_replace("/^(\.{1,2}(\/|\\\))+/","",$filename); 157 $mtime = $this->getMTime(isset($stat[9]) ? $stat[9] : null); 158 159 $complength = strlen($data); 160 161 $zipData = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00". 162 $mtime. 163 pack("VVVvv", 164 $crc32, 165 $complength, 166 $normlength, 167 strlen($filename), 168 0x00). 169 $filename. 170 $data; 171 172 $error = $this->innerWriter->writeData($zipData); 173 if (PEAR::isError($error)) { 174 return $error; 175 } 176 177 $this->central .= "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00". 178 $mtime. 179 pack("VVVvvvvvVV", 180 $crc32, $complength, $normlength, 181 strlen($filename), 0x00,0x00,0x00,0x00, 182 0x0000,$this->offset). 183 $filename; 184 185 $this->offset += strlen($zipData); 186 } 187 188 function appendFile($filename, $dataFilename) 189 { 190 //Try to read from the cache 191 $cache = File_Archive::getOption('cache', null); 192 if ($cache !== null && $this->compressionLevel > 0) { 193 $id = realpath($dataFilename); 194 $id = urlencode($id); 195 $id = str_replace('_', '%5F', $id); 196 197 $group = 'FileArchiveZip'.$this->compressionLevel; 198 $mtime = filemtime($dataFilename); 199 200 //Tries to read from cache 201 if (($data = $cache->get($id, $group)) !== false) { 202 $info = unpack('Vmtime/Vcrc/Vnlength', substr($data, 0, 12)); 203 $data = substr($data, 12); 204 } 205 206 //If cache failed or file modified since then 207 if ($data === false || 208 $info['mtime'] != $mtime) { 209 210 $data = file_get_contents($dataFilename); 211 212 $info = array( 213 'crc' => crc32($data), 214 'nlength' => strlen($data), 215 'mtime' => $mtime 216 ); 217 218 $data = gzcompress($data,$this->compressionLevel); 219 $data = substr($data,2,strlen($data)-6); 220 $data = pack('VVV', $info['mtime'], $info['crc'], $info['nlength']).$data; 221 $cache->save($data, $id, $group); 222 } 223 224 return $this->appendCompressedData( 225 $filename, 226 stat($dataFilename), 227 $data, 228 $info['crc'], 229 $info['nlength'] 230 ); 231 232 } 233 234 //If no cache system, use the standard way 235 return parent::appendFile($filename, $dataFilename); 236 } 237 238 /** 239 * @see File_Archive_Writer_MemoryArchive::sendFooter() 240 * @access protected 241 */ 242 function sendFooter() 243 { 244 return $this->innerWriter->writeData( 245 $this->central. 246 "\x50\x4b\x05\x06\x00\x00\x00\x00". 247 pack("vvVVv", 248 $this->nbFiles,$this->nbFiles, 249 strlen($this->central),$this->offset, 250 strlen($this->comment)). 251 $this->comment 252 ); 253 } 254 /** 255 * @see File_Archive_Writer::getMime() 256 */ 257 function getMime() { return "application/zip"; } 258} 259 260?>