Files
2026-10-16 19:12:18 +00:00

129 lines
3.7 KiB
PHP

<?php
/**
* Eigenständiger ZIP-Writer ohne php_zip Erweiterung.
* Implementiert ZIP-Format (PKZIP 2.0) direkt in PHP.
*/
class SimpleZip
{
private string $path;
private $fh;
private array $entries = [];
private int $offset = 0;
public function __construct(string $path)
{
$this->path = $path;
$this->fh = fopen($path, 'wb');
if (!$this->fh) throw new RuntimeException("Kann ZIP nicht erstellen: $path");
}
public function addFromString(string $localName, string $data): void
{
$this->addData($localName, $data, time());
}
public function addFile(string $filePath, string $localName): void
{
$data = file_get_contents($filePath);
$mtime = filemtime($filePath);
$this->addData($localName, $data, $mtime);
}
private function addData(string $localName, string $data, int $mtime): void
{
$localName = str_replace('\\', '/', $localName);
$crc = crc32($data);
$size = strlen($data);
$compressed = gzdeflate($data, 6);
$compSize = strlen($compressed);
// Lokaler Header
$dosTime = $this->dosTime(filemtime($filePath));
$nameLen = strlen($localName);
$localHeader = pack('VvvvvVVVvv',
0x04034b50, // Signatur
20, // Version needed
0, // Flags
8, // Deflate
$dosTime, // Mod time
$crc,
$compSize,
$size,
$nameLen,
0 // Extra length
) . $localName;
fwrite($this->fh, $localHeader);
fwrite($this->fh, $compressed);
$this->entries[] = [
'name' => $localName,
'crc' => $crc,
'size' => $size,
'compSize' => $compSize,
'offset' => $this->offset,
'dosTime' => $dosTime,
];
$this->offset += strlen($localHeader) + $compSize;
}
public function close(): void
{
$cdOffset = $this->offset;
$cdSize = 0;
foreach ($this->entries as $e) {
$nameLen = strlen($e['name']);
$cd = pack('VvvvvVVVvvvvvVV',
0x02014b50, // CD Signatur
20, // Version made by
20, // Version needed
0, // Flags
8, // Deflate
$e['dosTime'],
$e['crc'],
$e['compSize'],
$e['size'],
$nameLen,
0, // Extra length
0, // Comment length
0, // Disk start
0, // Internal attr
0, // External attr
$e['offset']
) . $e['name'];
fwrite($this->fh, $cd);
$cdSize += strlen($cd);
}
// End of central directory
$count = count($this->entries);
fwrite($this->fh, pack('VvvvvVVv',
0x06054b50, // EOCD Signatur
0, // Disk number
0, // Disk with CD
$count, // Entries on disk
$count, // Total entries
$cdSize,
$cdOffset,
0 // Comment length
));
fclose($this->fh);
}
private function dosTime(int $unixTime): int
{
$d = getdate($unixTime);
return (($d['year'] - 1980) << 25)
| ($d['mon'] << 21)
| ($d['mday'] << 16)
| ($d['hours'] << 11)
| ($d['minutes'] << 5)
| ($d['seconds'] >> 1);
}
}