Die PDO
-Klasse ist eine tolle Klasse, um Datenbankabfragen über PHP gleich viel sicherer zu machen. Wer bisher nur die prozeduralen MySQL-Funktion oder die (auch) objektorientierte MySQLi-Klasse verwendet hat, der muss sich ein kleines bisschen umstellen. PDO hört sich aber komplizierter an als es ist. Ich versuche hier nun mal die PDO-Klasse so gut es geht verständlich zu erklären.
Was ist die PDO-Klasse?
Die PDO-Klasse ist die Profi-Klasse für sichere Datenbankabfragen. Mit PDO kann man wie mit den anderen beiden auf herkömmliche Weise Datenbankabfragen an einen MySQL-Server schicken. Der Unterschied ist nur die Syntax.
Damit die Abfragen auch sicher und vor SQL-Injections geschützt sind, reicht es aber nicht aus, die normale query
-Methode zu benutzen. Diese eignet sich hervorragend für Abfragen, bei denen keine fremden Eingaben in die Abfrage geschrieben werden. Für Abfragen mit fremden Inhalten sollte immer die prepare
-Methode verwendet werden.
PDO-Verbindung zur Datenbank aufbauen
Bevor wir Abfragen an den Datenbankserver schicken können, muss zuerst eine Verbindung aufgebaut werden. Also alles schön der Reihe nach damit auch nichts ausbleibt. PDO kann übrigens nicht wie bei MySQLi objektorientiert und prozedural verwendet werden, sondern nur objektorientiert. Deshalb ist es sehr von Vorteil, wenn man bereits Erfahrung mit der Syntax der objektorientierten Programmierung in PHP hat.
Im Folgenden schreiben wir das Konfigurationsarray, bauen die Verbindung auf und fangen die PDOException
, falls es einen Fehler beim Verbindungsaufbau gab. Im nächsten Abschnitt folgt dann die erste Abfrage an die Datenbank.
$dbconfig['host'] = 'localhost';
$dbconfig['user'] = 'username';
$dbconfig['base'] = 'database';
$dbconfig['pass'] = 'password';
$dbconfig['char'] = 'utf8';
try {
$pdo = new PDO('mysql:host='.$dbconfig['host'].';dbname='.$dbconfig['base'].';charset='.$dbconfig['char'].';', $dbconfig['user'], $dbconfig['pass']);
}
catch(PDOException $e) {
exit('Unable to connect Database.');
}
PDO-Abfrage mit der query
-Methode
Da die Datenbankverbindung aufgebaut und das PDO
-Objekt instanziert wurde, kann es auch schon mit der ersten simplen Abfrage an die Datenbank los gehen. Im Beispiel frage ich einfach einen Wert von einer fiktiven Tabelle ab und lasse ihn ausgeben. Vorher müssen die Daten natürlich noch gefetcht werden.
Beim Fetchen kann man sich ein normales Array, ein assoziatives Array, ein Objekt und noch ein paar weitere Typen zurückgeben lassen. Ich verwende jetzt im gesamten Artikel zur Übersichtlichkeit die normale fetch
-Methode.
// Wenn ein einziger Datensatz erwartet wird
$stmt = $pdo->query('SELECT foo FROM bar WHERE id = 1');
$row = $stmt->fetch();
echo $row['foo'];
// Wenn mehrere Datensätze erwartet werden
$stmt = $pdo->query('SELECT foo FROM bar');
while($row = $stmt->fetch()) {
echo $row['foo'].'<br />';
}
Im vorherigen Beispiel wurden zwei simple Abfragen ausgeführt. Einmal mit einem einzigen Datensatz und einmal mit mehreren Datensätzen, die zurückgegeben werden. Allerdings ist diese Methode nicht sicher vor fremden Benutzereingaben. Würde man beispielsweise dort einen GET
-Parameter hart reinschreiben, dann wäre dort auf jeden Fall eine SQL-Injection möglich. Diese Abfrageart eignet sich also nur für Abfragen, bei denen wirklich keine Eingaben von außen in den Query gelangen können.
PDO: Sichere Abfragen mit Prepared Statements
Um zu verhindern, dass böse Jungs SQL-Injections ausführen, können wir Prepared Statements benutzen. Die prepare
-Methode sollte man auch immer dann nutzen, wenn fremde Eingaben mit in den Query geschrieben werden.
Bei Prepared Statements wird anstatt der Variable eine Kennzeichnung an die entsprechende Stelle des Querys geschrieben. Die SQL-Abfrage wird danach über eine spezielle Methode namens execute
abgeschickt, an die dann ein Array mit den Kennzeichnungen und den tatsächlichen Werten übergeben wird.
$stmt = $pdo->prepare('SELECT foo, bla FROM bar WHERE id = :paramEins AND bla = :paramZwei');
$array = array(
':paramEins' => $_GET['id'],
':paramZwei' => $_GET['bla']
);
$stmt->execute($array);
while($row = $stmt->fetch()) {
echo $row['foo'].'<br />';
echo $row['bla'].'<br />';
}
Meiner Meinung nach sollte das Beispiel schon verständlich genug sein. Es wird lediglich die prepare
-Methode anstatt der query
-Methode genutzt, und um den SQL-Befehl an die Datenbank zu schicken wird execute
aufgerufen. An execute
wird dann ein Array übergeben, das die Kennzeichnungen und die dazugehörigen Werte enthält.
Der Rest funktioniert genau so, wie ganz oben bereits erklärt.
PDO Prepared Statements mit bindParam
Ähnlich wie im vorherigen Abschnitt lassen sich mit der bindParam
-Methode ebenfalls sichere SQL-Abfragen ausführen. Allerdings ist diese Methode etwas komplizierter. Man kann hier ebenfalls Platzhalter setzen, muss aber als zweiten Parameter eine Variable übergeben. Man darf keine Werte direkt als zweiten Parameter übergeben, da dies sonst einen Fatal Error hervorruft. Als dritten Parameter kann man optional noch einen Datentyp angeben. Und wenn man will auch noch einen vierten vom Typ Integer, der die Länge enthält.
$stmt = $pdo->prepare('SELECT * FROM foo WHERE bar = :eins AND bla = :zwei');
$stmt->bindParam(':eins', $_GET['bar']);
$stmt->bindParam(':zwei', $_GET['bla']);
$stmt->execute();
Das Gleiche funktioniert aber nicht nur mit selbstdefinierten Platzhaltern, sondern auch mit Fragezeichen. Welche Variante man benutzt, bleibt jedem selbst überlassen. Mit den Fragezeichen wird es für mich ein bisschen übersichtlicher, als mit selbstdefinierten Platzhaltern.
$stmt = $pdo->prepare('SELECT * FROM foo WHERE bar = ? AND bla = ?');
$stmt->bindParam(1, $_GET['bar']);
$stmt->bindParam(2, $_GET['bla']);
$stmt->execute();
PDO Prepared Statements mit bindValue
Diese Methode funktioniert genau so wie die bindParam
-Methode. Nur ist man bei der bindValue
-Methode nicht gezwungen, eine Variable (also eine Referenz) als zweiten Parameter zu übergeben.
Man kann hier also einen String oder einen beliebigen anderen Datentyp direkt hart als zweiten Parameter übergeben. Außerdem kann man hier keinen vierten Parameter wie bei bindParam
benutzen.
$stmt = $pdo->prepare('SELECT * FROM foo WHERE bar = ? AND bla = ?');
$stmt->bindValue(1, $_GET['bar']);
$stmt->bindValue(2, $_GET['bla']);
$stmt->execute();