PageRenderTime 42ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/system/codeigniter/system/libraries/Zip.php

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