Weg mit den temporären Dateien!

Will man in PHP z.B. eine CSV-Datei erstellen, so geht das mit fputcsv() ganz einfach. Eine Herangehensweise ist sicherlich, diese Datei irgendwo temporär zu erstellen, und dann weiterzuverarbeiten. Das kann z.B. bedeuten, sie zum Download anzubieten, oder per Mail zu versenden.

Aber immer hat man danach diese Datei rumliegen, um die man sich kümmern muss. Oft baut man sich dann einen Task, der regelmäßig alte Dateien löscht (z.B. Änderungsdatum > 24h). Macht man das nicht, dann bleibt zwangsläufig Müll liegen, wenn z.B. zwischen Datei erstellen und Datei löschen der Programmablauf aufgrund eines Fehlers stoppt.

Das ist aber gefährlich, denn so macht man ein Tor auf für DOS-Attacken: Es muss nur jemand sehr häufig die Funktion aufrufen, die die Datei erzeugt, und mehr oder weniger schnell sind die Platten der Server voll. Zudem bedeutet jede Erzeugung Plattenzugriff, und wenn man Datenbank und Webserver auf der selben Maschine hat, dann knickt bei Last auch mal die I/O-Schiene weg.

Streams statt Files

Ergo: Irgendwie muss dieser Dateisystem-Zugriff weg! Dabei hilft, dass PHP Dateien als Streams abstrahiert, zwar nicht so schön wie .NET oder Java, aber immerhin C-like mit Hilfe eines Filehandles. Oder besser eines Stream-Handles, denn dahinter muss keine Datei stecken. Ein fopen() verträgt als Parameter nicht nur einen Dateipfad, sondern auch z.B. eine URL.

Das ist zwar im Zeitalter von HTTPS eher ein Antipattern, und sollte lieber über z.B. Guzzle gelöst werden.

Aber neben file:// und http:// gibt es auch weitere unterstützte Protokolle. Die vollständige Liste gibt es in der Dokumentation. Interessant für unseren Fall ist das php://-Protokoll.

Beispiel

$stream = fopen('php://memory', 'r+');
fputcsv($stream, ['Bla', 'Blubb'], ';', '"');

rewind($stream);
$content = stream_get_contents($stream);
fclose($stream);

Der Memory-Stream ist nur über das File-Handle erreichbar, ein zweites fopen('php://memory') liefert einen neuen Stream zurück. Standardfall ist wie im Beispiel: Stream öffnen, Daten reinschreiben, Schreib/Lese-Zeiger mittels rewind() wieder an den Anfang zurückschieben, und dann die Daten wieder auslesen.

Und schon spart man sich die Gedanken über die temporären Dateien. Nais!

Big Data

Wenn man erwartet, dass in den Memory-Stream mehr Daten rein fließen sollen, und man keine Möglichkeit hat, die Daten in kleinen Häppchen durch den Stream zu schieben, dann handelt man sich ein neues Problem ein: Bei vielen gleichzeitigen Zugriffen läuft der Speicher voll.

Dann ist Festplatte doch wieder eine gute Idee. Netterweise bietet PHP neben memory auch noch temp an. Der Unterschied: Erreicht der Stream eine definierbare Größe, dann werden die Daten auf Festplatte geschrieben, vollkommen transparent, und ohne dass man sich Gedanken um Datenhaltung machen muss.

$stream = fopen('php://temp/maxmemory:10485760');

Der Maxmemory-Parameter ist optional und standardmäßig auf 2MB gesetzt.

PHP erzeugt die temporären Dateien im Temp-Verzeichnis, und ich hoffe (ohne es getestet zu haben), dass auch bei Fehlern die Dateien problemlos wieder verschwinden.

About the author

People Enabler at CHECK24

Comments

  1. Nachtrag: Wenn man aus irgendeinem Grund eine temporäre Datei braucht, dann ist tmpfile() eine Alternative. Hier stellt PHP ebenfalls sicher, dass die Datei nach fclose() oder Skriptende wieder gelöscht wird.

    Mir fällt aber gerade kein Anwendungsfall ein, in dem das sinnvoller ist, denn wenn man die Datei nicht benötigt, dann ist das unnötige I/O-Last.

Comments are closed.