PHP/Tutorials/Datenspeicherung
Beim Entwickeln von Webapplikationen mit PHP stellt sich irgendwann die Frage, wo und wie Daten persistent gespeichert werden können. Dabei kann es sich um Nutzereingaben, eine kleine Log-Datei oder um die Inhalte eines CMS handeln.
Inhaltsverzeichnis
Speicherung von Daten in Dateien
Beim Speichern der Daten in Dateien müssen zwei grundlegende Fallstricke beachtet werden:
- Die Dateien müssen gesperrt werden, damit nicht zwei parallel laufende Programme gleichzeitig darauf zugreifen und die Daten somit korrumpieren.
- Die Daten-Dateien sollen in der Regel nicht vom Webserver ausgeliefert werden – eine Nutzerdatenbank sollte sich nicht auslesen lassen. Daher sollten die Dateien vorzugsweise außerhalb des Document-Roots liegen oder ersatzweise Zugriffsgeschützt sein. Das Speichern von Dateien außerhalb des Document-Roots ist allerdings robuster, weil es nur begrenzt von den weiteren Einstellungen des Webservers abhängig ist.
<?php
// Lesen:
$filename = '/path/to/file.txt';
$file = fopen($filename, 'r');
$data = '';
if(flock($file, LOCK_SH)) {
// etwas mit dem Inhalt der Datei tun...
$data = fread($file, filesize($filename));
flock($file, LOCK_UN);
}
fclose($file);
echo $data;
// Schreiben:
$file = fopen($filename, 'w');
$data = 'Hallo Leute!'.PHP_EOL.'Das hier ist ein Text!';
if(flock($file, LOCK_EX)) {
// etwas mit der Datei tun...
fwrite($file, $data);
flock($file, LOCK_UN);
}
fclose($file);
echo $data;
?>
Speicherung eines Werts pro Datei
Geht es beispielsweise um mehrere Texte, die an verschiedenen Stellen der Seite eingebunden werden und über ein Formular bearbeitbar sein sollen – fast schon ein kleines CMS, kann pro Text jeweils eine Datei pro Text in einem bestimmten Ordner angelegt werden.
Speicherung mehrerer Werte
Soll jedoch mehr als ein Wert, beispielsweise ein Array gespeichert werden, so ist es zweckmäßig, die Daten vor dem Speichern zu serialisieren, d. h. in ein speicherbares Format gebracht werden. Da es mit serialize()
und JSON bereits kompakte Formate gibt, ist die Selbstentwicklung eines Formats nicht zweckmäßig.
$values = [
'ersterSchlüssel' => 'ersterWert',
'einBoolean' => true,
'nichts' => null,
'ein Array im Array' => [1, 2, 3.3]
];
... mit JSON
JSON ist mit etwas Übung leicht zu lesen und von Hand zu bearbeiten. Zudem bringen viele Sprachen wie JavaScript – aus dem JSON auch ursprünglich hervorging – Parser für dieses Format mit, JSON ist somit ein ideales Austauschformat.
PHP besitzt zur Verarbeitung von JSON eine Sammlung von Funktionen, hier sei kurz die Verwendung von json_encode() sowie json_decode() gezeigt.
echo json_encode($values, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
json_encode()
wird mittels der Option JSON_PRETTY_PRINT
angewiesen, die Ausgabe der Lesbarkeit zuliebe einzurücken – im Produktivbetrieb würde man darauf verzichten, JSON_UNESCAPED_UNICODE
sorgt dafür, dass nicht in ASCII enthaltene Zeichen nicht escaped werden (aus ü
würde dann \u00fc
), das Setzen dieser Option spart Speicherplatz und ist nicht nötig, wenn die verarbeitenden Systeme mit UTF-8 umgehen können.{
"ersterSchlüssel": "ersterWert",
"einBoolean": true,
"nichts": null,
"ein Array im Array": [
1,
2,
3.3
]
}
$json = json_encode($values);
$values_from_json = json_decode($json);
// Überprüfen, ob Dekodieren erfolgreich war:
if(json_last_error() == JSON_ERROR_NONE) {
// hier sollte die Fehlerbehandlung erfolgen
// (z. B. Werte verwerfen o. ä.)
echo "json_decode() hat nicht funktioniert!";
}
// Funktioniert nicht, da in der Variable ein Objekt vom Typ „stdClass“ gespeichert ist
//echo $values_from_json['ersterSchlüssel'];
// auf Eigenschaften eines Objekts zugreifen:
echo $values_from_json->{'ersterSchlüssel'};
// alternativ in ein Array umwandeln:
$values_from_json = (array)$values_from_json;
echo $values_from_json['ersterSchlüssel'];
json_decode()
liefert ein Objekt, obwohl das JSON mit einem Array erstellt wurde. Dies muss beim Zugriff beachtet werden. Beispielhaft wird nach dem Dekodieren überprüft, ob Fehler aufgetreten sind. Je nach Anwendung und welcher Fehler genau (siehe PHP-Handbuch) aufgetreten sind, muss und sollte dieser geeignet behandelt werden.... mit serialize()
Die in PHP integrierte Serialisierungsfunktion sollte benutzt werden, wenn die Daten lediglich mit PHP weiterverarbeitet werden sollen. Hier bietet serialize() den Vorteil, dass die Daten unkompliziert wieder hergestellt werden können. Arrays bleiben Arrays und bei Objekten kann zudem die Zugehörigkeit zu Klassen gewahrt bleiben, da serialize() diese Information speichert.
$serialized = serialize($values);
echo $serialized.PHP_EOL;
$unserialized = unserialize($serialized);
var_export($unserialized);
a:4:{s:16:"ersterSchlüssel";s:10:"ersterWert";s:10:"einBoolean";b:1;s:6:"nichts";N;s:18:"ein Array im Array";a:3:{i:0;i:1;i:1;i:2;i:2;d:3.3;}}
array (
'ersterSchlüssel' => 'ersterWert',
'einBoolean' => true,
'nichts' => NULL,
'ein Array im Array' =>
array (
0 => 1,
1 => 2,
2 => 3.3,
),
)
unserialize()
übergeben, stattdessen sollte JSON als Format benutzt werden. Weitere Hinweise dazu sind in der PHP-Doku zu finden.... in einer CSV-Datei
Die Benutzung von CSV ist eine Option, wenn man eine Art Log-Datei schreiben möchte, an die jeweils ein Eintrag als neue Zeile ergänzt wird und es so vermieden wird, die gesamte Datei einzulesen und komplett neu zu schreiben, wie man es mit JSON und serialisierten PHP-Datentrukturen machen müsste. CSV lässt sich unkompliziert mit einer Tabellenkalkulation wie Microsoft Excel oder LibreOffice Calc öffnen und weiterverarbeiten.
Ein Problem bei der Weitergabe von CSV besteht darin, dass das Format nicht einheitlich definiert ist. Einigkeit besteht darin, dass irgendein Zeichen die einzelnen Werte eines Eintrags trennt und die Einträge in einer Zeile zusammengehören. Welches Zeichen die Einträge trennt, wie es escaped wird und welche Zeichenkodierung benutzt wird, legt die Anwendung fest.
Name,Anmeldedatum,Anreise mit dem... Hans Mustermann,10.12.2019,Auto Peter Lustig,11.12.2019,Zug Klaus Meier,30.12.2019,zu Fuß Laura Schmidt,31.12.2019,Longboard Hein Schlau,02.01.2020,"Rad, Auto oder sogenannter ""ÖPNV""
,
innerhalb eines Eintrags demonstriert.PHP bietet mit fgetcsv()
und fputcsv()
bequem zu benutzende Funktionen zum Lesen und Schreiben von CSV-Daten.
<?php
$filename = 'file.csv';
// CSV-Daten schreiben (falls die Datei noch nicht existiert, wird sie angelegt):
$file = fopen($filename, 'a');
$data = ['Louise Müller', '05.01.2020', 'noch nicht bekannt'];
if(flock($file, LOCK_EX)) {
fputcsv($file, $data);
flock($file, LOCK_UN);
}
fclose($file);
// CSV-Daten auslesen und in einer HTML-Tabelle ausgeben
$file = fopen($filename, 'r');
$data = '';
echo '<table>';
if(flock($file, LOCK_SH)) {
while(!feof($file)) {
$line = fgetcsv($file);
echo '<tr>';
foreach ($line as $value) {
echo '<td>'.htmlspecialchars($value).'</td>'.PHP_EOL;
}
echo '</tr>'.PHP_EOL;
}
flock($file, LOCK_UN);
}
fclose($file);
echo '</table>';
Das Resultat:
Name | Anmeldedatum | Anreise mit dem... |
Hans Mustermann | 10.12.2019 | Auto |
Peter Lustig | 11.12.2019 | Zug |
Klaus Meier | 30.12.2019 | zu Fuß |
Laura Schmidt | 31.12.2019 | Longboard |
Hein Schlau | 02.01.2020 | Rad, Auto oder sogenannter "ÖPNV" |
Louise Müller | 05.01.2020 | noch nicht bekannt |
Speicherung in einer Datenbank
Sobald man in den Daten suchen, diese sortieren oder mehrere Tabellen verknüpfen möchte, dann wird eine Datenbank benötigt. Natürlich können diese Operationen auch bei der Speicherung in Dateien implementiert werden, nur würde hier unnötigerweise das Rad neu erfunden.
Client-Server-Anwendung
Klassische Datenbanksysteme wie MySQL, MariaDB oder PostgreSQL sind als Server konzipiert, die Webapplikation bildet den Client, der die SQL-Abfragen stellt und die erhaltenen Daten verarbeitet. Von Vorteil ist hier, dass der Datenbank-Server nicht zwingend auf dem gleichen System wie die Webapplikation laufen muss und über das Netzwerk auf ihn zugegriffen werden kann. Zudem kann die Datenbank zwischen mehreren Servern redundant verfügbar gehalten werden (Replikation) und erreicht somit potenziell eine hohe Leistung. Außerdem kann je nach Leistung der Datenbank-Servers eine große Anzahl von parallelen Anfragen verarbeitet werden.
Als Nachteil ist die zusätzlich nötige Installation und Konfiguration eines Systems zu nennen. Des Weiteren muss zum Erstellen eines Datenbank-Backups aus der Datenbank gelesen und das Backup in eine Datei geschrieben werden.
Datenbank in einer Datei
Das Datenbanksystem SQLite speichert seine Daten in einer einzigen Datei. Außerdem benötigt es keinen permanent laufenden Server, sondern liest seine Datenbank bei jedem Aufruf neu ein. Oft wird SQLite daher in Programmen zur Verwaltung von lokal gespeicherten Daten benutzt. Die SQLite-Website listet Anwendungsfälle und ihre Eigenheiten auf.
Vorteile: Auf die Daten kann mit SQL zugegriffen werden. Komplexe Operationen auf den Daten führt SQLite aus und müssen vom Programmierer nicht selbst implementiert werden. Die Webapplikation lässt sich dennoch wie bei der Verwendung von blanken Dateien als Datenspeicher einfach durch das Kopieren von Dateien umziehen. Außerdem ist es ein Leichtes, Backups von der Datenbank zu machen und diese wieder einzuspielen.
Nachteile: Bei komplexen und vielen Anfragen muss SQLite die Datenbank-Datei unter Umständen sperren, wenn beispielsweise Daten geschrieben werden sollen – es kann dann ggf. nur eine Abfrage gleichzeitig verarbeitet werden.
fopen()
finden Sie in der PHP-Doku.