von Philip Hetjens
PHP Data Objects (PDO) bietet im Vergleich zu anderen Datenbankschnittstellen den Vorteil, dass eine Anwendung nicht für jede Datenbank umgeschrieben werden muss. Wichtigsten Features, wie Objekt-orientiertes Interface und Prepared Statements werden Ebenfalls unterstützt.
Die PDO Erweiterung ist seit Version 5.1 Bestandteil von PHP. “PHP Data Objects” übertrifft alle anderen Datenbankschnittstellen in PHP. Es ist objektorientiert aufgebaut. Um ein neues DBMS hinzuzufügen, muss nur der Treiber in PHP einkompiliert werden. Das wird zwar bei Sharedhosting-Angeboten scheitern, doch normalerweise wird PDO schon mit Support für MySQL angeboten und das reicht oft bereits aus. Ein weitere Vorteil von PDO im Vergleich zu ext/MySQL ist, dass PDO prepared Statements unterstützt und damit viele PHP Applikationen leicht sicher zu erstellen sind. PDO unterstützt auch Transaktionen für beispielsweise Oracle oder auch MySQL 5.
Gute Verbindung
Um eine Verbindung zu einer Datenbank aufzubauen, muss eine Instanz eines PDO-Objekts erstellt werden. Der Konstruktor der PDO-Klasse erwartet drei String-Parameter:
$pdo = new PDO("mysql:host=localhost;dbname=test", $user, $pass);
Der erste Parameter beinhaltet den Connection-String, der eine Verbindung zum Datenbankserver beschreibt. Das erste Wort in diesem Parameter gibt an, welcher Datenbanktreiber verwendet werden soll. In unserem Fall ist es der MySQL-Treiber. Dieser Treiber erwartet zwei weitere Attribute: host und dbname. Der Wert von host definiert die Adresse des Datenbankservers. dbname enthält den Namen der Datenbank auf dem Server. Der Connection-String für eine Verbindung zu einem Postgre-Server besitzt dieselben Parameter, nur er beginnt statt mit mysql mit pgsql. Die anderen beiden Parameter geben den Benutzernamen und das Passwort für die Verbindung zum Datenbank-Server an. Wenn keine PDOException geworfen wurde, ist die Verbindung hergestellt und in dem Objekt $pdo gekapselt.
Einfache Queries
Beginnen wir etwas aus einer Datenbank auszulesen. Wir bemühen die Query-Methode des PDO-Objekts um zum Beispiel alle Zeilen der Tabelle users auszulesen:
foreach($pdo->query("SELECT * FROM users") as $row) {
var_dump($row);
}
Die Query-Methode ist vergleichbar mit einer Kombination aus den Funktionen mysql_query und mysql_fetch_array. Die Schleife wird so oft durchlaufen, wie Zeilen in der Tabelle. Innerhalb der Schleife sind pro Durchlauf die Daten einer Zeile verfügbar. Wenn nur Daten in die Datenbank eingetragen oder aktualisiert werden sollen, reicht der Aufruf der Query-Methode:
$pdo->query("INSERT INTO users (loginname,password)
VALUES ('test','123')");
$pdo->query("UPDATE users SET password = 'abc' WHERE loginname = 'test'");
Vorgefertigte Anfragen
Einer der Vorteile von PDO sind prepared Statements. Prepard Statements verringern die Anfälligkeit für SQL-Injections erheblich. Worum geht es? Stellen Sie sich eine Tabelle users vor. Diese Tabelle beinhaltet die Benutzernamen und die Passwörter aller Kunden. Ihre PHP-Anwendung benutzt folgendes SQL-Statement um ein Passwort auszulesen:
$sql = "SELECT * FROM users WHERE loginname = '".$_REQUEST['name'].'";
Diese Anfrage funktioniert ohne Probleme, wenn in $_REQUEST['name'] wirklich nur ein Benutzername steht. Stellen Sie sich weiterhin vor, in $_REQUEST['name'] stünde folgendes:
'; DELETE FROM users; --
dann erstellt Ihre Anwendung folgende SQL-Anfrage:
SELECT * FROM users WHERE loginname = ''; DELETE FROM users; --
Anschließend wären alle Kundendaten verschwunden. Die Datenbank wird zuerst das SELECT-Statement ausführen und kein Ergebnis zurückliefern, weil kein Benutzer ohne Name in der Tabelle steht. Anschließend würde die DELETE-Anweisung ausgeführt werden. Die beiden “–” sorgen dafür, dass alles weitere als Kommentar behandelt wird und folglich ignoriert wird.
Hier kommen nun prepared Statements ins Spiel. Mit PDO würde die Anfrage wie folgt zusammengebaut:
$stmt = $pdo->prepare("SELECT * FROM users WHERE loginname = :name;");
$stmt->bindValue(':name',$_REQUEST['name']);
$stmt->execute();
$row = $stmt->fetch();
Als erste Aktion ist die prepare-Methode des PDO-Objekts aufzurufen. Diese Methode erwartet einen Parameter in dem die SQL-Anfrage mit Platzhaltern hinterlegt ist. Als Platzhalter hat sich ein Doppelpunkt gefolgt von einem Namen etabliert. Es ist wichtig, dass diese Platzhalter nicht in Anführungsstriche gesetzt werden.
Die prepare-Methode gibt anschließend ein Objekt vom Typ Statement zurück. Dieses Objekt repräsentiert ab jetzt die Anfrage. Anschließend füllen wir den Platzhalter mit der Methode bindValue. Sie erwartet als ersten Parameter den Platzhalter und als zweiten Parameter den neuen Inhalt. Die bindValue-Methode sorgt dafür, dass der übergebene String maskiert wird und eine SQL-Injection verhindert wird.
Im nächsten Schritt ist das Statement auszuführen. Dies geschieht mit der Methode execute.
Die Methode fetch entspricht der mysql_fetch_array-Methode. Sie gibt die nächste Zeile zurück. In dem Fall oben wird nur eine Zeile zurückgegeben. Auch bei dieser Methode ist eine Kombination mit einer Schleife möglich:
foreach($stmt->fetch() as $row) {
var_dump($row);
}
Die Schliefe wird sofort durchlaufen, wie Zeilen in der Antwort der Datenbank.
Achtung Falle!
Ein Statement-Objekt besitzt zwar die Methode rowCount, aber bei den meisten DBMS, darunter auch MySQL, ergibt rowCount bei einer SELECT-Anfrage nicht das gewünschte Ergebnis. Um die Anzahl der Zeilen zu ermitteln, muss bei PDO ein SELECT count(*)-Query abgesetzt werden und das Ergebnis ausgelesen werden.
SELECT count(*) as count FROM users WHERE loginname = 'Testbenutzer';
Die Anzahl der Zeilen werden anschließend in der ersten Ergebniszeile in der Spalte count gefunden.
Fazit
PDO bietet viele Funktionen. Es besitzt im Vergleich zu allen anderen Datenbankschnittstellen den Vorteil, dass die Anwendung nicht für jede Datenbank umgeschrieben werden muss. PDO wird einfach um einen Treiber ergänzt, der Connection-String wird abgeändert und schon kommuniziert die Anwendung mit einem anderen DBMS. Das ist eine Flexibilität, die man bei PHP im Bereich der Datenbank meist mit Abstraktionslayern in der eigenen Anwendung kompensiert hat. Diese können aber nun auch abgeschafft werden. Da PDO in C geschrieben wurde, läuft es schneller als jede Eigenentwicklung in PHP. Es sollte deshalb den Vorzug erhalten.
Ich für meinen Teil werde die Funktion mysql_row_count vermissen. Aber als professioneller PHP-Entwickler sollte man sich selbst eingestehen, dass die SELECT count(*)-Anweisung trotz der Mehrzeilen der elegantere Weg ist.
Die Vorteile, wie prepared Statements und objektorientiertes Interface, überwiegen auf jeden Fall den Nachteilen. Aus diesem Grund sollte PDO bei jedem neuen Projekt für PHP 5.1 oder höher in Betracht gezogen werden. ™
Philip Hetjens ist ein Student im 7. Semester des Studiengangs Software Engineering an der Fontys Hogeschool Techniek en Bedrijfsmanagement Venlo (Niederlande).
Material zum Thema:
Erstveröffentlichung 27.10.2006




Grundsätzlich Zustimmung zum Fazit. Fehlermeldungen sind bei PDO leider nicht so klasse gelöst. Standardmäßig wird keine Fehlermeldung ausgegeben, was die Entwicklung manchmal doch etwas behindert. Ziehe allerdings auch ORM selbst geschriebenem SQL vor. :)