/system/libraries/Zip.php
PHP | 359 lines | 186 code | 45 blank | 128 comment | 17 complexity | 20e9638903b7b178270e3072091cf5d5 MD5 | raw file
Possible License(s): Unlicense
1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2/** 3 * CodeIgniter 4 * 5 * An open source application development framework for PHP 4.3.2 or newer 6 * 7 * @package CodeIgniter 8 * @author ExpressionEngine Dev Team 9 * @copyright Copyright (c) 2008 - 2010, EllisLab, Inc. 10 * @license http://codeigniter.com/user_guide/license.html 11 * @link http://codeigniter.com 12 * @since Version 1.0 13 * @filesource 14 */ 15 16// ------------------------------------------------------------------------ 17 18/** 19 * Zip Compression Class 20 * 21 * This class is based on a library I found at Zend: 22 * http://www.zend.com/codex.php?id=696&single=1 23 * 24 * The original library is a little rough around the edges so I 25 * refactored it and added several additional methods -- Rick Ellis 26 * 27 * @package CodeIgniter 28 * @subpackage Libraries 29 * @category Encryption 30 * @author ExpressionEngine Dev Team 31 * @link http://codeigniter.com/user_guide/libraries/zip.html 32 */ 33class CI_Zip { 34 35 var $zipdata = ''; 36 var $directory = ''; 37 var $entries = 0; 38 var $file_num = 0; 39 var $offset = 0; 40 41 function CI_Zip() 42 { 43 log_message('debug', "Zip Compression Class Initialized"); 44 } 45 46 // -------------------------------------------------------------------- 47 48 /** 49 * Add Directory 50 * 51 * Lets you add a virtual directory into which you can place files. 52 * 53 * @access public 54 * @param mixed the directory name. Can be string or array 55 * @return void 56 */ 57 function add_dir($directory) 58 { 59 foreach ((array)$directory as $dir) 60 { 61 if ( ! preg_match("|.+/$|", $dir)) 62 { 63 $dir .= '/'; 64 } 65 66 $this->_add_dir($dir); 67 } 68 } 69 70 // -------------------------------------------------------------------- 71 72 /** 73 * Add Directory 74 * 75 * @access private 76 * @param string the directory name 77 * @return void 78 */ 79 function _add_dir($dir) 80 { 81 $dir = str_replace("\\", "/", $dir); 82 83 $this->zipdata .= 84 "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00" 85 .pack('V', 0) // crc32 86 .pack('V', 0) // compressed filesize 87 .pack('V', 0) // uncompressed filesize 88 .pack('v', strlen($dir)) // length of pathname 89 .pack('v', 0) // extra field length 90 .$dir 91 // below is "data descriptor" segment 92 .pack('V', 0) // crc32 93 .pack('V', 0) // compressed filesize 94 .pack('V', 0); // uncompressed filesize 95 96 $this->directory .= 97 "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00" 98 .pack('V',0) // crc32 99 .pack('V',0) // compressed filesize 100 .pack('V',0) // uncompressed filesize 101 .pack('v', strlen($dir)) // length of pathname 102 .pack('v', 0) // extra field length 103 .pack('v', 0) // file comment length 104 .pack('v', 0) // disk number start 105 .pack('v', 0) // internal file attributes 106 .pack('V', 16) // external file attributes - 'directory' bit set 107 .pack('V', $this->offset) // relative offset of local header 108 .$dir; 109 110 $this->offset = strlen($this->zipdata); 111 $this->entries++; 112 } 113 114 // -------------------------------------------------------------------- 115 116 /** 117 * Add Data to Zip 118 * 119 * Lets you add files to the archive. If the path is included 120 * in the filename it will be placed within a directory. Make 121 * sure you use add_dir() first to create the folder. 122 * 123 * @access public 124 * @param mixed 125 * @param string 126 * @return void 127 */ 128 function add_data($filepath, $data = NULL) 129 { 130 if (is_array($filepath)) 131 { 132 foreach ($filepath as $path => $data) 133 { 134 $this->_add_data($path, $data); 135 } 136 } 137 else 138 { 139 $this->_add_data($filepath, $data); 140 } 141 } 142 143 // -------------------------------------------------------------------- 144 145 /** 146 * Add Data to Zip 147 * 148 * @access private 149 * @param string the file name/path 150 * @param string the data to be encoded 151 * @return void 152 */ 153 function _add_data($filepath, $data) 154 { 155 $filepath = str_replace("\\", "/", $filepath); 156 157 $uncompressed_size = strlen($data); 158 $crc32 = crc32($data); 159 160 $gzdata = gzcompress($data); 161 $gzdata = substr($gzdata, 2, -4); 162 $compressed_size = strlen($gzdata); 163 164 $this->zipdata .= 165 "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00" 166 .pack('V', $crc32) 167 .pack('V', $compressed_size) 168 .pack('V', $uncompressed_size) 169 .pack('v', strlen($filepath)) // length of filename 170 .pack('v', 0) // extra field length 171 .$filepath 172 .$gzdata; // "file data" segment 173 174 $this->directory .= 175 "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00" 176 .pack('V', $crc32) 177 .pack('V', $compressed_size) 178 .pack('V', $uncompressed_size) 179 .pack('v', strlen($filepath)) // length of filename 180 .pack('v', 0) // extra field length 181 .pack('v', 0) // file comment length 182 .pack('v', 0) // disk number start 183 .pack('v', 0) // internal file attributes 184 .pack('V', 32) // external file attributes - 'archive' bit set 185 .pack('V', $this->offset) // relative offset of local header 186 .$filepath; 187 188 $this->offset = strlen($this->zipdata); 189 $this->entries++; 190 $this->file_num++; 191 } 192 193 // -------------------------------------------------------------------- 194 195 /** 196 * Read the contents of a file and add it to the zip 197 * 198 * @access public 199 * @return bool 200 */ 201 function read_file($path, $preserve_filepath = FALSE) 202 { 203 if ( ! file_exists($path)) 204 { 205 return FALSE; 206 } 207 208 if (FALSE !== ($data = file_get_contents($path))) 209 { 210 $name = str_replace("\\", "/", $path); 211 212 if ($preserve_filepath === FALSE) 213 { 214 $name = preg_replace("|.*/(.+)|", "\\1", $name); 215 } 216 217 $this->add_data($name, $data); 218 return TRUE; 219 } 220 return FALSE; 221 } 222 223 // ------------------------------------------------------------------------ 224 225 /** 226 * Read a directory and add it to the zip. 227 * 228 * This function recursively reads a folder and everything it contains (including 229 * sub-folders) and creates a zip based on it. Whatever directory structure 230 * is in the original file path will be recreated in the zip file. 231 * 232 * @access public 233 * @param string path to source 234 * @return bool 235 */ 236 function read_dir($path) 237 { 238 if ($fp = @opendir($path)) 239 { 240 while (FALSE !== ($file = readdir($fp))) 241 { 242 if (@is_dir($path.$file) && substr($file, 0, 1) != '.') 243 { 244 $this->read_dir($path.$file."/"); 245 } 246 elseif (substr($file, 0, 1) != ".") 247 { 248 if (FALSE !== ($data = file_get_contents($path.$file))) 249 { 250 $this->add_data(str_replace("\\", "/", $path).$file, $data); 251 } 252 } 253 } 254 return TRUE; 255 } 256 } 257 258 // -------------------------------------------------------------------- 259 260 /** 261 * Get the Zip file 262 * 263 * @access public 264 * @return binary string 265 */ 266 function get_zip() 267 { 268 // Is there any data to return? 269 if ($this->entries == 0) 270 { 271 return FALSE; 272 } 273 274 $zip_data = $this->zipdata; 275 $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00"; 276 $zip_data .= pack('v', $this->entries); // total # of entries "on this disk" 277 $zip_data .= pack('v', $this->entries); // total # of entries overall 278 $zip_data .= pack('V', strlen($this->directory)); // size of central dir 279 $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir 280 $zip_data .= "\x00\x00"; // .zip file comment length 281 282 return $zip_data; 283 } 284 285 // -------------------------------------------------------------------- 286 287 /** 288 * Write File to the specified directory 289 * 290 * Lets you write a file 291 * 292 * @access public 293 * @param string the file name 294 * @return bool 295 */ 296 function archive($filepath) 297 { 298 if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE))) 299 { 300 return FALSE; 301 } 302 303 flock($fp, LOCK_EX); 304 fwrite($fp, $this->get_zip()); 305 flock($fp, LOCK_UN); 306 fclose($fp); 307 308 return TRUE; 309 } 310 311 // -------------------------------------------------------------------- 312 313 /** 314 * Download 315 * 316 * @access public 317 * @param string the file name 318 * @param string the data to be encoded 319 * @return bool 320 */ 321 function download($filename = 'backup.zip') 322 { 323 if ( ! preg_match("|.+?\.zip$|", $filename)) 324 { 325 $filename .= '.zip'; 326 } 327 328 $zip_content =& $this->get_zip(); 329 330 $CI =& get_instance(); 331 $CI->load->helper('download'); 332 333 force_download($filename, $zip_content); 334 } 335 336 // -------------------------------------------------------------------- 337 338 /** 339 * Initialize Data 340 * 341 * Lets you clear current zip data. Useful if you need to create 342 * multiple zips with different data. 343 * 344 * @access public 345 * @return void 346 */ 347 function clear_data() 348 { 349 $this->zipdata = ''; 350 $this->directory = ''; 351 $this->entries = 0; 352 $this->file_num = 0; 353 $this->offset = 0; 354 } 355 356} 357 358/* End of file Zip.php */ 359/* Location: ./system/libraries/Zip.php */