PHP Conference Nagoya 2025

The ZipArchive class

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

Introduction

A file archive, compressed with Zip.

Class synopsis

class ZipArchive implements Countable {
/* Constants */
public const int CREATE;
public const int EXCL;
public const int CHECKCONS;
public const int OVERWRITE;
public const int RDONLY;
public const int FL_NOCASE;
public const int FL_NODIR;
public const int FL_COMPRESSED;
public const int FL_UNCHANGED;
public const int FL_RECOMPRESS;
public const int FL_ENCRYPTED;
public const int FL_OVERWRITE;
public const int FL_LOCAL;
public const int FL_CENTRAL;
public const int FL_ENC_GUESS;
public const int FL_ENC_RAW;
public const int FL_ENC_STRICT;
public const int FL_ENC_UTF_8;
public const int FL_ENC_CP437;
public const int FL_OPEN_FILE_NOW;
public const int CM_DEFAULT;
public const int CM_STORE;
public const int CM_SHRINK;
public const int CM_REDUCE_1;
public const int CM_REDUCE_2;
public const int CM_REDUCE_3;
public const int CM_REDUCE_4;
public const int CM_IMPLODE;
public const int CM_DEFLATE;
public const int CM_DEFLATE64;
public const int CM_PKWARE_IMPLODE;
public const int CM_BZIP2;
public const int CM_LZMA;
public const int CM_LZMA2;
public const int CM_ZSTD;
public const int CM_XZ;
public const int CM_TERSE;
public const int CM_LZ77;
public const int CM_WAVPACK;
public const int CM_PPMD;
public const int ER_OK;
public const int ER_MULTIDISK;
public const int ER_RENAME;
public const int ER_CLOSE;
public const int ER_SEEK;
public const int ER_READ;
public const int ER_WRITE;
public const int ER_CRC;
public const int ER_ZIPCLOSED;
public const int ER_NOENT;
public const int ER_EXISTS;
public const int ER_OPEN;
public const int ER_TMPOPEN;
public const int ER_ZLIB;
public const int ER_MEMORY;
public const int ER_CHANGED;
public const int ER_COMPNOTSUPP;
public const int ER_EOF;
public const int ER_INVAL;
public const int ER_NOZIP;
public const int ER_INTERNAL;
public const int ER_INCONS;
public const int ER_REMOVE;
public const int ER_DELETED;
public const int ER_ENCRNOTSUPP;
public const int ER_RDONLY;
public const int ER_NOPASSWD;
public const int ER_WRONGPASSWD;
public const int ER_OPNOTSUPP;
public const int ER_INUSE;
public const int ER_TELL;
public const int ER_COMPRESSED_DATA;
public const int ER_CANCELLED;
public const int ER_DATA_LENGTH;
public const int ER_NOT_ALLOWED;
public const int ER_TRUNCATED_ZIP;
public const int AFL_RDONLY;
public const int AFL_IS_TORRENTZIP;
public const int AFL_WANT_TORRENTZIP;
public const int OPSYS_DOS;
public const int OPSYS_AMIGA;
public const int OPSYS_OPENVMS;
public const int OPSYS_UNIX;
public const int OPSYS_VM_CMS;
public const int OPSYS_ATARI_ST;
public const int OPSYS_OS_2;
public const int OPSYS_MACINTOSH;
public const int OPSYS_Z_SYSTEM;
public const int OPSYS_CPM;
public const int OPSYS_WINDOWS_NTFS;
public const int OPSYS_MVS;
public const int OPSYS_VSE;
public const int OPSYS_ACORN_RISC;
public const int OPSYS_VFAT;
public const int OPSYS_ALTERNATE_MVS;
public const int OPSYS_BEOS;
public const int OPSYS_TANDEM;
public const int OPSYS_OS_400;
public const int OPSYS_OS_X;
public const int OPSYS_DEFAULT;
public const int EM_NONE;
public const int EM_TRAD_PKWARE;
public const int EM_AES_128;
public const int EM_AES_192;
public const int EM_AES_256;
public const int EM_UNKNOWN;
public const string LIBZIP_VERSION;
public const int LENGTH_TO_END;
public const int LENGTH_UNCHECKED;
/* Properties */
public readonly int $lastId;
public readonly int $status;
public readonly int $statusSys;
public readonly int $numFiles;
public readonly string $filename;
public readonly string $comment;
/* Methods */
public addEmptyDir(string $dirname, int $flags = 0): bool
public addFile(
    string $filepath,
    string $entryname = "",
    int $start = 0,
    int $length = ZipArchive::LENGTH_TO_END,
    int $flags = ZipArchive::FL_OVERWRITE
): bool
public addFromString(string $name, string $content, int $flags = ZipArchive::FL_OVERWRITE): bool
public addGlob(string $pattern, int $flags = 0, array $options = []): array|false
public addPattern(string $pattern, string $path = ".", array $options = []): array|false
public clearError(): void
public close(): bool
public count(): int
public deleteIndex(int $index): bool
public deleteName(string $name): bool
public extractTo(string $pathto, array|string|null $files = null): bool
public getArchiveComment(int $flags = 0): string|false
public getArchiveFlag(int $flag, int $flags = 0): int
public getCommentIndex(int $index, int $flags = 0): string|false
public getCommentName(string $name, int $flags = 0): string|false
public getExternalAttributesIndex(
    int $index,
    int &$opsys,
    int &$attr,
    int $flags = 0
): bool
public getExternalAttributesName(
    string $name,
    int &$opsys,
    int &$attr,
    int $flags = 0
): bool
public getFromIndex(int $index, int $len = 0, int $flags = 0): string|false
public getFromName(string $name, int $len = 0, int $flags = 0): string|false
public getNameIndex(int $index, int $flags = 0): string|false
public getStreamIndex(int $index, int $flags = 0): resource|false
public getStreamName(string $name, int $flags = 0): resource|false
public static isCompressionMethodSupported(int $method, bool $enc = true): bool
public static isEncryptionMethodSupported(int $method, bool $enc = true): bool
public locateName(string $name, int $flags = 0): int|false
public open(string $filename, int $flags = 0): bool|int
public registerProgressCallback(float $rate, callable $callback): bool
public renameIndex(int $index, string $new_name): bool
public renameName(string $name, string $new_name): bool
public replaceFile(
    string $filepath,
    int $index,
    int $start = 0,
    int $length = ZipArchive::LENGTH_TO_END,
    int $flags = 0
): bool
public setArchiveComment(string $comment): bool
public setArchiveFlag(int $flag, int $value): bool
public setCommentIndex(int $index, string $comment): bool
public setCommentName(string $name, string $comment): bool
public setCompressionIndex(int $index, int $method, int $compflags = 0): bool
public setCompressionName(string $name, int $method, int $compflags = 0): bool
public setEncryptionIndex(int $index, int $method, #[\SensitiveParameter] ?string $password = null): bool
public setEncryptionName(string $name, int $method, #[\SensitiveParameter] ?string $password = null): bool
public setExternalAttributesIndex(
    int $index,
    int $opsys,
    int $attr,
    int $flags = 0
): bool
public setExternalAttributesName(
    string $name,
    int $opsys,
    int $attr,
    int $flags = 0
): bool
public setMtimeIndex(int $index, int $timestamp, int $flags = 0): bool
public setMtimeName(string $name, int $timestamp, int $flags = 0): bool
public statIndex(int $index, int $flags = 0): array|false
public statName(string $name, int $flags = 0): array|false
public unchangeAll(): bool
public unchangeIndex(int $index): bool
public unchangeName(string $name): bool
}

Properties

lastId

Index value of last added entry (file or directory). Available as of PHP 8.0.0 and PECL zip 1.18.0.

status

Status of the Zip Archive. Available for closed archive, as of PHP 8.0.0 and PECL zip 1.18.0.

statusSys

System status of the Zip Archive. Available for closed archive, as of PHP 8.0.0 and PECL zip 1.18.0.

numFiles

Number of files in archive

filename

File name in the file system

comment

Comment for the archive

Table of Contents

add a note

User Contributed Notes 15 notes

up
58
umbalaconmeogia at NOSPAM dot gmail dot com
12 years ago
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($outZipPath, ZIPARCHIVE::CREATE);
$z->addEmptyDir($dirName);
self::folderToZip($sourcePath, $z, strlen("$parentPath/"));
$z->close();
}
}
?>
up
18
ohcc at 163 dot com
9 years ago
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();
?>
up
2
theking2(at)king.ma
1 year ago
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($zipArchivename, ZipArchive::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 ->addFile( substr($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.
up
9
AshleyDambra at live dot com
11 years ago
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($destination, 0777, true);

if (
$zip->open(str_replace("//", "/", "{$destination}/partz{$part}.zip"), ZipArchive::CREATE)) {
foreach ((array)
$array as $key => $value) {
$zip->addFile($value, str_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 / $limit, 0)) ? round($maxinput / $limit, 0) + 1 : round($maxinput / $limit, 0);

for(
$i = 0; $i < $splitinto; $i ++) {
$this->_zip(array_slice($input, ($i * $limit), $limit, true), $i, $destination);
}

unset(
$input);
return;
}
public function
unzip($source, $destination) {
@
mkdir($destination, 0777, true);

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/");
?>
up
10
Jerry dot Saravia at emc dot com
13 years ago
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_r( basename( $stat['name'] ) . PHP_EOL );
}
?>
up
13
ebestwebmaster at gmail dot com
9 years ago
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($zipname, ZipArchive::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);

?>
up
4
hardcorevenom at gmx dot com
14 years ago
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($fp, 2048); //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.
up
8
bruno dot vibert at bonobox dot fr
12 years ago
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 );
}
}

?>
up
4
webmaster at sebastiangrinke dot info
13 years ago
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);}
}
?>
up
4
nick at fullfatthings dot com
9 years ago
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.
up
3
niklas dot schumi at NOSPAM dot googlemail dot com
12 years ago
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!
up
1
panique at web dot de
12 years ago
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.
up
0
ohcc at 163 dot com
9 years ago
<?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'));
?>
up
-2
piotr dot stop dot spam at gmail dot com
10 years ago
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, 6 ) == 0 ) {
$string = fread( $fp, 2 );
if (
false !== $string ) {
$data = unpack("vgeneral", $string);
$encrypted = $data[ 'general' ] & 0x01 ? true : false;
}
fclose( $fp );
}
return
$encrypted;
}
up
-4
h-fate at gmx dot net
14 years ago
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.
To Top