<?php // Update: Es existiert eine umfangreichere Version unter http://coding.binon.net/ZIP2
/*
Beispielaufruf:
$path2zip=$_SERVER['DOCUMENT_ROOT'].'/data';
$z=new ZIP();
$newzip=$z->zip($path2zip);
// Packt den angegeben Ordner inkl. der Unterordner. Der Name des Archive entspricht dem (extensionslosen) Namen des Ordners + '.zip' und der Ordnername ist im Archiv gesichert (-> 'data\file1.ext').
Weitere Beispiele:
$newzip=$z->zip($path2zip,'',TRUE,TRUE); // Exakt dasselbe
$newzip=$z->zip($path2zip,'',TRUE,FALSE); // Ordnername wird nicht im Archiv gesichert
$newzip=$z->zip($path2zip,'',TRUE,'dir'); // Nicht der Ordnername, 'dir\' wird gesichert
$newzip=$z->zip($path2zip,'',FALSE,TRUE); // Unterverzeichnisse werden nicht archiviert
$newzip=$z->zip($path2zip,'my.zip'); // Archivname ist 'my.zip'
$newzip=$z->zip($path2zip,'/archive/'); // Archiv wird in Pfad '/archive' erstellt
$newzip=$z->zip($path2zip,'/dir/my.zip'); // '/dir/my.zip' wird erstellt
*/
class ZIP {
/* ZIP-Archiv erstellen 051012 */
/* $source: Dateiliste (string array), Pfad oder leer (=aktuelles Verzeichnis) (string)
$zipfile: Pfad & Dateiname des ZIP-Archivs, nur Pfad, nur Dateiname oder leer (string)
$catch: Ggf. auch Unterverzeichnisse archivieren (boolean)
$include_basedir: Basisverzeichnis im Archiv vermerken (boolean) oder beliebiger Pfad (string)
Aufruf: $zip=new ZIP();
$zip->zip([$path2zip[,$zipfilename[,$catch[,$include_basedir]]]]);
Rueckgabe: Dateiname des ZIP-Archivs oder FALSE
*/
function zip($source='.',$zipfile='',$catch=TRUE,$include_basedir=TRUE) {
// Startbedingung: zLib muss vorhanden sein
if(function_exists('gzcompress')) {
// Variableninitalisierung
$worklist=array();
$prepath='';
$listtype='';
// Steuervariablen: Extension beachten bei automatischem ZIP-Dateinamen
$use_folder_ext=TRUE;
$use_file_ext=FALSE;
// Steuervariable: Vorhandene Eintraege ueberschreiben
$overwrite_entries=TRUE;
// Source-Parameter analysieren
if(is_array($source)) {
// Dateiliste wurde uebergeben: verwenden
$filelist=$source;
$listtype='entries';
// Ggf. Dateinamen des ZIP-Archivs bilden (aus dem Basisverzeichnis-Parameter)
if(!$zipfile && is_string($include_basedir) && strlen($include_basedir)) {
$include_basedir=unify_path($include_basedir);
$zipname=realDirname($include_basedir,1,FALSE);
$zipfile=getAbsPath(filename($zipname,!$use_folder_ext).'.zip');
}
} elseif(abs_path($source)) {
// Ggf. Dateinamen des ZIP-Archivs bilden ...
if(!$zipfile) {
// ... aus dem Source-Verzeichnis-/Dateinamen (Ext nach Wunsch bewahren)
$zipfile=getAbsPath(filename($source,!(is_dir($source)?$use_folder_ext:$use_file_ext)).'.zip');
} elseif(is_path($zipfile)) {
// ... aus dem vorgegebenen Pfad + Source-Dateiname (Ext nach Wunsch bewahren)
$zipfile.=filename($source,!(is_dir($source)?$use_folder_ext:$use_file_ext)).'.zip';
}
if(is_dir($source)) {
// Verzeichnis wurde uebergeben: Dateiliste erstellen
$filelist=filelist('',$source,'',($catch),FALSE);
$listtype='directory';
} elseif(is_file($source)) {
// Dateiname wurde uebergeben: in Dateiliste umwandeln
$filelist=array($source);
$listtype='entries';
}
}
// Abbrechen, wenn ungueltiger Source-Parameter
if(!$listtype || !abs_path($zipfile) || !isWritable($zipfile)) {
$result=FALSE;
} else {
// Basisverzeichnis voranstellen?
if($include_basedir!==FALSE) {
// Als Basisverzeichnis das ...
if(is_string($include_basedir) && strlen($include_basedir)) {
// ... explizit uebergebene verwenden
$prepath=$include_basedir.'/';
} elseif($listtype=='directory') {
// ... das tatsaechliche verwenden (sofern ein Verzeichnis archiviert werden soll)
$prepath=basename(($source)?realpath($source):getcwd()).'/';
}
// Basisverzeichnis vereinheitlichen (inkl. Pfadtrenner am Ende)
if($prepath) { $prepath=str_replace('//','/',unify_path($prepath.'/')); }
}
// Arbeitsliste aus Namen der ZIP-Eintraege (Key) und Server-Dateinamen (Value) erstellen
foreach($filelist as $filename) {
// Serverdateinamen ermitteln (wurde Verzeichnisname uebergeben, den Pfad mitgeben)
$serverFilename=(($listtype=='directory')?($source.'/'):'').$filename;
if(is_file($serverFilename)) {
// Wenn Dateiliste uebergeben wurde: Nur Dateinamen ohne Pfad sichern
if($listtype=='entries') { $filename=basename($filename); }
// Arbeitseintrag zusammenstellen
$worklist[$prepath.$filename]=$serverFilename;
}
}
// Die Dateiliste wird jetzt nicht mehr gebraucht
unset($filelist);
// Vorhandenes Archiv auswerten
if(file_exists($zipfile)) {
$zh=zip_open($zipfile);
if($zh) {
// Alle Eintraege durchgehen
while($entry=zip_read($zh)) {
if(zip_entry_open($zh,$entry,'r')) {
// Name des Eintrags ermitteln
$entryFilename=zip_entry_name($entry);
// Existiert kein identischer Name in der Arbeitsliste oder Ueberschreiben ist ohnehin nicht erlaubt?
if(!isset($worklist[$entryFilename]) || $overwrite_entries===FALSE) {
// Ja, also vorhandene Datei in neues Archiv uebernehmen und ...
$this->addFile(zip_entry_read($entry,zip_entry_filesize($entry)),$entryFilename);
// ... Eintrag ggf. aus der worklist entfernen
if(isset($worklist[$entryFilename])) { unset($worklist[$entryFilename]); }
}
zip_entry_close($entry);
}
}
zip_close($zh);
}
}
// Arbeitsliste durchgehen
foreach($worklist as $entryFilename => $serverFilename) {
// Archiv zusammenstellen
$this->addFile(file_get_contents($serverFilename),$entryFilename);
}
// Archivdatei erstellen/ausgeben
$archive=$this->file();
$fh=@fopen($zipfile,'wb');
if($fh) {
fwrite($fh,$archive,strlen($archive));
fclose($fh);
$result=$zipfile;
} else {
$result=FALSE;
}
}
} else {
$result=FALSE;
}
return $result;
}
// =================== Zip file creation class (uses zLib) ===================
var $datasec = array();
var $ctrl_dir = array();
var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
var $old_offset = 0;
/**
* Converts an Unix timestamp to a four byte DOS date and time format (date
* in high two bytes, time in low two bytes allowing magnitude comparison).
*
* @param integer the current Unix timestamp
*
* @return integer the current date in a four byte DOS format
*
* @access private
*/
function unix2DosTime($unixtime = 0) {
$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
} // end if
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
} // end of the 'unix2DosTime()' method
/**
* Adds "file" to archive
*
* @param string file contents
* @param string name of the file in the archive (may contains the path)
* @param integer the current timestamp
*
* @access public
*/
function addFile($data, $name, $time = 0)
{
$name = str_replace('\\', '/', $name);
$dtime = dechex($this->unix2DosTime($time));
$hexdtime = '\x' . $dtime[6] . $dtime[7]
. '\x' . $dtime[4] . $dtime[5]
. '\x' . $dtime[2] . $dtime[3]
. '\x' . $dtime[0] . $dtime[1];
eval('$hexdtime = "' . $hexdtime . '";');
$fr = "\x50\x4b\x03\x04";
$fr .= "\x14\x00"; // ver needed to extract
$fr .= "\x00\x00"; // gen purpose bit flag
$fr .= "\x08\x00"; // compression method
$fr .= $hexdtime; // last mod time and date
// "local file header" segment
$unc_len = strlen($data);
$crc = crc32($data);
$zdata = gzcompress($data,9);
$c_len = strlen($zdata);
$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
$fr .= pack('V', $crc); // crc32
$fr .= pack('V', $c_len); // compressed filesize
$fr .= pack('V', $unc_len); // uncompressed filesize
$fr .= pack('v', strlen($name)); // length of filename
$fr .= pack('v', 0); // extra field length
$fr .= $name;
// "file data" segment
$fr .= $zdata;
// "data descriptor" segment (optional but necessary if archive is not
// served as file)
$fr .= pack('V', $crc); // crc32
$fr .= pack('V', $c_len); // compressed filesize
$fr .= pack('V', $unc_len); // uncompressed filesize
// add this entry to array
$this -> datasec[] = $fr;
$new_offset = strlen(implode('', $this->datasec));
// now add to central directory record
$cdrec = "\x50\x4b\x01\x02";
$cdrec .= "\x00\x00"; // version made by
$cdrec .= "\x14\x00"; // version needed to extract
$cdrec .= "\x00\x00"; // gen purpose bit flag
$cdrec .= "\x08\x00"; // compression method
$cdrec .= $hexdtime; // last mod time & date
$cdrec .= pack('V', $crc); // crc32
$cdrec .= pack('V', $c_len); // compressed filesize
$cdrec .= pack('V', $unc_len); // uncompressed filesize
$cdrec .= pack('v', strlen($name) ); // length of filename
$cdrec .= pack('v', 0 ); // extra field length
$cdrec .= pack('v', 0 ); // file comment length
$cdrec .= pack('v', 0 ); // disk number start
$cdrec .= pack('v', 0 ); // internal file attributes
$cdrec .= pack('V', 32 ); // external file attributes - 'archive' bit set
$cdrec .= pack('V', $this -> old_offset ); // relative offset of local header
$this -> old_offset = $new_offset;
$cdrec .= $name;
// optional extra field, file comment goes here
// save to central directory
$this -> ctrl_dir[] = $cdrec;
} // end of the 'addFile()' method
/**
* Dumps out file
*
* @return string the zipped file
*
* @access public
*/
function file()
{
$data = implode('', $this -> datasec);
$ctrldir = implode('', $this -> ctrl_dir);
return
$data .
$ctrldir .
$this -> eof_ctrl_dir .
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries "on this disk"
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries overall
pack('V', strlen($ctrldir)) . // size of central dir
pack('V', strlen($data)) . // offset to start of central dir
"\x00\x00"; // .zip file comment length
} // end of the 'file()' method
} // end of the 'ZIP' class
// ---------------------------------------------------------------------------
/* Pfadtrenner (auf Unix-Format) vereinheitlichen 051004 */
function unify_path($path,$dos=FALSE) {
return ($dos)?str_replace('/','\\',$path):str_replace('\\','/',$path);
}
// ---------------------------------------------------------------------------
/* Ermittelt Dateinamen 050629 */
function filename($file,$nosuffix=TRUE) {
// Dateinamen ermitteln
$filename=realBasename($file);
// Wenn Dateiendung nicht erwuenscht ...
if($nosuffix) {
// ... Position der Endung ermitteln ...
$extPos=strrpos($filename,'.');
// ... und Dateinamen ggf. kuerzen
if($extPos && $extPos<strlen($filename)-1) { $filename=substr($filename,0,$extPos); }
}
return $filename;
}
// ---------------------------------------------------------------------------
/* Ermittelt einen Verzeichnisnamen nach Nummer, bzw. Anzahl der Verzeichnisse 051005 */
function realDirname($path,$part=-1,$last_is_file=TRUE) {
$result=FALSE;
// Pfad vereinheitlichen ...
$path=unify_path($path);
// ... und zerlegen
$folder=explode('/',$path);
// Ggf. Dateinamen entfernen
if($last_is_file) {
$filename=array_pop($folder);
$path=implode('/',$folder);
}
// Ggf. vorhandenen "/" (oder "x:/") am Pfadanfang ausgleichen
if((strlen($path)>0 && $path{0}=='/') || (strlen($path)>1 && $path{1}==':')) { array_shift($folder); }
// Je nach Vorzeichen von links oder rechts zaehlen
if($part>0) {
$part-=1;
} elseif($part<0) {
$part=sizeof($folder)+($part-((is_path($path))?1:0));
} else {
// Anzahl der Verzeichnisse ermitteln (bei $part=0)
$result=sizeof($folder)-((is_path($path))?1:0);
}
// Gewuenschten Verzeichnisteil ausschneiden (FALSE wenn nicht vorhanden)
if($result===FALSE) { $result=(!empty($folder[$part]))?$folder[$part]:FALSE; }
return $result;
}
// ---------------------------------------------------------------------------
/* Wahren Dateinamen ermitteln 050412 */
function realBasename($file) {
// Nur wirklichen Dateinamen ermitteln, nicht vom Pfad
return (is_path($file))?'':basename($file);
}
// ---------------------------------------------------------------------------
/* Zeichenkette ist ein Pfad? 050412 */
function is_path($file) {
// Letztes Zeichen ist "/" oder "\"?
$lastChar=substr($file,-1);
return ($lastChar=='/' || $lastChar=='\\');
}
// ---------------------------------------------------------------------------
/* Verzeichnis einlesen 050922 */
function filelist($path,$base='',$condition='',$recursive=FALSE,$headingslash=TRUE) {
$files=array();
// Verzeichnis oeffnen
$handle=opendir($base.$path);
// Wenn Verzeichnis geoeffnet:
if($handle) {
// Verzeichniseintraege durchgehen ...
while($entry=readdir($handle)) {
$filename=$path.'/'.$entry;
// ... und ggf. naechsten Eintrag holen ...
if($entry=='.' || $entry=='..') { continue; }
// ... bzw. Rekursion wenn Eintrag ein Unterverzeichnis ist
if(is_dir($base.$filename)) {
if($recursive) {
$subdir=filelist($filename,$base,$condition,$recursive,$headingslash);
if($subdir && sizeof($subdir)) {
$files=array_merge($files,$subdir);
}
unset($subdir);
}
} else {
// Dateinamen zu Array hinzufuegen
array_push($files,(($headingslash)?$filename:substr($filename,1)));
}
}
closedir($handle);
}
if(!sizeof($files)) { $files=FALSE; }
return $files;
}
// ---------------------------------------------------------------------------
/* Wandelt einen relativen in einen absoluten Pfad um 051005
Beispiel: $path='dir/dir2/../dir2b';
if(!abs_path($path) echo 'Ungueltiger Pfad';
Im Fall des Gelingens, enthaelt $path jetzt '/actual_path/dir/dir2b'!
*/
function abs_path(&$path) {
if($path==='') { $path='.'; }
// Pfadtrenner vereinheitlichen
$path=unify_path($path);
// Relative Basis in absolute Basis umwandeln
if((strlen($path)>0 && $path{0}!='/') && !(strlen($path)>1 && $path{1}==':') ) {
$curdir=getcwd();
if($curdir) {
$path=$curdir.'/'.$path;
}
}
// Dateipfad "normalisieren" und in einzelne Verzeichnisnamen zerlegen
$folder=explode('/',unify_path($path));
// Verzeichnisnamen durchgehen
for($i=0;$i<count($folder);$i++) {
// Verweis auf aktuelles oder uebergeordnetes Verzeichnis herausfiltern ...
if($folder[$i]=='.' || $folder[$i]=='..') {
if($folder[$i]=='..') { unset($folder[$i]); $i--; }
unset($folder[$i]); $i--;
// .. indem das Verzeichnisarray ggf. gekuerzt wird
$folder=array_slice($folder,0,count($folder));
}
// Wurde im Pfad "vor" das Startverzeichnis verwiesen: FALSE zurueck
if($i<0) { return FALSE; }
}
// Keine Fehler: Pfadvariable aktualisieren und TRUE zurueck
$path=implode('/',$folder);
return TRUE;
}
// ---------------------------------------------------------------------------
/* Ermittelt den absoluten Pfad */
function getAbsPath($path) {
if(!abs_path($path)) { $path=''; }
return $path;
}
// ---------------------------------------------------------------------------
/* Datei beschreib- oder anlegbar? */
function isWritable($file) {
return (file_exists($file))?is_writable($file):(file_exists(dirname($file))?is_writable(dirname($file)):FALSE);
}
?>