MySQL / PHP: PDO-Tutorial – Grundlagen

Auch wenn die veralteten mysql_* Funktionen in den aktuellen PHP-Versionen mittlerweile nicht mehr nur „deprecated“ sind sondern komplett entfernt wurden, kursieren im Internet natürlich immer noch etliche veraltete Tutorials, welche diese noch benutzen, was wiederum dazu führt, dass man immer noch regelmäßig in diversen Foren etc Beiträge von Usern findet, deren Codes dadurch natürlich nicht funktionieren.

Als aktuelle Alternativen gibt es nun zum einen MySQLi und zum anderen PDO, wobei ich hier nur auf PDO eingehen möchte, da ich das einerseits selbst verwende und es zudem auch noch einige Vorteile gegenüber MySQLi bietet. So ist nämlich PDO nicht auf MySQL Datenbanken beschränkt sondern man kann damit auch mit z.B. PostgreSQL, SQLite, Firebird u.v.m. arbeiten, mehr dazu siehe hier. Das kann vor allem von Vorteil sein, wenn man bei einem Projekt die genutzte Datenbank wechseln will, z.B. von MySQL zu PostgreSQL oder ähnliches, ein solcher Umstieg würde mit PDO deutlich weniger aufwändig ausfallen als mit MySQLi.

PDO – Datenbankverbindung aufbauen

  $dbuser = 'username';
  $dbname = 'database';
  $dbpass = 'password';
  $dbhost = 'localhost';
  $options = array();

  $server = 'mysql:host='. $dbhost .';dbname='. $dbname;
  $db = new PDO($server, $dbuser, $dbpass, $options);

Hiermit hat man nun schon einmal grundsätzlich die Datenbankverbindung aufgebaut, über den $options Array kann man dabei noch weitere Einstellungen vornehmen, welche man allerdings auch nachher noch setzen kann, da ich letzteres bevorzuge habe ich den $options Array hier nur der Vollständigkeit halber mit eingefügt, um die Einstellungen nachher vorzunehmen geht man z.B. folgendermaßen vor:

  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

Letzteres setzt übrigens den „Fetchmode“ auf Object, also die Ergebnisse einer Abfrage werden dann als Objekt zurückgegeben statt als Array, gefällt mir persönlich einfach besser (einfacher zu tippen^^) und stelle das nur um, wenn ich explizit einen Array benötige, aber wenn man das nicht möchte, dann kann man diese Zeile natürlich einfach weglassen, der Standard-Fetchmode ist dann Array.

PDO – Einfache SELECT Abfragen

Die Statements selber sind grundsätzlich natürlich genauso aufgebaut wie bei mysql_* / mysqli_* (mal abgesehen von Prepared Statements, aber dazu später mehr), nur die Ausführung sieht geringfügig anders aus:

  $sql = "SELECT feld_a, feld_b, feld_c FROM tabelle WHERE id = 1";
  $result = $db->query($sql);
  
  foreach ($result as $r) {
    echo $r->feld_a;
    ...
  }

PDO – INSERT, UPDATE und DELETE Statements

Auch hier unterscheidet sich PDO im Grunde nur in der Ausführung, man benutzt hier die exec() Methode, welche im Gegensatz zu mysql(i)_* übrigens direkt die Anzahl der betroffenen Zeilen zurückliefert, man muss diese also nicht mehr explizit ermitteln:

  $sql = "UPDATE tabelle SET feld_a = 'x' WHERE id > 5";
  $rows = $db->exec($sql);
  echo $rows .' Datensätze aktualisiert.';

PDO – ID des letzten eingefügten Datensatzes ermitteln

Auch dies funktioniert bei PDO mit Hilfe der Methode lastInsertId() mindestens genauso einfach wie mit mysql(i)_*:

  $sql = "INSERT INTO tabelle SET feld_a = 'a', feld_b = 'b'";
  $db->exec($sql);
  $last_id = $db->lastInsertId();

PDO – Mehr Sicherheit mit Prepared Statements

Während man seine Statements / Variablen mit mysql_* noch „manuell“ mit Hilfe von z.B. mysql_real_escape_string() bearbeiten musste um SQL-Injections zu verhindern, sollte man bei PDO am besten grundsätzlich mit den sogenannten „Prepared Statements“ arbeiten, dadurch werden SQL-Injections quasi von vornherein ausgeschlossen. Dabei benutzt man eigene Variablen innerhalb der Statements und weist diesen dann, entweder gezielt, oder via Array mehrere auf einmal, die entsprechenden Werte / PHP Variablen zu. Die Array-Variante mag dabei zwar etwas kürzer sein, aber da sie imho recht unübersichtlich ist und ich sie selbst in der Regel nicht benutze, werde ich von dieser nur ein kurzes Beispiel anfügen.

Mit einem Array könnte ein Prepared Statement z.B. so aussehen:

$sql = "SELECT * FROM tabelle WHERE feld_a = ? AND feld_b = ?";
$stmt = $db->prepare($sql);
$stmt->execute(array($val_a, $val_b)); 

Wie man sieht kann man da, vor allem wenn es deutlich mehr Felder sind, schnell mal damit durcheinander geraten, welcher Wert nun zu welchem Feld gehört, daher bevorzuge ich die folgende Methode, auch wenn es etwas mehr Tipperei bedeutet:

$sql = "SELECT * FROM tabelle WHERE feld_a = :feld_a AND feld_b = :feld_b";
$stmt = $db->prepare($sql);
$stmt->bindValue(':feld_a', $val_a, PDO::PARAM_INT);
$stmt->bindValue(':feld_b', $val_b, PDO::PARAM_STR);
$stmt->execute();

Wenn man hier die Variablen passend benennt sind Irrtümer fast ausgeschlossen, zudem kann man noch bei jedem Feld explizit festlegen, was für ein Datentyp in der Variable stehen soll, in diesem Fall also z.B. ein Integer in feld_a und ein String in feld_b (wobei String der Standardtyp ist), enthält eine übergebene Variable einen falschen Datentyp, so erhält man eine Fehlermeldung.

Ich habe mir mittlerweile angewöhnt, konsequent nur noch letzeres zu verwenden, selbst wenn ich keine Variablen aus Usereingaben benutze, wenn man das einmal so verinnerlicht hat, dann muss man sich über SQL-Injections quasi keine Gedanken mehr machen.

PDO – Error Handling / Fehlerbehandlung

Es gibt 3 verschiedene Modi um mit PDO Fehler zu behandeln:

1. PDO::ERRMODE_SILENT
– Hier muss man den Fehler explizit mit Hilfe von $db->errorInfo(); abfragen um nähere Informationen zu erhalten, es wird auch keine Meldung ausgegeben.

2. PDO::ERRMODE_WARNING
– In diesem Modus werden Fehlermeldungen als PHP Warnung ausgegeben

3. PDO::ERRMODE_EXCEPTION
– Dies ist der empfohlene Modus, hier wird bei einem Fehler eine Exception geworfen, welche man wie gewohnt via try/catch abfangen kann um sie z.B. mitzuloggen, die Fehlermeldung in einem eigenen Format auszugeben oder ähnliches.

Beispiel:

try {
    $db->query('blubb'); // Fehlerhaftes Query
} catch(PDOException $ex) {
    echo "Es ist ein Fehler aufgetreten!"; // Einfache Ausgabe für den User
    my_error_logger($ex->getMessage()); // Speichern der kompletten Fehlermeldung mittels einer eigenen Log-Funktion
}
, , , , , , markiert

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.