mitmario.dev

JSON

PHP Sandbox 3 Min Lesezeit 5 BeispieleLektion 3 von 7

Ein Array in eine Datei zu schreiben klingt nach einer Aufgabe für einen Nachmittag: Trennzeichen ausdenken, Sonderfälle bedenken, Einlesefunktion bauen. Das ist alles schon erledigt, und zwar in einem Format, das außer PHP auch JavaScript, Python und jede Schnittstelle im Netz versteht.

Das Paar: json_encode und json_decode

Hin und wieder zurück
<?php

$warenkorb = [
    "kunde" => "Mia",
    "artikel" => ["Kaffee", "Brot"],
    "summe" => 1245,
];

$text = json_encode($warenkorb);
echo "Als Text:\n", $text, "\n";
echo "Typ: ", get_debug_type($text), "\n";

$zurueck = json_decode($text, true);
echo "\nUnd wieder zurueck:\n";
var_dump($zurueck);

json_encode() macht aus deinem Array eine Zeichenkette. json_decode() macht daraus wieder ein Array. Mehr ist es im Kern nicht, und für alles, was du in diesem Kurs speicherst, reicht dieses Paar.

Sieh dir das Ergebnis der ersten Zeile an: Der ganze Warenkorb steht in einer Zeile, mit geschweiften Klammern für Zuordnungen und eckigen für Listen. Genau der Unterschied aus Lektion 5.2, nur diesmal sichtbar im Text.

Das zweite Argument entscheidet, was zurückkommt

Mit true ein Array, ohne true etwas anderes
<?php

$text = '{"kunde": "Mia", "summe": 1245}';

echo "Ohne zweites Argument:\n";
$objekt = json_decode($text);
var_dump($objekt);
echo "Zugriff: ", $objekt->kunde, "\n";

echo "\nMit true:\n";
$array = json_decode($text, true);
var_dump($array);
echo "Zugriff: ", $array["kunde"], "\n";

json_decode($text) gibt dir kein Array zurück, sondern ein Objekt. Der Zugriff sieht dann $objekt->kunde aus statt $objekt["kunde"], und count(), foreach mit Schlüssel und alles andere aus Abschnitt 5 verhält sich anders.

Objekte kennst du seit Lektion 6.7, hier willst du aber keine. Was du mit frisch gelesenen Daten vorhast, sind die Array-Funktionen aus Abschnitt 5, und die arbeiten mit Arrays. Deshalb steht in diesem Kurs überall true als zweites Argument, und deshalb solltest du es dir angewöhnen: Solange du nicht ausdrücklich Objekte willst, willst du Arrays.

Damit die Datei lesbar bleibt

Lesbar für Menschen
<?php

$daten = ["ware" => "Müsli", "menge" => 3, "bio" => true];

echo "So klein wie moeglich:\n";
echo json_encode($daten), "\n";

echo "\nMit Einrueckung:\n";
echo json_encode($daten, JSON_PRETTY_PRINT), "\n";

echo "\nMit Einrueckung und echten Umlauten:\n";
echo json_encode($daten, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n";

Standardmäßig schreibt json_encode so kurz wie möglich: keine Umbrüche, keine Leerzeichen. Für eine Übertragung übers Netz ist das richtig, für eine Datei, in die du selbst noch einmal hineinsiehst, nicht.

Zwei Schalter helfen, und beide gehören zusammen:

  • JSON_PRETTY_PRINT setzt Umbrüche und rückt mit vier Leerzeichen ein.
  • JSON_UNESCAPED_UNICODE lässt Umlaute als Umlaute stehen. Ohne den Schalter macht PHP aus dem ü die Ersatzschreibweise \u00fc, aus Müsli wird also M\u00fcsli. Beides ist gültiges JSON und wird beim Einlesen wieder dasselbe, nur lesen kann ein Mensch nur das eine. Ohne Datei geht das auch: php -r 'echo json_encode("Müsli");' im Terminal antwortet mit "M\u00fcsli".

Der senkrechte Strich dazwischen ist derselbe wie bei den Dateischaltern in Lektion 8.1.

Wenn das JSON kaputt ist

Wenn das JSON kaputt ist
<?php

$kaputt = '{"kunde": "Mia", "summe": }';

echo "Ohne Schalter:\n";
$still = json_decode($kaputt, true);
var_dump($still);
echo "Nachgefragt: ", json_last_error_msg(), "\n";

echo "\nMit JSON_THROW_ON_ERROR:\n";
try {
    json_decode($kaputt, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $fehler) {
    echo "Gefangen: ", $fehler->getMessage(), "\n";
    echo "Code:     ", $fehler->getCode(), "\n";
}

Und jetzt der Anschluss an Abschnitt 7. Bekommt json_decode etwas, das kein gültiges JSON ist, gibt es stillschweigend null zurück. Keine Warning, kein Fehler, nichts auf dem Fehlerkanal. Dein Programm arbeitet mit null weiter, und der Fehler taucht drei Funktionen später als etwas ganz anderes auf.

Um zu erfahren, was los war, müsstest du von dir aus json_last_error_msg() fragen. Genau das ist das Muster aus Lektion 7.4: eine Antwort, die man übersehen kann, wird übersehen.

Das vierte Argument räumt damit auf. JSON_THROW_ON_ERROR macht aus dem stillen null eine JsonException, und die kannst du fangen wie jede andere Ausnahme. Die Meldung lautet Syntax error, der Code ist 4.

Das dritte Argument, das dazwischensteht, ist die maximale Verschachtelungstiefe. 512 ist der Standardwert, und du schreibst ihn nur hin, weil du an das vierte herankommen willst. Wer die Schreibweise mit den benannten Argumenten aus Lektion 6.3 vorzieht, kommt mit json_decode($text, true, flags: JSON_THROW_ON_ERROR) zum selben Ziel.

Der ganze Weg an einem Stück

Lesen, ergänzen, zurückschreiben
<?php

$daten = json_decode(file_get_contents("lager.json"), true, 512, JSON_THROW_ON_ERROR);

echo "Vorher: ", count($daten["waren"]), " Ware\n";

$daten["waren"][] = ["name" => "Müsli", "menge" => 3];

file_put_contents(
    "lager.json",
    json_encode($daten, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);

echo "Nachher: ", count($daten["waren"]), " Waren\n";
echo "\nSo sieht die Datei jetzt aus:\n";
echo file_get_contents("lager.json"), "\n";

Lesen, umbauen, zurückschreiben. Das ist das Muster, mit dem eine kleine Anwendung ohne Datenbank auskommt, und es ist genau das, was du gleich in der Challenge baust.

Beachte die Reihenfolge im Beispiel: Erst wird die Datei vollständig gelesen, dann im Speicher verändert, dann in einem Rutsch zurückgeschrieben. Nicht Stück für Stück in die Datei hinein. So bleibt sie in jedem Moment gültiges JSON, und wenn zwischendrin etwas schiefgeht, steht dort noch der alte Stand.

Und weil die Datei danach anders ist als davor, fängt ein zweites php index.php im Terminal mit zwei Waren an statt mit einer: Das Beispiel arbeitet auf dem Stand, den es selbst hinterlassen hat. Der Prüfknopf setzt die Datei vor jedem Lauf zurück, das Terminal nicht.

Zum Mitnehmen

Ein Array lebt nur, solange dein Skript läuft. JSON ist der übliche Weg, es auf die Festplatte zu legen und beim nächsten Lauf unverändert zurückzubekommen, und zwar in einem Format, das jede andere Sprache auch versteht.

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.