<?php /** * * This class implements PHP's zip module. It is usefull if zip module is not available. * Requires zip and unzip binaries. * * @author vadik56 * SVN: $Id: ZipArchive.php 2 2009-03-06 00:52:03Z vadik56 $ */ class ZipArchive { const CREATE = 1; const EXCL = 2; const CHECKCONS = 4; const OVERWRITE = 8; const FL_NOCASE = 1; const FL_NODIR = 2; const FL_COMPRESSED = 4; const FL_UNCHANGED = 8; const CM_DEFAULT = -1; const CM_STORE = 0; const CM_SHRINK = 1; const CM_REDUCE_1 = 2; const CM_REDUCE_2 = 3; const CM_REDUCE_3 = 4; const CM_REDUCE_4 = 5; const CM_IMPLODE = 6; const CM_DEFLATE = 8; const CM_DEFLATE64 = 9; const CM_PKWARE_IMPLODE = 10; const ER_OK = 0; const ER_MULTIDISK = 1; const ER_RENAME = 2; const ER_CLOSE = 3; const ER_SEEK = 4; const ER_READ = 5; const ER_WRITE = 6; const ER_CRC = 7; const ER_ZIPCLOSED = 8; const ER_NOENT = 9; const ER_EXISTS = 10; const ER_OPEN = 11; const ER_TMPOPEN = 12; const ER_ZLIB = 13; const ER_MEMORY = 14; const ER_CHANGED = 15; const ER_COMPNOTSUPP = 16; const ER_EOF = 17; const ER_INVAL = 18; const ER_NOZIP = 19; const ER_INTERNAL = 20; const ER_INCONS = 21; const ER_REMOVE = 22; const ER_DELETED = 23; private static $unzip_exec="unzip"; //path to unzip executable private static $zip_exec="zip"; //path to zip executable /** * Absolute path to requested zip file. * * @var string */ private $zipFilePath; /** * Directory containing temporary zip directory structure(absolute path). * It should not contain trailing slash. * * @var string */ private $tempDirPath; private $zip_is_open=false; private $fileIndex = array(); public static function setup(){ if( defined(ZIP_MODULE_IMPLEMENTATION_PATH) ){ $possible_zip_locations = explode(":", ZIP_MODULE_IMPLEMENTATION_PATH); //try to find zip executable foreach($possible_zip_locations as $loc){ if( is_executable($loc.DIRECTORY_SEPARATOR."zip") ){ self::$zip_exec = $loc.DIRECTORY_SEPARATOR."zip"; }else if( is_executable($loc.DIRECTORY_SEPARATOR."unzip") ){ self::$unzip_exec = $loc.DIRECTORY_SEPARATOR."unzip"; } } } } /** * Open a ZIP file archive * @link http://php.net/manual/en/function.ziparchive-open.php * @param filename string <p> * The file name of the ZIP archive to open. * </p> * @param flags int[optional] <p> * The mode to use to open the archive. * <p> * ZIPARCHIVE::OVERWRITE * </p> * @return mixed Error codes * <p> * Returns true on success or the error code. * <p> * ZIPARCHIVE::ER_EXISTS * </p> * <p> * ZIPARCHIVE::ER_INCONS * </p> * <p> * ZIPARCHIVE::ER_INVAL * </p> * <p> * ZIPARCHIVE::ER_MEMORY * </p> * <p> * ZIPARCHIVE::ER_NOENT * </p> * <p> * ZIPARCHIVE::ER_NOZIP * </p> * <p> * ZIPARCHIVE::ER_OPEN * </p> * <p> * ZIPARCHIVE::ER_READ * </p> * <p> * ZIPARCHIVE::ER_SEEK * </p> * </p> */ public function open ($filename, $flags = null) { if( is_dir($filename) ){ return self::ER_NOZIP; } //if zip file exists already exists if( is_file($filename) ){ if( !is_readable($filename) ){ return self::ER_READ; } //if user asked for error then return it if( $flags & self::EXCL ){ return self::ER_EXISTS; } //if need to create new file but overwrite flag is not defined if(($flags & self::CREATE) && !($flags & self::OVERWRITE) ){ return self::ER_EXISTS; } } //if overwrite then remove file if($flags & self::OVERWRITE){ if( is_file($filename) ){ if ( !(@unlink($filename)) ){ return self::ER_READ; } } } //create/extract directory layout in temporary folder //create temp dir $tempDir = sys_get_temp_dir(); if( $tempDir[strlen($tempDir)-1] != DIRECTORY_SEPARATOR ){ $tempDir = $tempDir . DIRECTORY_SEPARATOR; } $this->tempDirPath = $tempDir.__CLASS__. uniqid(); if( @mkdir($this->tempDirPath) !== true ) { return self::ER_OPEN; } if( is_file($filename) ){ //extract zip file to tempDir if( !$this->__extractTo($filename,$this->tempDirPath) ){ return self::ER_OPEN; } } if($filename[0] == '/'){ $this->zipFilePath = $filename; }else{ $this->zipFilePath = getcwd() .DIRECTORY_SEPARATOR. $filename; } $this->zip_is_open = true; return true; } /** * Close the active archive (opened or newly created) * @link http://php.net/manual/en/function.ziparchive-close.php * @return bool Returns true on success or false on failure. */ public function close () { if(!$this->zip_is_open){ return false; } if( strtolower(substr($this->zipFilePath, strlen($this->zipFilePath)-4)) == ".zip" ){ //if $this->zipFilePath has .zip in the end then use this path for zip $target_zip_file = $this->zipFilePath; }else{ //else use {$this->tempDirPath}.zip as zip file and then move it to final location $target_zip_file = $this->tempDirPath . ".zip"; } $currentWorkingDir = getcwd(); if( @chdir($this->tempDirPath) !== True){ //unable to chdir to temp directory return false; } exec(self::$zip_exec." -m -r {$target_zip_file} * 2>&1", $output, $return_value); chdir($currentWorkingDir); if($target_zip_file != $this->zipFilePath){ if( @rename($target_zip_file, $this->zipFilePath) !== true){ //if unable to copy zip to final destination then return false return false; } } if($return_value != 0){ return false; } //remove trailing tempdir @rmdir($this->tempDirPath); //do cleanup $this->zip_is_open = false; $this->zipFilePath = ""; $this->tempDirPath = ""; $this->fileIndex = array(); return true; } /** * Add a new directory * @link http://php.net/manual/en/function.ziparchive-addemptydir.php * @param dirname string <p> * The directory to add. * </p> * @return bool Returns true on success or false on failure. */ public function addEmptyDir ($dirname) { if(!$this->zip_is_open){ return false; } return mkdir($this->tempDirPath .DIRECTORY_SEPARATOR. $dirname ); } /** * Add a file to a ZIP archive using its contents * @link http://php.net/manual/en/function.ziparchive-addfromstring.php * @param localname string <p> * The name of the entry to create. * </p> * @param contents string <p> * The contents to use to create the entry. It is used in a binary * safe mode. * </p> * @return bool Returns true on success or false on failure. */ public function addFromString ($localname, $contents) { if(!$this->zip_is_open){ return false; } $target_dir = $this->tempDirPath .DIRECTORY_SEPARATOR. dirname($localname); $taget_file = basename($localname); //create directory if it does not exist if( ! is_dir($target_dir) ){ if(! mkdir($target_dir,0777,true) ){ return false; } } //create file from string if( file_put_contents($target_dir.DIRECTORY_SEPARATOR.$taget_file,$contents) === False){ return false; } return true; } /** * Adds a file to a ZIP archive from the given path * @link http://php.net/manual/en/function.ziparchive-addfile.php * @param filename string <p> * The path to the file to add. * </p> * @param localname string[optional] <p> * local name inside ZIP archive. * </p> * @return bool Returns true on success or false on failure. */ public function addFile ($filename, $localname = null) {} /** * Renames an entry defined by its index * @link http://php.net/manual/en/function.ziparchive-renameindex.php * @param index int <p> * Index of the entry to rename. * </p> * @param newname string <p> * New name. * </p> * @return bool Returns true on success or false on failure. */ public function renameIndex ($index, $newname) {} /** * Renames an entry defined by its name * @link http://php.net/manual/en/function.ziparchive-renamename.php * @param name string <p> * Name of the entry to rename. * </p> * @param newname string <p> * New name. * </p> * @return bool Returns true on success or false on failure. */ public function renameName ($name, $newname) {} /** * Set the comment of a ZIP archive * @link http://php.net/manual/en/function.ziparchive-setarchivecomment.php * @param comment string <p> * The contents of the comment. * </p> * @return mixed Returns true on success or false on failure. */ public function setArchiveComment ($comment) {} /** * Returns the Zip archive comment * @link http://php.net/manual/en/function.ziparchive-getarchivecomment.php * @return string the Zip archive comment or false on failure. */ public function getArchiveComment () {} /** * Set the comment of an entry defined by its index * @link http://php.net/manual/en/function.ziparchive-setcommentindex.php * @param index int <p> * Index of the entry. * </p> * @param comment string <p> * The contents of the comment. * </p> * @return mixed Returns true on success or false on failure. */ public function setCommentIndex ($index, $comment) {} /** * Set the comment of an entry defined by its name * @link http://php.net/manual/en/function.ziparchive-setCommentName.php * @param name string <p> * Name of the entry. * </p> * @param comment string <p> * The contents of the comment. * </p> * @return mixed Returns true on success or false on failure. */ public function setCommentName ($name, $comment) {} /** * Returns the comment of an entry using the entry index * @link http://php.net/manual/en/function.ziparchive-getcommentindex.php * @param index int <p> * Index of the entry * </p> * @param flags int[optional] <p> * If flags is set to ZIPARCHIVE::FL_UNCHANGED, the original unchanged * comment is returned. * </p> * @return string the comment on success or false on failure. */ public function getCommentIndex ($index, $flags = null) {} /** * Returns the comment of an entry using the entry name * @link http://php.net/manual/en/function.ziparchive-getcommentname.php * @param name string <p> * Name of the entry * </p> * @param flags int[optional] <p> * If flags is set to ZIPARCHIVE::FL_UNCHANGED, the original unchanged * comment is returned. * </p> * @return string the comment on success or false on failure. */ public function getCommentName ($name, $flags = null) {} /** * delete an entry in the archive using its index * @link http://php.net/manual/en/function.ziparchive-deleteindex.php * @param index int <p> * Index of the entry to delete. * </p> * @return bool Returns true on success or false on failure. */ public function deleteIndex ($index) {} /** * delete an entry in the archive using its name * @link http://php.net/manual/en/function.ziparchive-deletename.php * @param name string <p> * Name of the entry to delete. * </p> * @return bool Returns true on success or false on failure. */ public function deleteName ($name) {} /** * Get the details of an entry defined by its name. * @link http://php.net/manual/en/function.ziparchive-statname.php * @param name name <p> * Name of the entry * </p> * @param flags int[optional] <p> * The flags argument specifies how the name lookup should be done. * Also, ZIPARCHIVE::FL_UNCHANGED may be ORed to it to request * information about the original file in the archive, * ignoring any changes made. * <p> * ZIPARCHIVE::FL_NOCASE * </p> * @return mixed an array containing the entry details or false on failure. */ public function statName ($name, $flags = null) {} /** * Get the details of an entry defined by its index. * @link http://php.net/manual/en/function.ziparchive-statindex.php * @param index int <p> * Index of the entry * </p> * @param flags int[optional] <p> * ZIPARCHIVE::FL_UNCHANGED may be ORed to it to request * information about the original file in the archive, * ignoring any changes made. * </p> * @return mixed an array containing the entry details or false on failure. */ public function statIndex ($index, $flags = null) {} /** * Returns the index of the entry in the archive * @link http://php.net/manual/en/function.ziparchive-locatename.php * @param name string <p> * The name of the entry to look up * </p> * @param flags int[optional] <p> * The function returns the index of the file named fname in * archive. The flags are specified by ORing the following values, * or 0 for none of them. * <p> * ZIPARCHIVE::FL_NOCASE * </p> * @return mixed the index of the entry on success or false on failure. */ public function locateName ($name, $flags = null) { if(!$this->zip_is_open){ return false; } $noDir = ($flags & self::FL_NODIR) ? true:false; $noCase = ($flags & self::FL_NOCASE) ? true:false; $loopFile = ($noDir) ? basename($name) : $name ; if($noCase){ $search = strtolower($search); } foreach($this->fileIndex as $ind => $fileRelPath){ $loopFile = ($noDir) ? basename($fileRelPath) : $fileRelPath ; if($noCase){ $loopFile = strtolower($loopFile); } if( $loopFile == $search ){ return $ind; } } } /** * Returns the name of an entry using its index * @link http://php.net/manual/en/function.ziparchive-getnameindex.php * @param index int <p> * Index of the entry. * </p> * @return string the name on success or false on failure. */ public function getNameIndex ($index) { if(!$this->zip_is_open){ return false; } if(! is_set($this->fileIndex[$index]) ){ return false; } return $this->fileIndex[$index]; } /** * Revert all global changes done in the archive. * @link http://php.net/manual/en/function.ziparchive-unchangearchive.php * @return mixed Returns true on success or false on failure. */ public function unchangeArchive () {} /** * Undo all changes done in the archive. * @link http://php.net/manual/en/function.ziparchive-unchangeall.php * @return mixed Returns true on success or false on failure. */ public function unchangeAll () {} /** * Revert all changes done to an entry at the given index. * @link http://php.net/manual/en/function.ziparchive-unchangeindex.php * @param index int <p> * Index of the entry. * </p> * @return mixed Returns true on success or false on failure. */ public function unchangeIndex ($index) {} /** * Revert all changes done to an entry with the given name. * @link http://php.net/manual/en/function.ziparchive-unchangename.php * @param name string <p> * Name of the entry. * </p> * @return mixed Returns true on success or false on failure. */ public function unchangeName ($name) {} /** * Extract the archive contents * @link http://php.net/manual/en/function.ziparchive-extractto.php * @param destination string <p> * Location where to extract the files. * </p> * @param entries mixed[optional] <p> * The entries to extract. It accepts either a single entry name or * an array of names. * </p> * @return mixed Returns true on success or false on failure. */ public function extractTo ($destination, $entries = null) { if(!$this->zip_is_open){ return false; } return $this->__extractTo($this->zipFilePath, $destination, $entries); } private function __extractTo($zip_path, $destination, $entries = null) { $return_value=0; $output = array(); //determine list of files to extract $list = ""; if( is_string($entries) ){ $list = "'$entries'"; } if(is_array($entries)){ $list = "'".implode("' '", $entries)."'"; } exec(self::$unzip_exec." -o {$zip_path} -d {$destination} {$list} 2>&1", $output, $return_value); if($return_value != 0){ return false; } return true; } /** * Returns the entry contents using its name. * @link http://php.net/manual/en/function.ziparchive-getfromname.php * @param name string <p> * Name of the entry * </p> * @param flags int[optional] <p> * The flags to use to open the archive. the following values may * be ORed to it. * <p> * ZIPARCHIVE::FL_UNCHANGED * </p> * @return mixed the contents of the entry on success or false on failure. */ public function getFromName ($name, $flags = null) { if(!$this->zip_is_open){ return false; } return @file_get_contents($this->tempDirPath .DIRECTORY_SEPARATOR. $name ); } /** * Returns the entry contents using its index. * @link http://php.net/manual/en/function.ziparchive-getfromindex.php * @param index int <p> * Index of the entry * </p> * @param flags int[optional] <p> * The flags to use to open the archive. the following values may * be ORed to it. * <p> * ZIPARCHIVE::FL_UNCHANGED * </p> * @return mixed the contents of the entry on success or false on failure. */ public function getFromIndex ($index, $flags = null) { if(!$this->zip_is_open){ return false; } $localFilePath = $this->getNameIndex($index); if($localFilePath === false){ return false; } return $this->getFromName($localFilePath); } /** * Get a file handler to the entry defined by its name (read only). * @link http://php.net/manual/en/function.ziparchive-getstream.php * @param name string <p> * The name of the entry to use. * </p> * @return resource a file pointer (resource) on success or false on failure. */ public function getStream ($name) {} /** * * @param $dir_path absolute path to zip directory */ private function __indexDirRec($dir_path){ $dirEntries = scandir($dir_path); foreach($dirEntries as $dirEntriy){ if( $dirEntriy == "." || $dirEntriy == ".."){ continue; } $subject = $dir_path .DIRECTORY_SEPARATOR. $dirEntriy; //if this is directory then index its contents if( is_dir($subject) ){ $this->__indexDirRec($subject); }else{ //save path as relative path inside zip $this->fileIndex[] = str_replace($this->tempDirPath.DIRECTORY_SEPARATOR, "", $subject); } } } } ZipArchive::setup(); //try to find locations of zip/unzip binaries /** * Open a ZIP file archive * @link http://php.net/manual/en/function.zip-open.php * @param filename string <p> * The file name of the ZIP archive to open. * </p> * @return mixed a resource handle for later use with * zip_read and zip_close * or returns the number of error if filename does not * exist or in case of other error. */ function zip_open ($filename) {} /** * Close a ZIP file archive * @link http://php.net/manual/en/function.zip-close.php * @param zip resource <p> * A ZIP file previously opened with zip_open. * </p> * @return void */ function zip_close ($zip) {} /** * Read next entry in a ZIP file archive * @link http://php.net/manual/en/function.zip-read.php * @param zip resource <p> * A ZIP file previously opened with zip_open. * </p> * @return mixed a directory entry resource for later use with the * zip_entry_... functions or false if * there's no more entries to read or number of error in case of other error. */ function zip_read ($zip) {} /** * Open a directory entry for reading * @link http://php.net/manual/en/function.zip-entry-open.php * @param zip resource <p> * A valid resource handle returned by zip_open. * </p> * @param zip_entry resource <p> * A directory entry returned by zip_read. * </p> * @param mode string[optional] <p> * Any of the modes specified in the documentation of * fopen. * </p> * <p> * Currently, mode is ignored and is always * "rb". This is due to the fact that zip support * in PHP is read only access. * </p> * @return bool Returns true on success or false on failure. * </p> * <p> * Unlike fopen and other similar functions, * the return value of zip_entry_open only * indicates the result of the operation and is not needed for * reading or closing the directory entry. */ function zip_entry_open ($zip, $zip_entry, $mode = null) {} /** * Close a directory entry * @link http://php.net/manual/en/function.zip-entry-close.php * @param zip_entry resource <p> * A directory entry previously opened zip_entry_open. * </p> * @return bool Returns true on success or false on failure. */ function zip_entry_close ($zip_entry) {} /** * Read from an open directory entry * @link http://php.net/manual/en/function.zip-entry-read.php * @param zip_entry resource <p> * A directory entry returned by zip_read. * </p> * @param length int[optional] <p> * The number of bytes to return. If not specified, this function will * attempt to read 1024 bytes. * </p> * <p> * This should be the uncompressed length you wish to read. * </p> * @return string the data read, or false if the end of the file is * reached. */ function zip_entry_read ($zip_entry, $length = null) {} /** * Retrieve the actual file size of a directory entry * @link http://php.net/manual/en/function.zip-entry-filesize.php * @param zip_entry resource <p> * A directory entry returned by zip_read. * </p> * @return int The size of the directory entry. */ function zip_entry_filesize ($zip_entry) {} /** * Retrieve the name of a directory entry * @link http://php.net/manual/en/function.zip-entry-name.php * @param zip_entry resource <p> * A directory entry returned by zip_read. * </p> * @return string The name of the directory entry. */ function zip_entry_name ($zip_entry) {} /** * Retrieve the compressed size of a directory entry * @link http://php.net/manual/en/function.zip-entry-compressedsize.php * @param zip_entry resource <p> * A directory entry returned by zip_read. * </p> * @return int The compressed size. */ function zip_entry_compressedsize ($zip_entry) {} /** * Retrieve the compression method of a directory entry * @link http://php.net/manual/en/function.zip-entry-compressionmethod.php * @param zip_entry resource <p> * A directory entry returned by zip_read. * </p> * @return string The compression method. */ function zip_entry_compressionmethod ($zip_entry) {}