<?php // Achtung: Es existiert eine einfachere Version unter http://coding.binon.net/ZIP
class ZIP {
/* ZIP-Archiv erstellen 051015 - under construction! */
/* $source: Dateiliste (string array), Pfad oder leer (=aktuelles Verzeichnis) (string)
$zipfile: Pfad & Dateiname des ZIP-Archivs, nur Pfad, nur Dateiname oder leer (=wird automatisch gebildet) (string)
Ein existentes Archiv wird erweitert. %DOWNLOAD% (optional zu Pfad/Namen) fuer Download.
$exclude: Dateien ausschliessen. FALSE: Keine Dateien ausschliessen, ggf. auch Unterverzeichnisse
archivieren; TRUE: Vordefinierte Dateien ausschliessen; String-Array: Liste der
auszuschliessenden Dateien. Folgende Schluesselworte sind ebenfalls moeglich:
%DIR% (Verzeichnisse), %SCRIPT% (ZIP-Script), %ARCHIVE% (ZIP-Archiv), %HIDDEN% (versteckte Dateien)
*.ext (Dateien mit der Endung 'ext')
$save_basedir: Basisverzeichnis im Archiv vermerken (boolean) oder beliebiger Pfad (string)
Aufruf: $zip=new ZIP();
$zip->zip([$path2zip[,$zipfilename[,$exclude[,$save_basedir]]]]);
Rueckgabe: Dateiname des ZIP-Archivs oder FALSE
*/
function zip($source='.',$zipfile='',$exclude=TRUE,$save_basedir=TRUE) {
// Startbedingung: zLib muss vorhanden sein
if(function_exists('gzcompress')) {
// Variableninitalisierung
$worklist=array();
$prepath='';
$listtype='';
if($source==='') { $source='.'; }
// Steuervariablen: Extension beachten bei automatischem ZIP-Dateinamen
$default['use_folder_ext']=FALSE;
$default['use_file_ext']=FALSE;
// Steuervariablen: Spezielle Dateien automatisch ausschliessen
$default['exclude_subdir']=FALSE;
$default['exclude_zipscript']=TRUE;
$default['exclude_ziparchive']=TRUE;
$default['exclude_hidden_files']=TRUE;
// Steuervariable: Vorhandene Eintraege ueberschreiben
$default['overwrite_entries']=TRUE;
// ZipFile-Parameter analysieren
$download_zip=(strpos($zipfile,'%DOWNLOAD%')!==FALSE)?TRUE:FALSE;
if($download_zip) { $zipfile=str_replace('%DOWNLOAD%','',$zipfile); }
// 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($save_basedir) && strlen($save_basedir)) {
$save_basedir=unify_path($save_basedir);
$zipname=realDirname($save_basedir,1,FALSE);
$zipfile=getAbsPath(filename($zipname,!$default['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)?$default['use_folder_ext']:$default['use_file_ext'])).'.zip');
} elseif(is_path($zipfile)) {
// ... aus dem vorgegebenen Pfad + Source-Dateiname (Ext nach Wunsch bewahren)
$zipfile.=filename($source,!(is_dir($source)?$default['use_folder_ext']:$default['use_file_ext'])).'.zip';
}
if(is_dir($source)) {
// Verzeichnis wurde uebergeben: Dateiliste erstellen (ggf. ohne Unterverzeichnisse: Wenn %DIR% in $exclude oder, wenn $exlude TRUE ist, den Default-Wert)
$filelist=filelist('',$source,'',((is_array($exclude))?!in_array('%DIR%',$exclude):(($exclude)?!$default['exclude_subdir']:TRUE)),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($save_basedir!==FALSE) {
// Als Basisverzeichnis das ...
if(is_string($save_basedir) && strlen($save_basedir)) {
// ... explizit uebergebene verwenden
$prepath=$save_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
if($listtype=='directory') {
// Wenn Verzeichnis uebergeben wurde: den Pfad mitgeben
$serverFilename=$source.'/'.$filename;
// Ggf. Dateien ausschliessen
if($default['exclude_zipscript'] && $serverFilename==$_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF']) { $serverFilename=''; }
if($default['exclude_ziparchive'] && $serverFilename==$zipfile) { $serverFilename=''; }
if($default['exclude_hidden_files'] && substr(basename($serverFilename),0,1)=='.') { $serverFilename=''; }
} else {
// Wenn Dateiliste uebergeben wurde: Dateiname ist so korrekt, aber ...
$serverFilename=$filename;
// ... nur den Namen, nicht den Pfad sichern
$filename=basename($filename);
}
if($serverFilename && is_file($serverFilename)) {
// 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]) || $default['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
if($download_zip) {
header('Content-Type: application/zip');
header('Content-Disposition: inline; filename='.basename($zipfile).';');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.strlen($this->file()));
header('Expires: Sat, 01 Jan 2000 12:00:00 GMT');
echo $this->file();
$result=TRUE;
} else {
$fh=@fopen($zipfile,'wb');
if($fh) {
fwrite($fh,$this->file(),strlen($this->file()));
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);
}
?>