January 4, 2013

PHP, PDO und Prepared Statements Schritt für Schritt verstehen

Was sind PDOs?

PDO steht für PHP Data Objects, eingeführt mit PHP 5.1. Zusammengefasst ist PDO eine abstrahierte Datenbankschnittstelle, die mittels verschiedener Treiber (MySQL, PostgreSQL, Oracle,…) auf verschiedene Datenbanken zugreifen kann.

Was sind Prepared Statements?

Prepared Statements sind vorkompilierbare Query-Templates, die ressourcensparend mit verschiedenen Parametern wiederholt ausgeführt werden können. Im Vergleich zu ‘normalen’, immer wieder komplett ausgeführten Queries bieten Prepared Statements einen Performancevorteil.

PDO Vorteile

Ist PDO langsamer als mysqli?

Oft wird behauptet, dass PDO langsamer als mysqli ist. Benchmarks mit Prepared Statements zeigen, dass der Geschwindigkeitsvorteil von mysqli max. 6 % beträgt. Bei anderen Benchmarks haben PDO dagegen komplett die Nase vorn. Und bei non-Prepared-Statements Benchmarks kommen PDO auf die gleiche Geschwindigkeit wie normale MySQL-Queries, mysqli weit abgeschlagen.

Übersichtlicher

Selbst wenn mysqli leicht schneller sein sollte, dafür ist die PDO Syntax wesentlich übersichtlicher. Ein Fragezeichen (mysqli) verliert einfach im Vergleich zu frei wählbaren Parameternamen (PDO). Ausserdem kann man die Named-Placeholders einzeln belegen, im Gegensatz zu mysqli. _bindparam(“sss”, $string, $string2, $string3) comes to mind 🙂

Prepared Statements mit PDO: Aufbau

Verbindung

Wer sich schon mit PHP und mysqli auseinandergesetzt hat, muss für PDOs nur wenig Neues lernen.

Als erstes muss eine Verbindung hergestellt werden. Dies geschieht per

$handler = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpw);

Verbindung mit Exception

Das ganze kann man auch noch in einen Try-Catch Block schreiben, um die PDO-eigenen Exceptions abzufangen:

try {
  $handler = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpw);
} catch(PDOException $e) {
  echo $e->getMessage();
}

Query vorbereiten: prepare()

Die PDO-Schnittstelle stellt genauso wie mysqli eine prepare()-Methode zur Verfügung, mit dem Queries von der Datenbank vorbereitet und optimiert werden. Im Gegensatz zu mysqli nutzt man hier aber im WHERE-Clause keine “?” sondern Named Parameters, die durch einen vorangestellten Doppelpunkt gekennzeichnet werden (hier :name).

$stmt = $handler->prepare("Normales MySQL, z.B. SELECT \* FROM table WHERE name = :name");

Named Parameter mit Werten ‘binden’

Ebenso wie mysqli gibt es bei PHP Data Objects eine Parameter bindende Methode, bindParam(). Über diese kann man die Queryparameter an PHP-Variablen binden, sodass bei Queryausführung dann die Parameter die Variablenwerte erhalten.

$stmt->bindParam(':name', $name);
// beliebiger Code
// \[...\]
$name = "Foobar";

Jetzt ist die Variable $name an den Parameter :name gebunden, und :name wird im Query mit dem ersetzt, das $name zur Ausführungszeit enthält. Im Beispiel-Code wäre dies jetzt “Foobar”.

Named Parameter als assoziatives Array

Alternativ kann man die Parameter auch als Array der execute()-Methode am Ende übergeben:

$params = array('name' => 'Foobar');

PDO Query ausführen: execute()

Nachdem wir ein Query prepared und die Parameter gebunden haben, können wir es ausführen:

$stmt->execute();
// oder falls Parameter im Array gebunden wurden:
$stmt->execute($params);

// Was hier passiert:
// SELECT * FROM table WHERE name = 'Foobar';

Ergebnis-Datensätze holen: fetch(), fetchObject() und fetchAll()

Um die Ergebnisse zu liefern geben dir PDO die Methoden fetch(), fetchObject() und fetchAll() an die Hand.

while ($row = $stmt->fetch()) {
  $allResults[] = $row;
}
// oder
$allResults = $stmt->fetchAll();
// oder
$obj = $stmt->fetchObject("MyClass");

Fetch-Modi

Dabei kann man fetch() und fetchAll() noch über optionale Parameter modifizieren:

  • PDO::FETCH_BOTH : holt ein assoziatives und ein numerisches Array (ist default)
  • PDO::FETCH_ASSOC : holt nur ein assoziatives Array, Indizes sind Spaltennamen
  • PDO::FETCH_OBJ : holt ein Objekt, Properties sind gleich Spaltennamen
$result = $stmt->fetch(PDO::FETCH\_OBJ);
$result->getColumnName();
  • PDO::FETCH_INTO : zieht das Result in eine Objektinstanz der Klasse MyClass
$instance = $stmt->fetch(PDO::FETCH\_INTO, new MyClass);

fetch-Modus im Vorraus festlegen: setFetchMode()

Etwas sauberer kann man den fetch-Modus auch mit der Methode setFetchMode() festlegen:

$stmt->setFetchMode(PDO::MeinFetchModus, Parameter);
$row = $stmt->fetch(); // etc

PDO Verbindung schliessen

Die PDO Verbindung kann man ganz einfach schliessen, indem man sie mit NULL gleichsetzt.

$handler = null;

PDO Fazit

Prepared Statements sind mit PHP & PDO wesentlich übersichtlicher, mächtiger und flexibler als mit mysqli. Die verschiedenen Benchmarkergebnisse, bei dem nur eines knapp für mysqli sprach, sollten nicht vor PDO abschrecken. Vielmehr sprechen die schon genannten Features so sehr für PDO, dass man mysqli getrost vergessen kann. Selbst wenn es in manchen Fällen um wenige Prozentpunkte schneller als PDO sein sollte.

Achtung:

Allerdings sollte man jetzt nicht immer Prepared Statements nutzen. Ein ‘normales’ $pdo->query(“MySQL Query) ist ca. drei mal so schnell wie ein Prepared Statement mit PDO. Prepared Statements sollten nur dann verwendet werden, wenn ein und dasselbe Query immer und immer wieder mit verschiedenen Parametern ausgeführt wird.

Ist noch etwas unklar? Fehlt dir etwas in diesem Artikel? Hilf mir, ihn zu verbessern, und hinterlasse unten bitte ein Kommentar oder bewerte ihn!

Wenn Dir dieser Artikel gefallen hat, dann abonniere doch einfach den kostenlosen Newsletter und bekomme alle Artikel bequem per RSS oder Email geliefert!