Zip.php

Go to the documentation of this file.
00001 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
00002 /**
00003  * CodeIgniter
00004  *
00005  * An open source application development framework for PHP 4.3.2 or newer
00006  *
00007  * @package             CodeIgniter
00008  * @author              ExpressionEngine Dev Team
00009  * @copyright   Copyright (c) 2008, EllisLab, Inc.
00010  * @license             http://codeigniter.com/user_guide/license.html
00011  * @link                http://codeigniter.com
00012  * @since               Version 1.0
00013  * @filesource
00014  */
00015 
00016 // ------------------------------------------------------------------------
00017 
00018 /**
00019  * Zip Compression Class
00020  *
00021  * This class is based on a library I found at Zend:
00022  * http://www.zend.com/codex.php?id=696&single=1
00023  *
00024  * The original library is a little rough around the edges so I
00025  * refactored it and added several additional methods -- Rick Ellis
00026  *
00027  * @package             CodeIgniter
00028  * @subpackage  Libraries
00029  * @category    Encryption
00030  * @author              ExpressionEngine Dev Team
00031  * @link                http://codeigniter.com/user_guide/libraries/zip.html
00032  */
00033 class CI_Zip  {
00034 
00035         var $zipdata    = '';
00036         var $directory  = '';
00037         var $entries    = 0;
00038         var $file_num   = 0;
00039         var $offset             = 0;
00040 
00041         function CI_Zip()
00042         {
00043                 log_message('debug', "Zip Compression Class Initialized");
00044         }
00045 
00046         // --------------------------------------------------------------------
00047 
00048         /**
00049          * Add Directory
00050          *
00051          * Lets you add a virtual directory into which you can place files.
00052          *
00053          * @access      public
00054          * @param       mixed   the directory name. Can be string or array
00055          * @return      void
00056          */
00057         function add_dir($directory)
00058         {
00059                 foreach ((array)$directory as $dir)
00060                 {
00061                         if ( ! preg_match("|.+/$|", $dir))
00062                         {
00063                                 $dir .= '/';
00064                         }
00065 
00066                         $this->_add_dir($dir);
00067                 }
00068         }
00069 
00070         // --------------------------------------------------------------------
00071 
00072         /**
00073          * Add Directory
00074          *
00075          * @access      private
00076          * @param       string  the directory name
00077          * @return      void
00078          */
00079         function _add_dir($dir)
00080         {
00081                 $dir = str_replace("\\", "/", $dir);
00082 
00083                 $this->zipdata .=
00084                         "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00085                         .pack('V', 0) // crc32
00086                         .pack('V', 0) // compressed filesize
00087                         .pack('V', 0) // uncompressed filesize
00088                         .pack('v', strlen($dir)) // length of pathname
00089                         .pack('v', 0) // extra field length
00090                         .$dir
00091                         // below is "data descriptor" segment
00092                         .pack('V', 0) // crc32
00093                         .pack('V', 0) // compressed filesize
00094                         .pack('V', 0); // uncompressed filesize
00095 
00096                 $this->directory .=
00097                         "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00098                         .pack('V',0) // crc32
00099                         .pack('V',0) // compressed filesize
00100                         .pack('V',0) // uncompressed filesize
00101                         .pack('v', strlen($dir)) // length of pathname
00102                         .pack('v', 0) // extra field length
00103                         .pack('v', 0) // file comment length
00104                         .pack('v', 0) // disk number start
00105                         .pack('v', 0) // internal file attributes
00106                         .pack('V', 16) // external file attributes - 'directory' bit set
00107                         .pack('V', $this->offset) // relative offset of local header
00108                         .$dir;
00109 
00110                 $this->offset = strlen($this->zipdata);
00111                 $this->entries++;
00112         }
00113         
00114         // --------------------------------------------------------------------
00115 
00116         /**
00117          * Add Data to Zip
00118          *
00119          * Lets you add files to the archive. If the path is included
00120          * in the filename it will be placed within a directory.  Make
00121          * sure you use add_dir() first to create the folder.
00122          *
00123          * @access      public
00124          * @param       mixed
00125          * @param       string
00126          * @return      void
00127          */     
00128         function add_data($filepath, $data = NULL)
00129         {
00130                 if (is_array($filepath))
00131                 {
00132                         foreach ($filepath as $path => $data)
00133                         {
00134                                 $this->_add_data($path, $data);
00135                         }
00136                 }
00137                 else
00138                 {
00139                         $this->_add_data($filepath, $data);
00140                 }
00141         }
00142 
00143         // --------------------------------------------------------------------
00144 
00145         /**
00146          * Add Data to Zip
00147          *
00148          * @access      private
00149          * @param       string  the file name/path
00150          * @param       string  the data to be encoded
00151          * @return      void
00152          */     
00153         function _add_data($filepath, $data)
00154         {
00155                 $filepath = str_replace("\\", "/", $filepath);
00156 
00157                 $uncompressed_size = strlen($data);
00158                 $crc32  = crc32($data);
00159 
00160                 $gzdata = gzcompress($data);
00161                 $gzdata = substr($gzdata, 2, -4);
00162                 $compressed_size = strlen($gzdata);
00163 
00164                 $this->zipdata .=
00165                         "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00"
00166                         .pack('V', $crc32)
00167                         .pack('V', $compressed_size)
00168                         .pack('V', $uncompressed_size)
00169                         .pack('v', strlen($filepath)) // length of filename
00170                         .pack('v', 0) // extra field length
00171                         .$filepath
00172                         .$gzdata; // "file data" segment
00173 
00174                 $this->directory .=
00175                         "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00"
00176                         .pack('V', $crc32)
00177                         .pack('V', $compressed_size)
00178                         .pack('V', $uncompressed_size)
00179                         .pack('v', strlen($filepath)) // length of filename
00180                         .pack('v', 0) // extra field length
00181                         .pack('v', 0) // file comment length
00182                         .pack('v', 0) // disk number start
00183                         .pack('v', 0) // internal file attributes
00184                         .pack('V', 32) // external file attributes - 'archive' bit set
00185                         .pack('V', $this->offset) // relative offset of local header
00186                         .$filepath;
00187 
00188                 $this->offset = strlen($this->zipdata);
00189                 $this->entries++;
00190                 $this->file_num++;
00191         }
00192         
00193         // --------------------------------------------------------------------
00194 
00195         /**
00196          * Read the contents of a file and add it to the zip
00197          *
00198          * @access      public
00199          * @return      bool
00200          */     
00201         function read_file($path, $preserve_filepath = FALSE)
00202         {
00203                 if ( ! file_exists($path))
00204                 {
00205                         return FALSE;
00206                 }
00207 
00208                 if (FALSE !== ($data = file_get_contents($path)))
00209                 {
00210                         $name = str_replace("\\", "/", $path);
00211                         
00212                         if ($preserve_filepath === FALSE)
00213                         {
00214                                 $name = preg_replace("|.*/(.+)|", "\\1", $name);
00215                         }
00216 
00217                         $this->add_data($name, $data);
00218                         return TRUE;
00219                 }
00220                 return FALSE;
00221         }
00222 
00223         // ------------------------------------------------------------------------
00224         
00225         /**
00226          * Read a directory and add it to the zip.
00227          *
00228          * This function recursively reads a folder and everything it contains (including
00229          * sub-folders) and creates a zip based on it.  Whatever directory structure
00230          * is in the original file path will be recreated in the zip file.
00231          *
00232          * @access      public
00233          * @param       string  path to source
00234          * @return      bool
00235          */     
00236         function read_dir($path)
00237         {       
00238                 if ($fp = @opendir($path))
00239                 {
00240                         while (FALSE !== ($file = readdir($fp)))
00241                         {
00242                                 if (@is_dir($path.$file) && substr($file, 0, 1) != '.')
00243                                 {                                       
00244                                         $this->read_dir($path.$file."/");
00245                                 }
00246                                 elseif (substr($file, 0, 1) != ".")
00247                                 {
00248                                         if (FALSE !== ($data = file_get_contents($path.$file)))
00249                                         {                                               
00250                                                 $this->add_data(str_replace("\\", "/", $path).$file, $data);
00251                                         }
00252                                 }
00253                         }
00254                         return TRUE;
00255                 }
00256         }
00257 
00258         // --------------------------------------------------------------------
00259 
00260         /**
00261          * Get the Zip file
00262          *
00263          * @access      public
00264          * @return      binary string
00265          */     
00266         function get_zip()
00267         {
00268                 // Is there any data to return?
00269                 if ($this->entries == 0)
00270                 {
00271                         return FALSE;
00272                 }
00273 
00274                 $zip_data = $this->zipdata;
00275                 $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00";
00276                 $zip_data .= pack('v', $this->entries); // total # of entries "on this disk"
00277                 $zip_data .= pack('v', $this->entries); // total # of entries overall
00278                 $zip_data .= pack('V', strlen($this->directory)); // size of central dir
00279                 $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir
00280                 $zip_data .= "\x00\x00"; // .zip file comment length
00281 
00282                 return $zip_data;
00283         }
00284         
00285         // --------------------------------------------------------------------
00286 
00287         /**
00288          * Write File to the specified directory
00289          *
00290          * Lets you write a file
00291          *
00292          * @access      public
00293          * @param       string  the file name
00294          * @return      bool
00295          */     
00296         function archive($filepath)
00297         {
00298                 if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE)))
00299                 {
00300                         return FALSE;
00301                 }
00302 
00303                 flock($fp, LOCK_EX);    
00304                 fwrite($fp, $this->get_zip());
00305                 flock($fp, LOCK_UN);
00306                 fclose($fp);
00307 
00308                 return TRUE;    
00309         }
00310 
00311         // --------------------------------------------------------------------
00312 
00313         /**
00314          * Download
00315          *
00316          * @access      public
00317          * @param       string  the file name
00318          * @param       string  the data to be encoded
00319          * @return      bool
00320          */
00321         function download($filename = 'backup.zip')
00322         {
00323                 if ( ! preg_match("|.+?\.zip$|", $filename))
00324                 {
00325                         $filename .= '.zip';
00326                 }
00327 
00328                 $zip_content =& $this->get_zip();
00329 
00330                 $CI =& get_instance();
00331                 $CI->load->helper('download');
00332 
00333                 force_download($filename, $zip_content);
00334         }
00335 
00336         // --------------------------------------------------------------------
00337 
00338         /**
00339          * Initialize Data
00340          *
00341          * Lets you clear current zip data.  Useful if you need to create
00342          * multiple zips with different data.
00343          *
00344          * @access      public
00345          * @return      void
00346          */             
00347         function clear_data()
00348         {
00349                 $this->zipdata          = '';
00350                 $this->directory        = '';
00351                 $this->entries          = 0;
00352                 $this->file_num         = 0;
00353                 $this->offset           = 0;
00354         }
00355         
00356 }
00357 
00358 /* End of file Zip.php */
00359 /* Location: ./system/libraries/Zip.php */