PHP Managing zip files with ZipArchive
@shakedko
IF AN EXPERT SAYS IT CAN'T BE DONE GET ANOTHER EXPERT.
- DAVID BEN-GURION

PHP Managing zip files with ZipArchive

Requirements

  1. PHP 5.2 or greater (would be a bit sad to know that someone is still using PHP 4) 
  2. Upgrade PEAR to latest version 
  3. Upgrade PECL to latest version 
  4. Installing PHP ZipArchive library by using PECL's zip package

Installation 

I will use pecl to install ZipArchive PHP library.

Open terminal and execute pecl install zip

Updating your php.ini:

  1. Linux: find your php.ini file and add "extension=zip.so"
  2. Windows: same as one, just add "extension=zip.dll

Restart httpd\apache 

  1. service httpd restart
  2. apache2ctl restart
  3. or any other way that you like

Usage Example

There are many different ways to use and build Zip helpers. I will just demonstrate an example for using ZipArchive. 

 Pastebin: http://pastebin.com/J2M0jjQG

 Inline... 

<?php
/**
 * ZipArchiveHelper class
*/
class ZipArchiveHelper {
/**
 * @var ZipArchive $_zipArchive
*/
private $_zipArchive;
/**
 * @var array $_excludeList
*/
private $_excludeList;
const TIMEOUT = 5000;
/**
 * Get files list by path
 * @param string $sourcePath
 * @return RecursiveIteratorIterator
*/
private function _getFilesList($sourcePath){
    $dirlist = new RecursiveDirectoryIterator($sourcePath);
    $filelist = new RecursiveIteratorIterator($dirlist);
    return $filelist;
}
/**
 * Create Zip File
 * @param string $zipFileName
 * @param string $sourcePath
 * @param array[optional] $excludeList
 * @return string|false
*/
public function createZipFile($zipFileName,$sourcePath,array $excludeList = array()){
        //Set PHP timeout
        ini_set('max_execution_time', self::TIMEOUT);
        //set exclude list
        $this----->_excludeList = $excludeList;
        //set new zip archive object
        $this->_setZipArchive();
        //generate new file
        $this->_newZipFile($zipFileName);
        //compress
        $this->_compress($sourcePath);
        //get status
        $retValue = $this->_zipArchive->getStatusString();
        //close zip archive manager
        $this->_zipArchive->close();
        return $retValue;
}
/**
 * Set new ZipArchive instance
 * Seperated and protected for future unit tests
 * @return void
*/
protected function _setZipArchive(){
    $this->_zipArchive = new ZipArchive;
}
/**
 * Open new|existing zip file
 * @param int $type -   Overwrite by default. (you may use other options, e.g ZipArchive::CREATE)
 * @throw Exception
 * @return void
*/
protected function _newZipFile($zipFileName,$type=ZipArchive::OVERWRITE){
    if ($this->_zipArchive->open("$zipFileName", $type) !== TRUE) {
            throw new Exception("Could not open archive");
    }
}
/**
 * Validate file by checking if exists and not excluded type
 * @param string $fileName
 * @return bool
*/
private function _isValidFile($fileName){
    if (!is_file($fileName)){
        return false ;
    }
    foreach($this->_excludeList as $toExclude){
        if (strpos($fileName,$toExclude) !== false){
            return false;
        }
    }
    return true;
}
/**
 * Compress to ZIP file
 * @return bool
*/
private function _compress($sourcePath){
    //get file list
    $filelist = $this->_getFilesList($sourcePath);
    //add files to new zip file
    foreach ($filelist as $fileName=>$value) {
        if(!$this->_isValidFile($fileName)){
            continue ;
        }
        //you should use sprintf for this to work on both *NIX and Windows OS
        //note that str_replace is just for my example, you don't really have to use it.
        $value = sprintf("%s",str_replace($sourcePath,'',$value));
        if (!$this->_zipArchive->addFile($fileName,$value)){
             throw new Exception("ERROR: Could not add file: $fileName");
        }
    }
    return true;
  }
}

And:

<?php
$zipHelper = new ZipArchiveHelper();
try {
    var_dump($zipHelper->createZipFile(
        'shakedos.zip',
        '/tmp/zend_debug',
        array('.svn')
    ));
} catch (Exception $e){
    var_dump($e->getMessage());
}

Known Issues

  1. Did you define your PHP extension directory correct ? 
  2. Windows and *NIX system works different by using the opposite slash  "/"(*NIX) or "\"(Windows) - my solution is to use sprintf()   http://stackoverflow.com/questions/4620205/php-ziparchive-corrupt-in-windows
  3. Downloading the script sometimes requires to send some different header, the following example was taken from the stackoverflow

<?php
header("Cache-Control: public");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
//header("Content-Description: File Transfer");
//header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=\"YOUR_FILE_NAME_HERE.zip\"");
//header("Content-Transfer-Encoding: binary");
header("Content-length: " . filesize($filename));

Hope I helped, 

Work In Progress 🚧
Discipline