Класс ZipArchive

(PHP 5 >= 5.2.0, PECL zip >= 1.1.0)

Введение

Файловый архив, сжатый Zip.

Обзор классов

ZipArchive {
/* Свойства */
/* Методы */
bool addEmptyDir ( string $dirname )
bool addFile ( string $filename [, string $localname = NULL [, int $start = 0 [, int $length = 0 ]]] )
bool addFromString ( string $localname , string $contents )
bool addGlob ( string $pattern [, int $flags = 0 [, array $options = array() ]] )
bool addPattern ( string $pattern [, string $path = "." [, array $options = array() ]] )
bool close ( void )
bool deleteIndex ( int $index )
bool deleteName ( string $name )
bool extractTo ( string $destination [, mixed $entries ] )
string getArchiveComment ([ int $flags ] )
string getCommentIndex ( int $index [, int $flags ] )
string getCommentName ( string $name [, int $flags ] )
bool GetExternalAttributesIndex ( int $index , int &$opsys , int &$attr [, int $flags ] )
bool getExternalAttributesName ( string $name , int &$opsys , int &$attr [, int $flags ] )
mixed getFromIndex ( int $index [, int $length = 0 [, int $flags ]] )
mixed getFromName ( string $name [, int $length = 0 [, int $flags ]] )
string getNameIndex ( int $index [, int $flags ] )
string getStatusString ( void )
resource getStream ( string $name )
mixed locateName ( string $name [, int $flags ] )
mixed open ( string $filename [, int $flags ] )
bool renameIndex ( int $index , string $newname )
bool renameName ( string $name , string $newname )
mixed setArchiveComment ( string $comment )
mixed setCommentIndex ( int $index , string $comment )
mixed setCommentName ( string $name , string $comment )
bool setExternalAttributesIndex ( int $index , int $opsys , int $attr [, int $flags ] )
bool setExternalAttributesName ( string $name , int $opsys , int $attr [, int $flags ] )
mixed statIndex ( int $index [, int $flags ] )
mixed statName ( name $name [, int $flags ] )
mixed unchangeAll ( void )
mixed unchangeArchive ( void )
mixed unchangeIndex ( int $index )
mixed unchangeName ( string $name )
}

Свойства

status

Статус Zip-архива

statusSys

Системный статус Zip-архива

numFiles

Количество файлов в архиве

filename

Имя файла в файловой системе

comment

Комментарий к архиву

Содержание

Коментарии

Read a file from an archive to a variable.
A warning is printed automatically in case of a CRC32 mismatch, which we capture, so we can print our own error message.

<?php
$zip 
= new ZipArchive();
if (
$zip->open('archive.zip')) {
 
$fp $zip->getStream('myfile.txt'); //file inside archive
 
if(!$fp)
    die(
"Error: can't get stream to zipped file");
 
$stat $zip->statName('myfile.txt');

 
$buf ""//file buffer
 
ob_start(); //to capture CRC error message
   
while (!feof($fp)) {
     
$buf .= fread($fp2048); //reading more than 2156 bytes seems to disable internal CRC32 verification (bug?)
   
}
   
$s ob_get_contents();
 
ob_end_clean();
  if(
stripos($s"CRC error") != FALSE){
    echo 
'CRC32 mismatch, current ';
   
printf("%08X"crc32($buf)); //current CRC
   
echo ', expected ';
   
printf("%08X"$stat['crc']); //expected CRC
 
}

 
fclose($fp);
 
$zip->close();
 
//Done, unpacked file is stored in $buf
}
?>

To create a corrupt file, change a byte in a zip file using a hex editor.
2010-06-15 18:17:07
http://php5.kiev.ua/manual/ru/class.ziparchive.html
Be wary that there are several algorithms to generate a zip file. I found that Office OpenXML files created with ZipArchive are not recognized by Excel 2007, for example.

You have to use a different class to zip in this case, such as PclZip.
2010-10-05 07:17:12
http://php5.kiev.ua/manual/ru/class.ziparchive.html
The following code can be used to get a list of all the file names in a zip file.

<?php
$za 
= new ZipArchive();

$za->open('theZip.zip');

for( 
$i 0$i $za->numFiles$i++ ){
   
$stat $za->statIndex$i );
   
print_rbasename$stat['name'] ) . PHP_EOL );
}
?>
2011-08-08 16:57:06
http://php5.kiev.ua/manual/ru/class.ziparchive.html
Here is a simple function which zips folders with all sub folders or only a simple file... the $data var can be a string or an array...

<?php
public function un_zip($data,$arcpf,$mode='zip',$obj=''){
       
$absoluterpfad 'YOUR_BASE_PATH';
       
$arcpf $absoluterpfad.DS.$arcpf;
        if(
is_object($obj)==false){
             
$archiv = new ZipArchive();
             
$archiv->open($arcpf,ZipArchive::CREATE);
        }else{
$archiv =& $obj;}
        if(
$mode=='zip'){
           if(
is_array($data)==true){
                 foreach(
$data as $dtmp){
                     
$archiv =& un_zip($dtmp,$arcpf,'zip',&$archiv);
                 }
           }else{
            if(
is_dir($data)==true){
                   
$archiv->addEmptyDir(str_replace($absoluterpfad.DS,'',$data));
                 
$files scandir($data);
               
$bad = array('.','..');
               
$files array_diff($files,$bad);
               foreach(
$files as $ftmp){
                   if(
is_dir($data.DS.$ftmp)==true){
                       
$archiv->addEmptyDir(str_replace($absoluterpfad.DS,'',$data.'/'.$ftmp));
                       
$archiv =& un_zip($data.DS.$ftmp,$arcpf,'zip',&$archiv);
                   }elseif(
is_file($data.DS.$ftmp)==true){
                   
$archiv->addFile($data.DS.$ftmp,str_replace($absoluterpfad.DS,'',$data.'/'.$ftmp));
                   }
                 }
            }elseif(
is_file($data)==true){$archiv->addFile($data,str_replace($absoluterpfad.DS,'',$data));}
           }
        }
        if(
is_object($obj)==false){$archiv->close();}
        else{return 
$archiv;}
        if(
$mode=='unzip'){$archiv->extractTo($data);}
    }
?>
2011-10-06 16:30:25
http://php5.kiev.ua/manual/ru/class.ziparchive.html
There is a usefull function to get the ZipArchive status as a human readable string :

<?php
function ZipStatusString$status )
{
    switch( (int) 
$status )
    {
        case 
ZipArchive::ER_OK           : return 'N No error';
        case 
ZipArchive::ER_MULTIDISK    : return 'N Multi-disk zip archives not supported';
        case 
ZipArchive::ER_RENAME       : return 'S Renaming temporary file failed';
        case 
ZipArchive::ER_CLOSE        : return 'S Closing zip archive failed';
        case 
ZipArchive::ER_SEEK         : return 'S Seek error';
        case 
ZipArchive::ER_READ         : return 'S Read error';
        case 
ZipArchive::ER_WRITE        : return 'S Write error';
        case 
ZipArchive::ER_CRC          : return 'N CRC error';
        case 
ZipArchive::ER_ZIPCLOSED    : return 'N Containing zip archive was closed';
        case 
ZipArchive::ER_NOENT        : return 'N No such file';
        case 
ZipArchive::ER_EXISTS       : return 'N File already exists';
        case 
ZipArchive::ER_OPEN         : return 'S Can\'t open file';
        case 
ZipArchive::ER_TMPOPEN      : return 'S Failure to create temporary file';
        case 
ZipArchive::ER_ZLIB         : return 'Z Zlib error';
        case 
ZipArchive::ER_MEMORY       : return 'N Malloc failure';
        case 
ZipArchive::ER_CHANGED      : return 'N Entry has been changed';
        case 
ZipArchive::ER_COMPNOTSUPP  : return 'N Compression method not supported';
        case 
ZipArchive::ER_EOF          : return 'N Premature EOF';
        case 
ZipArchive::ER_INVAL        : return 'N Invalid argument';
        case 
ZipArchive::ER_NOZIP        : return 'N Not a zip archive';
        case 
ZipArchive::ER_INTERNAL     : return 'N Internal error';
        case 
ZipArchive::ER_INCONS       : return 'N Zip archive inconsistent';
        case 
ZipArchive::ER_REMOVE       : return 'S Can\'t remove file';
        case 
ZipArchive::ER_DELETED      : return 'N Entry has been deleted';
       
        default: return 
sprintf('Unknown status %s'$status );
    }
}

?>
2012-05-09 23:21:32
http://php5.kiev.ua/manual/ru/class.ziparchive.html
Important: Due to the natural file size limit of 4GB (~3,6GB to be correct) of zip files, this class will generate corrupt files of the result is larger than 4 GB. Using tar.gz is a proper alternative.
2012-07-14 14:21:48
http://php5.kiev.ua/manual/ru/class.ziparchive.html
Hi there.
I just wrote a little function to zip a whole folder while maintaining the dir-structure. I hope it might help someone.

<?php
function folderToZip($folder, &$zipFile$subfolder null) {
    if (
$zipFile == null) {
       
// no resource given, exit
       
return false;
    }
   
// we check if $folder has a slash at its end, if not, we append one
   
$folder .= end(str_split($folder)) == "/" "" "/";
   
$subfolder .= end(str_split($subfolder)) == "/" "" "/";
   
// we start by going through all files in $folder
   
$handle opendir($folder);
    while (
$f readdir($handle)) {
        if (
$f != "." && $f != "..") {
            if (
is_file($folder $f)) {
               
// if we find a file, store it
                // if we have a subfolder, store it there
               
if ($subfolder != null)
                   
$zipFile->addFile($folder $f$subfolder $f);
                else
                   
$zipFile->addFile($folder $f);
            } elseif (
is_dir($folder $f)) {
               
// if we find a folder, create a folder in the zip 
               
$zipFile->addEmptyDir($f);
               
// and call the function again
               
folderToZip($folder $f$zipFile$f);
            }
        }
    }
}
?>

Use it like this:
<?php
$z 
= new ZipArchive();
$z->open("test.zip"ZIPARCHIVE::CREATE);
folderToZip("storeThisFolder"$z);
$z->close();
?>

Have a good day!
2012-08-02 13:44:42
http://php5.kiev.ua/manual/ru/class.ziparchive.html
Zip a folder (include itself).
Usage:
  HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');

<?php
class HZip
{
 
/**
   * Add files and sub-directories in a folder to zip file.
   * @param string $folder
   * @param ZipArchive $zipFile
   * @param int $exclusiveLength Number of text to be exclusived from the file path.
   */
 
private static function folderToZip($folder, &$zipFile$exclusiveLength) {
   
$handle opendir($folder);
    while (
false !== $f readdir($handle)) {
      if (
$f != '.' && $f != '..') {
       
$filePath "$folder/$f";
       
// Remove prefix from file path before add to zip.
       
$localPath substr($filePath$exclusiveLength);
        if (
is_file($filePath)) {
         
$zipFile->addFile($filePath$localPath);
        } elseif (
is_dir($filePath)) {
         
// Add sub-directory.
         
$zipFile->addEmptyDir($localPath);
         
self::folderToZip($filePath$zipFile$exclusiveLength);
        }
      }
    }
   
closedir($handle);
  }

 
/**
   * Zip a folder (include itself).
   * Usage:
   *   HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
   *
   * @param string $sourcePath Path of directory to be zip.
   * @param string $outZipPath Path of output zip file.
   */
 
public static function zipDir($sourcePath$outZipPath)
  {
   
$pathInfo pathInfo($sourcePath);
   
$parentPath $pathInfo['dirname'];
   
$dirName $pathInfo['basename'];

   
$z = new ZipArchive();
   
$z->open($outZipPathZIPARCHIVE::CREATE);
   
$z->addEmptyDir($dirName);
   
self::folderToZip($sourcePath$zstrlen("$parentPath/"));
   
$z->close();
  }
}
?>
2012-11-28 05:48:10
http://php5.kiev.ua/manual/ru/class.ziparchive.html
Simple class xZip to zip big folders into multiple parts and unzip multi zip files at once.

<?php
class xZip {
    public function 
__construct() {}
    private function 
_rglobRead($source, &$array = array()) {
        if (!
$source || trim($source) == "") {
           
$source ".";
        }
        foreach ((array) 
glob($source "/*/") as $key => $value) {
           
$this->_rglobRead(str_replace("//""/"$value), $array);
        }
   
        foreach ((array) 
glob($source "*.*") as $key => $value) {
           
$array[] = str_replace("//""/"$value);
        }
    }
    private function 
_zip($array$part$destination) {
       
$zip = new ZipArchive;
        @
mkdir($destination0777true);
   
        if (
$zip->open(str_replace("//""/""{$destination}/partz{$part}.zip"), ZipArchive::CREATE)) {
            foreach ((array) 
$array as $key => $value) {
               
$zip->addFile($valuestr_replace(array("../""./"), NULL$value));
            }
           
$zip->close();
        }
    }
    public function 
zip($limit 500$source NULL$destination "./") {
        if (!
$destination || trim($destination) == "") {
           
$destination "./";
        }
   
       
$this->_rglobRead($source$input);
       
$maxinput count($input);
       
$splitinto = (($maxinput $limit) > round($maxinput $limit0)) ? round($maxinput $limit0) + round($maxinput $limit0);
   
        for(
$i 0$i $splitinto$i ++) {
           
$this->_zip(array_slice($input, ($i $limit), $limittrue), $i$destination);
        }
       
        unset(
$input);
        return;
    }
    public function 
unzip($source$destination) {
        @
mkdir($destination0777true);
   
        foreach ((array) 
glob($source "/*.zip") as $key => $value) {
           
$zip = new ZipArchive;
            if (
$zip->open(str_replace("//""/"$value)) === true) {
               
$zip->extractTo($destination);
               
$zip->close();
            }
        }
    }
   
    public function 
__destruct() {}
}

//$zip = new xZip;
//$zip->zip(500, "images/", "images_zip/");
//$zip->unzip("images_zip/", "images/");
?>
2013-05-16 11:23:26
http://php5.kiev.ua/manual/ru/class.ziparchive.html
You can check general purpose flag to test if the zip file is encrypted. Example function below.

<?php

/**
 * Check if the file is encrypted
 * 
 * Notice: if file doesn't exists or cannot be opened, function
 * also return false.
 * 
 * @param string $pathToArchive
 * @return boolean return true if file is encrypted
 */
function isEncryptedZip$pathToArchive ) {
   
$fp = @fopen$pathToArchive'r' );
   
$encrypted false;
    if ( 
$fp && fseek$fp) == ) {
       
$string fread$fp);
        if ( 
false !== $string ) {
           
$data unpack("vgeneral"$string);
           
$encrypted $data'general' ] & 0x01 true false;
        }
       
fclose$fp );
    }
    return 
$encrypted;
}
2014-01-17 18:58:27
http://php5.kiev.ua/manual/ru/class.ziparchive.html
A way of zipping files and downloading them thereafter:
<?php

 $files 
= array('image.jpeg','text.txt','music.wav');
$zipname 'enter_any_name_for_the_zipped_file.zip';
$zip = new ZipArchive;
$zip->open($zipnameZipArchive::CREATE);
foreach (
$files as $file) {
 
$zip->addFile($file);
}
$zip->close();

///Then download the zipped file.
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zipname);
header('Content-Length: ' filesize($zipname));
readfile($zipname);

?>
2014-12-16 13:46:16
http://php5.kiev.ua/manual/ru/class.ziparchive.html
There is a limit withing PHP 5.3.3 (which seems to have been addressed in later versions; 5.3.29 seems ok on a different server).

If you try to open a zip file with more than 65,535 files in it (in my case it had 237,942 files) then you cannot access the later files. The numFiles property only reports the first 65k files.
2015-03-24 16:59:24
http://php5.kiev.ua/manual/ru/class.ziparchive.html
<?php
   
//use bzip2 + ZipArchive to reduce file size of your zip archives.
   
$zip = new ZipArchive;
   
$zip->open('i.zip',ZIPARCHIVE::CREATE|ZIPARCHIVE::OVERWRITE);
   
$file='wuxiancheng.cn.sql';
   
$bzFilename $file.'.bz2';
   
$sql file_get_contents($file);
   
$sql bzcompress($sql,9);
   
$zip->addFromString($bzFilename,$sql);
   
$zip->setArchiveComment('zipped on '.date('Y-M-d'));
?>
2015-11-06 22:58:43
http://php5.kiev.ua/manual/ru/class.ziparchive.html
With PHP 5.6+, you may come up with theses errors.

Warning: Unknown: Cannot destroy the zip context in Unknown on line 0

Warning: ZipArchive::close(): Can't remove file: No such file or directory in xxxx.php on line xx

Examples

Warning: Unknown: Cannot destroy the zip context in Unknown on line 0

<?php         
    $za 
= new ZipArchive;
   
$za->open('51-n.com.zip',ZipArchive::CREATE|ZipArchive::OVERWRITE);
?>

Warning: ZipArchive::close(): Can't remove file: No such file or directory in xxxx.php on line xx

<?php         
    $za 
= new ZipArchive;
   
$za->open('51-n.com.zip',ZipArchive::CREATE|ZipArchive::OVERWRITE);
   
$za->close();
?>

It happens when the zip archive is empty.
Your zip archive will not be saved on disk unless it has at least one file. What's more, when ZipArchive::OVERWRITE is applied, if there exists a file with the same name, it will be removed after ZipArchive::open() is called.

So, don't forget to put at least one file to your zip archive.

<?php         
    $za 
= new ZipArchive;
   
$za->open('51-n.com.zip',ZipArchive::CREATE|ZipArchive::OVERWRITE);
   
$za->addFromString('wuxiancheng.cn.txt','yes');
   
$za->close();
?>
2015-11-28 20:14:56
http://php5.kiev.ua/manual/ru/class.ziparchive.html
Автор:
How to detect corrupt files with CRC mismatch:

Creating a corrupt archive for testing is simple - zip some files and change a byte with a hex editor in the resulting ZIP file. Now you can test the file with a ZIP application to learn which file inside the archive is corrupt.

ZipArchive seems unable to detect broken files. ZipArchive::CHECKCONS doesn't help, only if it's not a ZIP file at all. It happily decompressed corrupt files in my tests and the user downloading the data is not informed.

You can simply verify on the server for smaller files:
<?php
$maxsize 
1024*1024;
$z = new ZipArchive;
$r $z->open("foo.zip"ZipArchive::CHECKCONS);
if(
$r !== TRUE)
  die(
'ZIP error when trying to open "foo.zip": '.$r);

$stat $z->statName("mybrokenfile.txt");
if(
$stat['size'] > $maxsize)
  die(
'File too large, decompression denied');
$s $z->getStream($file);
$data stream_get_contents($s$maxsize);
fclose($s);
if(
$stat['crc'] != crc32($data))
  die(
'File is corrupt!');
//echo 'File is valid';

//you may send the file to the client now if you didn't output anything before
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="mybrokenfile.txt"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' $stat['size']);
ob_clean();
echo 
$data;
$z->close();
?>
 
If the file shall not be fully decompressed on the server but decompressed while streaming to the client due to it's size, the file transfer already startet and printing an error message later doesn't work. Maybe the best way would be to interrupt the connection before closing the file transfer. The client should be able to detect this as corrupt download.
On the server side a function is needed, that can calculate the CRC32 on streamed data stepwise.
2016-08-17 08:39:14
http://php5.kiev.ua/manual/ru/class.ziparchive.html
A modern way to zip a folder recursivly is using the DirectoryIterator. I use this little class:

<?php
class MakeZip
{
  private 
ZipArchive $zipArchive;
  private 
int $startPathLength// chars to remove from the start for the stored entity

 
public function __construct(
   
string $zipArchivename,
    public readonly 
string $startPath,
    public readonly 
mixed $convert_function,
  )
  {
   
$this->zipArchive = new \ZipArchive;
   
$this->zipArchive->open($zipArchivenameZipArchive::CREATE);
   
$this->startPathLength strlen($this-> startPath);

   
$this-> zipDir($startPath);
  }
  public function 
__destruct()
  {
   
$this-> zipArchive-> close();
  }

 
/**
   * Add files and sub-directories in a folder to zip file.
   * @param string $folder
   * @param ZipArchive $zipFile
   * @param int $exclusiveLength Number of text to be exclusived from the file path.
   */
 
private function zipDir($folder)
  {
    echo 
$folder '<br>' PHP_EOL;
    foreach (new 
\DirectoryIterator($folder) as $f) {
      if (
$f->isDot())
        continue; 
//skip . ..
     
if ($f->isDir()) {
        if( 
$f->getExtension() === 'git') continue; // skip .git folder
       
$this-> zipArchive-> addEmptyDir($f->getPathname());
       
$this-> zipDir($f->getPathname());

        continue;
      }
      if (
$f->isFile()) {
        if (
$f->getBasename() === basename(__FILE__)) continue; // skip self 
       
if ($f->getExtension() === 'zip') continue; // skip ZIP files

       
$this-> zipArchive ->addFilesubstr($f-> getPathname(), $this-> startPathLength) ); // remove './'

       
continue;
      }

    }
  }
}
?>

Which can be used thusly:
<?php
  $host 
str_replace('.''_'$_SERVER['HTTP_HOST']);
 
$date date('Ymd-His');

 
$zip = new \MakeZip("./archiv-$host-$date.zip"'./'$convert_function);
  unset(
$zip);
?>

Add encryption or other features in the constructor.
2023-05-20 20:00:33
http://php5.kiev.ua/manual/ru/class.ziparchive.html

    Поддержать сайт на родительском проекте КГБ