Logdateien wachsen still vor sich hin, Cache-Ordner belegen irgendwann Gigabyte, und JSON-Antworten werden mit jedem weiteren Feld größer. PHP gzencode nimmt einen String entgegen und gibt ihn im gzip-Format aus, bei Text häufig auf ein Zehntel geschrumpft. Dieses Tutorial geht den ganzen Weg: vom ersten gepackten String über die Datei mit der Endung .gz bis zu der Verwechslung, an der die meisten hängenbleiben.
Das Bild zeigt, was beim Packen mit dem Datenstrom geschieht. Im Code beginnt das mit einem einzigen Aufruf.
Was PHP gzencode() macht
PHP gzencode() erwartet einen String und gibt einen kürzeren String heraus, im Fehlerfall false. Zwei weitere Parameter sind freiwillig: die Kompressionsstufe und das Ausgabeformat. Das Ergebnis ist byteweise dasselbe, was auch das Kommandozeilenwerkzeug gzip liefert, und deshalb öffnet jedes Packprogramm eine so geschriebene Datei.
Dahinter steckt das Verfahren Deflate. Vereinfacht gesagt ersetzt es wiederkehrende Zeichenfolgen durch einen Verweis auf ihr erstes Vorkommen. Genau deswegen schrumpfen Text, HTML, JSON und Protokolldateien so stark, denn dort wiederholt sich fast alles. Um die Daten legt gzip einen Rahmen: zehn Bytes Kopf davor, eine CRC32-Prüfsumme und die Originallänge dahinter. Die ersten drei Bytes lauten immer 1f 8b 08, das ist die Magic Number.
Der erste Durchlauf: String packen und wieder auspacken
Der Einstieg braucht kaum mehr als drei Zeilen: ein Text geht hinein, ein deutlich kürzerer String kommt heraus, und gzdecode() stellt das Original wieder her.
<?php
$text = str_repeat(
'Fehler beim Speichern des Datensatzes. Bitte spaeter erneut versuchen. ',
20
);
$gepackt = gzencode($text, 6);
echo strlen($text) . ' Bytes'; /* 1420 Bytes */
echo strlen($gepackt) . ' Bytes'; /* 99 Bytes */
/* Verlustfrei: der Text kommt exakt zurueck */
var_dump(gzdecode($gepackt) === $text); /* bool(true) */
/* Die Magic Number am Anfang, immer dieselbe */
echo bin2hex(substr($gepackt, 0, 3)); /* 1f8b08 */
Aus 1420 Bytes werden 99, weil derselbe Satz zwanzigmal hintereinandersteht und Deflate genau das belohnt. Bei gewachsenem Fließtext fällt der Gewinn kleiner aus, bleibt aber deutlich. Wichtig ist die vorletzte Zeile: PHP gzencode arbeitet verlustfrei, es kommt Zeichen für Zeichen dasselbe heraus.
Eine Datei als .gz speichern
Der Weg zur .gz-Datei führt über dieselbe Funktion. PHP gzencode packt den Inhalt, geschrieben wird er anschließend ganz gewöhnlich.
<?php
$quelle = 'logs/app.log';
$ziel = 'logs/app.log.gz';
$roh = file_get_contents($quelle);
file_put_contents($ziel, gzencode($roh, 6));
printf(
"%d Bytes => %d Bytes (%.1f Prozent)\n",
strlen($roh),
filesize($ziel),
filesize($ziel) / strlen($roh) * 100
);
/* 19094 Bytes => 2882 Bytes (15.1 Prozent) */
/* Zurueck in den Ausgangszustand */
$zurueck = gzdecode(file_get_contents($ziel));
var_dump($zurueck === $roh); /* bool(true) */
Aus 19.094 Bytes werden 2.882 Bytes, also 15,1 Prozent der Ausgangsgröße. Die Feinheiten beim Schreiben behandelt das Tutorial zu PHP file_put_contents(), die Messung der Dateigrößen zeigt PHP filesize(). Die erzeugte Datei lässt sich danach auf der Kommandozeile auspacken, ganz ohne PHP.
gzencode, gzcompress und gzdeflate sind drei verschiedene Formate
Hier liegt der Kern des Themas. Die Namen legen nahe, dass gzcompress() die gzip-Ausgabe liefern würde. Genau das trifft nicht zu: jede der drei Funktionen erzeugt ein eigenes Format mit eigenem Rahmen, und gzip nach RFC 1952 kommt nur aus PHP gzencode.
<?php
$text = 'Kompression in PHP'; /* 18 Bytes */
$gzip = gzencode($text); /* RFC 1952 */
$zlib = gzcompress($text); /* RFC 1950 */
$roh = gzdeflate($text); /* RFC 1951 */
printf("gzencode: %2d Bytes, Kopf %s\n",
strlen($gzip), bin2hex(substr($gzip, 0, 3)));
printf("gzcompress: %2d Bytes, Kopf %s\n",
strlen($zlib), bin2hex(substr($zlib, 0, 2)));
printf("gzdeflate: %2d Bytes, Kopf %s\n",
strlen($roh), bin2hex(substr($roh, 0, 2)));
/* gzencode: 38 Bytes, Kopf 1f8b08
gzcompress: 26 Bytes, Kopf 789c
gzdeflate: 20 Bytes, Kopf f3ce */
/* Jedes Format nur mit seinem eigenen Gegenstueck */
var_dump(gzdecode($gzip) === $text); /* bool(true) */
var_dump(gzuncompress($zlib) === $text); /* bool(true) */
var_dump(gzinflate($roh) === $text); /* bool(true) */
Die Ausgabe zeigt drei verschiedene Köpfe, und daran erkennt man jedes Format wieder. Die folgende Tabelle beantwortet die eigentliche Frage: welche Entpackfunktion zu welcher Packfunktion passt.
| Funktion | Format | RFC | Kopfbytes | Entpackfunktion |
gzencode() | gzip | RFC 1952 | 1f8b08 | gzdecode() |
gzcompress() | zlib, mit Adler-32 | RFC 1950 | 789c | gzuncompress() |
gzdeflate() | roher Deflate, ohne Rahmen | RFC 1951 | wechselnd | gzinflate() |
Der Größenunterschied zwischen den Formaten ist gering und taugt nie als Entscheidungskriterium. Maßgeblich ist allein, was die Gegenstelle erwartet. Wer die Paare vertauscht, bekommt keinen brauchbaren Hinweis.
<?php
$zlib = gzcompress('Kompression in PHP');
/* Falsches Gegenstueck: gzcompress erzeugt kein gzip */
$ergebnis = gzdecode($zlib);
/* Warning: gzdecode(): data error
bool(false) */
var_dump($ergebnis);
/* Richtig, und der Rueckgabewert wird geprueft */
$ergebnis = gzuncompress($zlib);
if ($ergebnis === false) {
throw new RuntimeException('Daten liegen nicht im zlib-Format vor');
}
echo $ergebnis; /* Kompression in PHP */
Die Meldung data error klingt nach beschädigten Daten, gemeint ist aber ein anderes Format. Wer den Rückgabewert nicht auf false abfragt, rechnet unbemerkt mit einem leeren Wert weiter und sucht die Ursache später an ganz anderer Stelle.
Die Kompressionsstufen von PHP gzencode() im Vergleich
Der zweite Parameter reicht von 0 bis 9. Stufe 0 packt gar nicht und macht das Ergebnis sogar minimal größer als die Eingabe, Stufe 1 arbeitet am schnellsten, Stufe 9 am stärksten. Die Vorgabe -1 überlässt die Wahl der zlib-Bibliothek, und die entscheidet sich für 6. Statt zu raten, wird gemessen.
<?php
$roh = file_get_contents('korpus.txt');
foreach ([1, 3, 6, 9] as $stufe) {
$start = microtime(true);
$gepackt = gzencode($roh, $stufe);
$dauer = (microtime(true) - $start) * 1000;
printf(
"Stufe %d: %7d Bytes (%4.1f %%), %3.0f ms\n",
$stufe,
strlen($gepackt),
strlen($gepackt) / strlen($roh) * 100,
$dauer
);
}
/* Stufe 1: 348860 Bytes (21.2 %), 9 ms
Stufe 3: 307729 Bytes (18.7 %), 11 ms
Stufe 6: 259121 Bytes (15.7 %), 25 ms
Stufe 9: 255619 Bytes (15.5 %), 69 ms
Von 6 auf 9: 0,2 Prozentpunkte kleiner,
aber fast dreimal so viel Rechenzeit. */
| Stufe | Restgröße | Rechenzeit | Wofür geeignet |
| 1 | 21,2 Prozent | 9 ms | Daten im laufenden Betrieb |
| 3 | 18,7 Prozent | 11 ms | Stark belegte Server |
| 6 | 15,7 Prozent | 25 ms | Standard, zugleich Vorgabe der zlib-Bibliothek |
| 9 | 15,5 Prozent | 69 ms | Einmal gepackt, tausendfach ausgeliefert |
Die Zahlen stammen aus einem Lauf über 1.646.156 Bytes echten PHP-Quelltext. Von Stufe 1 auf Stufe 6 sind es 5,5 Prozentpunkte für 16 Millisekunden mehr, von 6 auf 9 nur noch 0,2 Prozentpunkte für fast die dreifache Rechenzeit. Damit ist die Frage nach der Stufe beantwortet: 6 bleibt stehen. Zwei Punkte an den Parametern von PHP gzencode überraschen trotzdem immer wieder.
<?php
/* Seit PHP 8 ein harter Fehler, keine stille Korrektur */
try {
gzencode('data', 12);
} catch (ValueError $e) {
echo $e->getMessage();
/* gzencode(): Argument #2 ($level)
must be between -1 and 9 */
}
/* Dritter Parameter: gzencode kann auch zlib ausgeben */
$alsGzip = gzencode('Testdaten fuer den Vergleich', 6, FORCE_GZIP);
$alsDeflate = gzencode('Testdaten fuer den Vergleich', 6, FORCE_DEFLATE);
echo bin2hex(substr($alsGzip, 0, 3)); /* 1f8b08, 46 Bytes */
echo bin2hex(substr($alsDeflate, 0, 2)); /* 789c, 34 Bytes */
/* Achtung: mit FORCE_DEFLATE gehoert das Ergebnis
in gzuncompress, nicht in gzdecode */
var_dump(gzuncompress($alsDeflate) !== false); /* bool(true) */
Eine Stufe außerhalb von -1 bis 9 wird seit PHP 8 nicht mehr zurechtgebogen, sondern beendet den Aufruf mit einem ValueError. Der dritte Parameter dreht das Ausgabeformat: mit FORCE_DEFLATE gibt PHP gzencode zlib statt gzip aus, das Ergebnis muss dann in gzuncompress(). Die moderneren Namen der Konstanten lauten ZLIB_ENCODING_GZIP und ZLIB_ENCODING_DEFLATE.
Große Dateien mit gzopen() und gzwrite() packen
Bei wenigen Megabyte fällt es nicht auf, bei mehreren Gigabyte sofort: PHP gzencode arbeitet auf einem String im Speicher, und gzencode(file_get_contents($pfad)) hält die Daten damit zweimal vor, einmal roh und einmal gepackt. Eine Quelldatei von 57,4 MB treibt die Speicherspitze auf rund 128 MB, und bei einem memory_limit von 32M bricht schon das Einlesen ab. Der blockweise Weg umgeht das.
<?php
$quelle = 'logs/gross.log'; /* 57,4 MB */
$ziel = 'logs/gross.log.gz';
/* Falsch bei grossen Dateien:
$gepackt = gzencode(file_get_contents($quelle), 6);
Speicherspitze rund 128 MB, bei memory_limit 32M
bricht schon file_get_contents mit einem
Fatal error ab. */
$ein = fopen($quelle, 'rb');
$aus = gzopen($ziel, 'wb6'); /* Stufe steht im Modus */
if ($ein === false || $aus === false) {
throw new RuntimeException('Datei nicht zu oeffnen');
}
while (!feof($ein)) {
gzwrite($aus, fread($ein, 65536));
}
gzclose($aus);
fclose($ein);
printf(
"%.1f MB => %.1f MB, Speicherspitze %.1f MB\n",
filesize($quelle) / 1048576,
filesize($ziel) / 1048576,
memory_get_peak_usage(true) / 1048576
);
/* 57.4 MB => 6.3 MB, Speicherspitze 2.0 MB */
Dieselbe Datei bleibt so bei 2,0 MB Speicherspitze und läuft auch mit knappem Limit durch. Die Stufe steht im Modus-String, wb6 bedeutet Schreiben mit Stufe 6. Beim Lesen gilt dasselbe Prinzip, dort stehen vier Wege zur Auswahl.
<?php
$ziel = 'logs/gross.log.gz';
/* Blockweise, das direkte Gegenstueck zu gzwrite */
$gz = gzopen($ziel, 'rb');
$gelesen = 0;
$treffer = 0;
while (!gzeof($gz)) {
/* gzread liefert entpackte Bytes, hier 64 KB je Durchgang */
$block = gzread($gz, 65536);
$gelesen += strlen($block);
$treffer += substr_count($block, 'ERROR');
}
gzclose($gz);
printf(
"%d Bytes entpackt, %d Treffer, Speicherspitze %.1f MB\n",
$gelesen,
$treffer,
memory_get_peak_usage(true) / 1048576
);
/* 60188322 Bytes entpackt, 221297 Treffer,
Speicherspitze 2.0 MB */
/* Zeilenweise, speicherschonend */
$gz = gzopen($ziel, 'rb');
while (($zeile = gzgets($gz)) !== false) {
if (str_contains($zeile, 'ERROR')) {
echo $zeile;
}
}
gzclose($gz);
/* Bequem, aber alles im Speicher: nur fuer kleine Dateien */
$zeilen = gzfile('logs/app.log.gz');
echo count($zeilen) . ' Zeilen';
/* Stream-Wrapper: funktioniert mit allen Dateifunktionen */
$inhalt = file_get_contents('compress.zlib://' . $ziel);
Die gemessenen Speicherspitzen an derselben Datei von 57,4 MB trennen die vier Wege deutlich: gzread() in 64-KB-Blöcken bleibt bei 2,0 MB, gzfile() liegt bei 104,0 MB für 885.273 Zeilen, und ein gzdecode(file_get_contents($ziel)) treibt sie auf 130,0 MB. gzfile() liest sich also bequem und taugt trotzdem nur für kleine Dateien.
Zwischen den beiden schlanken Wegen entscheidet die Form der Daten. gzread() liefert Bytes und weiß nichts von Zeilen, ein Suchbegriff kann also genau auf der Naht zwischen zwei Blöcken liegen und dabei verlorengehen. Wer zeilenweise auswertet, nimmt deshalb gzgets(); wer Bytes nur durchreicht, weiterschreibt oder stückweise an einen Parser gibt, fährt mit gzread() besser. In beiden Fällen beendet gzeof() die Schleife und gzclose() gibt den Griff wieder frei, genau wie fclose() bei einer gewöhnlichen Datei. Der Stream-Wrapper compress.zlib:// arbeitet daneben mit jeder gewöhnlichen Dateifunktion zusammen, auch innerhalb von fopen().
Praxis: Logdateien, Backups und Cache-Einträge
Textprotokolle schrumpfen typischerweise auf 10 bis 16 Prozent, damit ist die Rotation der lohnendste Einsatz. Eine Regel steht dabei über allem: das Original erst entfernen, wenn das Ergebnis geprüft ist.
<?php
function logDateiPacken(string $pfad, int $stufe = 6): string
{
$ziel = $pfad . '.gz';
$ein = fopen($pfad, 'rb');
$aus = gzopen($ziel, 'wb' . $stufe);
if ($ein === false || $aus === false) {
throw new RuntimeException('Datei nicht zu oeffnen: ' . $pfad);
}
while (!feof($ein)) {
gzwrite($aus, fread($ein, 65536));
}
gzclose($aus);
fclose($ein);
/* Gepackte Datei gegen das Original pruefen,
bevor irgendetwas geloescht wird */
$pruefsummeNeu = hash(
'sha256',
file_get_contents('compress.zlib://' . $ziel)
);
if (hash_file('sha256', $pfad) !== $pruefsummeNeu) {
unlink($ziel);
throw new RuntimeException('Pruefsumme weicht ab: ' . $pfad);
}
unlink($pfad);
return $ziel;
}
Der Vergleich der Prüfsummen stellt den entpackten Inhalt neben die Quelle, bevor etwas verschwindet. Auf Shared Hosting ist PHP gzencode oft der einzige Weg dorthin, weil exec() gesperrt ist und ein externer Packer damit ausfällt. Derselbe Aufbau taugt für nächtliche Datenbank-Dumps. Der zweite lohnende Fall ist der Cache.
<?php
$ergebnis = $repository->findeArtikel($filter); /* 500 Datensaetze */
$json = json_encode($ergebnis);
$datei = 'cache/artikel.json.gz';
file_put_contents($datei, gzencode($json, 6));
/* 60874 Bytes => 6077 Bytes, also 10,0 Prozent */
/* Beim Lesen: fehlende oder fremde Datei abfangen, ganz ohne @ */
$gepackt = is_file($datei) ? file_get_contents($datei) : false;
/* Die drei Kopfbytes aus dem Formatabschnitt als Eintrittskarte */
$istGzip = is_string($gepackt)
&& bin2hex(substr($gepackt, 0, 3)) === '1f8b08';
$roh = $istGzip ? gzdecode($gepackt) : false;
if ($roh === false) {
$roh = json_encode($repository->findeArtikel($filter));
file_put_contents($datei, gzencode($roh, 6));
}
$daten = json_decode($roh, true);
60.874 Bytes JSON werden mit Stufe 6 zu 6.077 Bytes, also 10,0 Prozent. Damit rechnet sich PHP gzencode überall dort, wo der Cache auf der Platte oder in einer Datenbank liegt. Eine fehlende Datei und eine, die gar kein gzip enthält, fängt schon die Prüfung der drei Kopfbytes ab. Ein beschädigter gzip-Strom kommt bis zu gzdecode() durch, liefert dort false und hinterlässt eine Warnung im Protokoll, und auch dann baut der Cache sich neu auf. Ein vorangestelltes @ hätte nur diese Spur beseitigt, den Fehler dagegen nicht.
Komprimierte Daten speichern und übertragen
Eine große JSON-Antwort lässt sich auch auf dem Weg zum Client verkleinern, allerdings nur, wenn der Client die Kompression vorher angekündigt hat.
<?php
$json = json_encode(['status' => 'ok', 'daten' => $datensaetze]);
$akzeptiert = $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '';
header('Content-Type: application/json; charset=utf-8');
/* Ohne diese Pruefung bekommt der Client Binaermuell */
if (str_contains($akzeptiert, 'gzip') && strlen($json) > 1024) {
$gepackt = gzencode($json, 6);
header('Content-Encoding: gzip');
header('Content-Length: ' . strlen($gepackt));
header('Vary: Accept-Encoding');
echo $gepackt;
} else {
header('Content-Length: ' . strlen($json));
echo $json;
}
/* Wichtig: viele Webserver packen die Antwort ohnehin.
Wer zusaetzlich selbst packt, komprimiert doppelt. */
Fehlt der Kopf Content-Encoding: gzip, zeigt der Empfänger Binärmüll an. Dazu ein Hinweis, der teuer werden kann: viele Webserver komprimieren die Antwort bereits selbst. Wer dann zusätzlich mit PHP gzencode arbeitet, packt doppelt und liefert kaputte Antworten aus. Der Selbstbau lohnt nur, wo der Server es nicht übernimmt oder genau eine Route betroffen ist. Beim Ablegen in der Datenbank entscheidet der Spaltentyp.
<?php
$json = json_encode($grosserDatensatz); /* 60874 Bytes */
$gepackt = gzencode($json, 6); /* 6077 Bytes */
/* Richtig: BLOB-Spalte, der Wert enthaelt Nullbytes */
$stmt = $pdo->prepare(
'INSERT INTO archiv (vorgang_id, nutzdaten) VALUES (?, ?)'
);
$stmt->bindValue(1, $vorgangId, PDO::PARAM_INT);
$stmt->bindValue(2, $gepackt, PDO::PARAM_LOB);
$stmt->execute();
/* Nur wenn wirklich eine Textspalte vorliegt */
$alsText = base64_encode($gepackt); /* 8104 Bytes */
/* 60874 => 6077 Bytes sind 90,0 Prozent Ersparnis,
60874 => 8104 Bytes nur noch 86,7 Prozent */
var_dump(gzdecode(base64_decode($alsText)) === $json); /* bool(true) */
Das Ergebnis von PHP gzencode ist binär und enthält Nullbytes und Steuerzeichen. In einer VARCHAR- oder TEXT-Spalte wird es stillschweigend beschädigt, und der Schaden fällt erst beim Entpacken auf. Richtig ist BLOB. Steht wirklich nur eine Textspalte bereit, hilft PHP base64_encode(), allerdings zum Preis von rund einem Drittel: aus 6.077 Bytes werden 8.104, die Ersparnis fällt von 90,0 auf 86,7 Prozent.
Wann sich Kompression nicht lohnt
Nicht jede Datenmenge wird kleiner. Bei bereits gepackten Formaten und bei sehr kurzen Werten fällt das Ergebnis sogar größer aus als die Eingabe, und beides begegnet einem ständig.
<?php
/* Bereits komprimierte Formate */
$bild = file_get_contents('img/tutorial.png'); /* 47208 Bytes */
echo strlen(gzencode($bild, 9)); /* 47236 Bytes, also groesser */
/* Sehr kurze Strings: der gzip-Rahmen kostet allein rund 20 Bytes */
echo strlen(gzencode('ok')); /* 22 statt 2 */
echo strlen(gzencode('Hallo Welt')); /* 30 statt 10 */
echo strlen(gzencode('2026-08-10 12:00:00')); /* 37 statt 19 */
/* Faustregel: erst ab einigen hundert Bytes Text
lohnt sich der Aufwand ueberhaupt. */
Bilder, Archive und Videos Eine PNG-Datei mit 47.208 Bytes wird zu 47.236 Bytes, eine WebP-Datei mit 24.316 Bytes zu 24.339 Bytes. Diese Formate bringen ihre eigene Kompression mit, es bleiben keine Muster, die Deflate durch Verweise ersetzen könnte. Dasselbe gilt für JPEG, ZIP und MP4.
Sehr kurze Werte Aus ok mit zwei Bytes werden 22, aus einem Zeitstempel mit 19 Bytes werden 37. Der gzip-Rahmen allein kostet rund 20 Bytes, und die holt kein kurzer Inhalt wieder herein.
Verschlüsselte Inhalte Ein Geheimtext sieht statistisch aus wie Zufall und hat deshalb nichts, woran sich Deflate festhalten könnte. Wo beides gebraucht wird, kommt die Kompression vor die Verschlüsselung, nie danach.
PHP gzencode(), ZIP-Archive und die Ausgabe an den Browser
Zwei Nachbarthemen werden hiermit regelmäßig verwechselt. gzip komprimiert genau einen Datenstrom und weiß nichts über Dateinamen oder Ordner. Wer mehrere Dateien mit Struktur zusammenfassen will, braucht ein Archiv, und dafür ist PHP ZipArchive zuständig. Die Kombination aus beidem heißt .tar.gz: tar bündelt die Dateien zu einem Strom, gzip komprimiert diesen Strom.
Das zweite Nachbarthema ist der Ausgabepuffer. ob_gzhandler packt die fertige Seitenausgabe auf dem Weg zum Browser, PHP gzencode packt einen Wert, den man selbst in der Hand behält und irgendwohin schreibt. Wie der Puffer arbeitet, zeigt das Tutorial zu PHP Output Buffering. Die Menge der Daten und das erwartete Format legen fest, welche der Packfunktionen am Ende übrig bleibt.
flowchart TD
A[Daten kleiner machen] --> B{Was genau?}
B -->|Mehrere Dateien| C[ZipArchive]
B -->|Ausgabe an Browser| D[ob_gzhandler]
B -->|String oder eine Datei| E{Wie gross?}
E -->|Sehr gross| F[gzopen plus gzwrite]
E -->|Passt in Speicher| G{Welches Format?}
G -->|gzip, RFC 1952| H[gzencode]
G -->|zlib, RFC 1950| I[gzcompress]
G -->|roh, RFC 1951| J[gzdeflate]
H --> K[Entpacken mit gzdecode]
I --> L[Entpacken mit gzuncompress]
J --> M[Entpacken mit gzinflate]
Fazit
PHP gzencode() macht aus einem String einen gzip-Strom, und gzdecode() macht ihn wieder lesbar. Drei Punkte tragen die tägliche Arbeit damit. Erstens die Zuordnung: gzip zu gzdecode(), zlib zu gzuncompress(), roher Deflate zu gzinflate(), wobei gzcompress() trotz seines Namens kein gzip erzeugt. Zweitens die Stufe: 6 ist gemessen der beste Kompromiss, 9 kostet fast die dreifache Rechenzeit für 0,2 Prozentpunkte. Drittens der Speicher: ab einer gewissen Dateigröße ersetzt gzopen() mit gzwrite() den Aufruf mit file_get_contents(), aus 128 MB Spitze werden 2 MB, und beim Lesen leistet gzread() mit gzeof() dasselbe. Dazu die ehrliche Grenze: bei JPEG, PNG, ZIP und sehr kurzen Werten bringt PHP gzencode nichts, dort wird das Ergebnis sogar minimal größer.