PageRenderTime 58ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/Zip.php

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