mitmario.dev

Eine Datei schreiben

PHP Sandbox 3 Min Lesezeit 5 BeispieleLektion 2 von 7

Lesen war die eine Hälfte, Schreiben ist die andere. Und weil du dabei etwas hinterlässt, das den Lauf überdauert, ist es zugleich die erste Stelle im Kurs, an der ein Fehler nicht einfach mit dem Programmende verschwindet.

file_put_contents, und was es mit dem Vorhandenen macht

Schreiben legt neu an und überschreibt
<?php

file_put_contents("notiz.txt", "Erster Satz.\n");
echo "Nach dem ersten Schreiben:\n";
echo file_get_contents("notiz.txt");

file_put_contents("notiz.txt", "Zweiter Satz.\n");
echo "\nNach dem zweiten Schreiben:\n";
echo file_get_contents("notiz.txt");

echo "\nVom ersten Satz ist nichts uebrig.\n";

file_put_contents() nimmt zwei Dinge: wohin, und was. Gibt es die Datei noch nicht, wird sie angelegt. Gibt es sie schon, wird ihr Inhalt ersetzt. Ohne Rückfrage, ohne Papierkorb, ohne Warnung.

Das ist keine Bosheit, sondern die Voreinstellung, die man in neun von zehn Fällen will: eine Zwischendatei neu schreiben, einen Zwischenstand ablegen, gleich in Lektion 8.3 eine JSON-Datei zurückschreiben. Man muss es nur wissen, bevor man es an der falschen Datei ausprobiert.

FILE_APPEND hängt an, statt zu ersetzen

Anhängen mit FILE_APPEND
<?php

file_put_contents("notiz.txt", "Erster Satz.\n");
file_put_contents("notiz.txt", "Zweiter Satz.\n", FILE_APPEND);
file_put_contents("notiz.txt", "Dritter Satz.\n", FILE_APPEND);

echo file_get_contents("notiz.txt");

$zeilen = file("notiz.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
echo "\n", count($zeilen), " Zeilen stehen jetzt drin.\n";

Das dritte Argument ist ein Schalter, und der wichtigste davon heißt FILE_APPEND. Damit landet dein Text hinter dem, was schon dasteht.

Das ist die Form, die ein Protokoll braucht. Jede Zeile kommt dazu, keine geht verloren, und du musst die Datei vorher nicht lesen. Genau das ist die Challenge dieser Lektion.

Der Rückgabewert, den fast niemand ansieht

Der Rückgabewert sagt, ob es geklappt hat
<?php

$geschrieben = file_put_contents("notiz.txt", "Hallo\n");
echo "Erster Versuch:  ";
var_dump($geschrieben);

echo "Zweiter Versuch: ";
$misslungen = file_put_contents("gibtsnicht/notiz.txt", "Hallo\n");
var_dump($misslungen);

echo "\nUnd so faengt man das ab:\n";
if ($misslungen === false) {
    echo "Konnte nicht geschrieben werden.\n";
}

file_put_contents() gibt die Zahl der geschriebenen Bytes zurück, und im Fehlerfall false. Das ist genau das Muster aus Lektion 7.4: eine alte Funktion, die auf einen Fehler mit einem Wert antwortet, den man übersehen kann.

Und übersehen wird er ständig. Im Beispiel scheitert der zweite Aufruf, weil es das Verzeichnis nicht gibt. PHP schreibt dazu eine Warning auf den Fehlerkanal, liefert false, und das Programm läuft munter weiter, als sei die Datei geschrieben worden.

Achte beim Prüfen auf ===. Eine leere Datei zu schreiben ist erlaubt und liefert 0, und 0 ist in einem if falsch. Wer mit if (!$geschrieben) prüft, hält den geglückten Schreibvorgang für einen Fehlschlag. Die Falsch-Liste aus Lektion 2.5, an einer Stelle, an der sie wehtut.

Rechte und Verzeichnisse

Auf deinem eigenen Rechner klappt Schreiben fast immer. Auf einem Hosting-Paket ist es die häufigste Ursache für „geht nicht”, und die Gründe sind jedes Mal dieselben zwei:

Das Verzeichnis muss es geben. PHP legt keine Ordner nebenbei an. Wer nach daten/log.txt schreiben will, braucht vorher ein daten. Anlegen geht mit mkdir("daten"), und mit mkdir("a/b/c", recursive: true) auch mehrstufig.

Der Webserver muss dort schreiben dürfen. Deine Dateien gehören dir, der PHP-Prozess läuft aber unter einem eigenen Benutzer. Deshalb sind auf gemieteten Servern die Verzeichnisse, in die etwas geschrieben wird, meist eigens dafür angelegt und liegen außerhalb dessen, was der Besucher aufrufen kann. Warum das keine Schikane ist, siehst du in Abschnitt 15.

Der vergessene Zeilenumbruch

Der vergessene Zeilenumbruch
<?php

foreach (["Milch", "Brot", "Kaffee"] as $ware) {
    file_put_contents("liste.txt", $ware, FILE_APPEND);
}

echo "So sieht die Datei aus:\n";
echo file_get_contents("liste.txt"), "\n";

$zeilen = file("liste.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
echo "\nUnd so viele Zeilen zaehlt PHP: ", count($zeilen), "\n";

Der häufigste Anfängerfehler beim Anhängen, und er sieht harmlos aus: Drei Wörter werden angehängt, und in der Datei steht MilchBrotKaffee. PHP hat alles richtig gemacht, es hat genau das geschrieben, was dastand.

Ein Zeilenumbruch ist ein Zeichen und entsteht nicht von selbst. Wer Zeilen anhängt, schreibt das \n mit. Am Ende, nicht am Anfang: sonst fängt die Datei mit einer Leerzeile an.

Im Terminal siehst du das auch ohne PHP: wc -l liste.txt zählt Zeilenumbrüche und sagt 0. Und weil das Beispiel anhängt, steht nach einem zweiten php index.php der Text zweimal hintereinander in der Datei. Der Prüfknopf räumt vor jedem Lauf auf, das Terminal nicht.

Löschen

Wieder wegräumen
<?php

file_put_contents("temp.txt", "Nur kurz.\n");
echo "Da:  ", var_export(file_exists("temp.txt"), true), "\n";

echo "Loeschen sagt: ", var_export(unlink("temp.txt"), true), "\n";
echo "Da:  ", var_export(file_exists("temp.txt"), true), "\n";

echo "\nUnd noch einmal loeschen:\n";
echo "Loeschen sagt: ", var_export(@unlink("temp.txt"), true), "\n";

unlink() löscht eine Datei und gibt true oder false zurück. Auf eine Datei, die es nicht gibt, antwortet es mit false und einer Warning, genau wie das Lesen in Lektion 8.1.

Das @ im letzten Aufruf des Beispiels kennst du aus Lektion 7.1: Es unterdrückt die Meldung, nicht das Problem. Hier ist es einmal vertretbar, weil das Ergebnis danach wirklich geprüft wird. Die sauberere Form bleibt trotzdem, vorher mit file_exists() zu fragen.

Zum Mitnehmen

Alles, was deine Programme bisher gerechnet haben, war beim letzten Zeichen der Ausgabe wieder weg. Eine Zeile ändert das, und dieselbe Zeile löscht mit einem falschen Schalter deine Daten.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 5 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.