129 lines
3.7 KiB
PHP
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);
|
|
}
|
|
}
|