Einleitung top
Soll eine Anwendung von einer Datenbank auf eine andere portiert werden, so muss -normalerweise- der komplette Quellcode geändert werden. Hätte man PDO -von Anfang an- eingesetzt, wäre dies nicht nötig gewesen.
PDO ist standardmäßig installiert, jedoch kann es passieren, dass man es noch installieren muss, in diesem Fall kann diese Installationsanleitung hilfreich sein.
Nachteile top
- “Umgewöhnungs” Aufwand, wie bei jedem neuen System
- Spezielle Eigenheiten der SQL-Varianten stehen nur bei Benutzung dieser zur Verfügung
Vorteile top
- Wenig Aufwand bei nachträglichem Datenbank-Wechsel
- Bietet Funktionen zur einfachen Datenbank
- Vereinfacht den Abfragevorgang
- 100% Sicherheit(ok, 99,999%
) gegen Injections - Exception(-behandlung) bei Fehlern
PDO im Einstatz top
Verbindungsaufbau top
Ich werde hier nur auf MySQL eingehen, da diese Datenbank sehr weit verbreitet ist, insbesondere in der Webentwicklung. Soll jedoch eine Verbindung zu einer anderen Datenbank hergestellt werden kann Google hilfreich sein.
<?php
$database = new PDO('mysql:host=SERVER;dbname=DATENBANK', 'BENUTZER', 'PASSWORT');
$database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>
$database = new PDO('mysql:host=SERVER;dbname=DATENBANK', 'BENUTZER', 'PASSWORT');
$database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>
Und schon wurde eine Verbindung aufgebaut, Voraussetzung ist -natürlich- das die Werte stimmen. Die zweite Zeile bewirkt, dass Exceptions, im Fehlerfall, geworfen werden.
Erste Abfrage top
Nun wollen wir eine Seite eines Gästebuches aufbauen.
<?php
//Datenbankverbindung aufbauen
$sql =
'SELECT `text`, `time`, `author` //wir holen die Spalten test, time und author...
FROM `guestbook_entries` //... aus der Tabelle guestbook_entries...
ORDER BY `time` DESC'; //... in der absteigenden Reihenfolge
foreach($database->query($sql) as $entry) //die Abfrage verarbeiten und Ergebnisse in einer Schleife durchlaufen
{
//VORSICHT: die Daten nicht einfach so ausgeben, da es sonst zu XSS kommen WIRD. Guckt euch die Funktion htmlspecialchars an und passt den Aufruf an Eure Bedürfnisse an.
echo 'Kommentar von ', htmlspecialchars($entry['author']), '<br />';
echo 'geschrieben am ', date('H:i d.m.Y', $entry['time']), '<br />'; //den Unix-Timestamp in maschinenlesbare Form umwandeln
echo 'Text: <br />', htmlspecialchars($entry['text']);
}
?>
//Datenbankverbindung aufbauen
$sql =
'SELECT `text`, `time`, `author` //wir holen die Spalten test, time und author...
FROM `guestbook_entries` //... aus der Tabelle guestbook_entries...
ORDER BY `time` DESC'; //... in der absteigenden Reihenfolge
foreach($database->query($sql) as $entry) //die Abfrage verarbeiten und Ergebnisse in einer Schleife durchlaufen
{
//VORSICHT: die Daten nicht einfach so ausgeben, da es sonst zu XSS kommen WIRD. Guckt euch die Funktion htmlspecialchars an und passt den Aufruf an Eure Bedürfnisse an.
echo 'Kommentar von ', htmlspecialchars($entry['author']), '<br />';
echo 'geschrieben am ', date('H:i d.m.Y', $entry['time']), '<br />'; //den Unix-Timestamp in maschinenlesbare Form umwandeln
echo 'Text: <br />', htmlspecialchars($entry['text']);
}
?>
(Die Kommentare aus der Abfrage löschen, da sie dort eigentlich nichts zu suchen haben)
ggf. kann auch eine BBCode-Funktion über den Text drüber laufen gelassen werden, dazu, in einem späteren Artikel, noch mehr.
Abfrage mit Parametern top
Nun wollen wir aber nur die Einträge eines Autors ausgeben, den Autor bekommen wir über GET übergeben. Dieser Parameter kann von dem Besucher verändert werden und ist daher als unsicher zu betrachten. Sollten dieser, ohne PDO, direkt an die Datenbank übergeben, können Daten in der Datenbank verändert oder (sensible) Daten ausgelesen werden.
<?php
//Datenbankverbindung aufbauen
$sql =
'SELECT `text`, `time`, `author` //wir holen die Spalten test, time und author...
FROM `guestbook_entries` //... aus der Tabelle guestbook_entries...
WHERE `author`=:author //... sobald author gleich dem übergebenen Wert ist (:author ist ein Platzhalter)...
ORDER BY `time` DESC'; //... in der absteigenden Reihenfolge sortieren
//Abfrage vorbereiten
$stmn = $database->prepare($sql);
//Parameter an PDO übergeben
$stmn->bindValue(':author', $_GET['author'], PDO::PARAM_STR);
//Abfrage ausführen
$stmn->execute();
//Ergebnisse holen und verarbeiten
foreach($stmn->fetchAll() as $entry)
{
echo 'Kommentar von ', htmlspecialchars($entry['author']), '<br />';
echo 'geschrieben am ', date('H:i d.m.Y', $entry['time']), '<br />';
echo 'Text: <br />', htmlspecialchars($entry['text']);
}
//bereinige Abfrage
$stmn->closeCursor();
?>
//Datenbankverbindung aufbauen
$sql =
'SELECT `text`, `time`, `author` //wir holen die Spalten test, time und author...
FROM `guestbook_entries` //... aus der Tabelle guestbook_entries...
WHERE `author`=:author //... sobald author gleich dem übergebenen Wert ist (:author ist ein Platzhalter)...
ORDER BY `time` DESC'; //... in der absteigenden Reihenfolge sortieren
//Abfrage vorbereiten
$stmn = $database->prepare($sql);
//Parameter an PDO übergeben
$stmn->bindValue(':author', $_GET['author'], PDO::PARAM_STR);
//Abfrage ausführen
$stmn->execute();
//Ergebnisse holen und verarbeiten
foreach($stmn->fetchAll() as $entry)
{
echo 'Kommentar von ', htmlspecialchars($entry['author']), '<br />';
echo 'geschrieben am ', date('H:i d.m.Y', $entry['time']), '<br />';
echo 'Text: <br />', htmlspecialchars($entry['text']);
}
//bereinige Abfrage
$stmn->closeCursor();
?>
Als Erstes wir eine PDO-Statement($stmn) erzeugt. Dieser bekommt die Zuweisung des Platzhalter :author mit dem Wert der Variable $_GET['author']. Dann wird die Abfrage ausgeführt und – wie im ersten Beispiel – ausgewertet.
Eine Abfrage mehrmals ausführen top
Möchte man eine gleich lautende Anfrage mehrmals, mit unterschiedlichen Parametern, ausführen, bietet PDO auch dafür eine schnelle und elegante Lösung. Als Erstes erstellen wir wieder ein Statement, doch diesmal werden als Parameter Variablen und nicht nur ihre Werte gebunden. So lässt sich eine Anfrage – mit unterschiedlichen Werten – mehrmals absetzt.
<?php
//Datenbankverbindung aufbauen
$sql =
'UPDATE `guestbook_entries` //Ändere die Tabelle guestbook_entries ...
SET `time`=:time, `text`=:text //... ändert die Werte time und text...
WHERE `author`=:author' //... sobald author gleich dem übergebenen ist(:author ist ein Platzhalter), Änderung durchführen
//Abfrage vorbereiten
$stmn = $database->prepare($sql);
//Die zu verbindenden Variablen erstellen
$author = '';
$time = 0;
$text = '';
//Parameter an PDO übergeben, BEACHTE, dass hier die Variable und nicht ihr Wert verbunden wird
$stmn->bindParam(':author', $author , PDO::PARAM_STR);
$stmn->bindParam(':time', $time , PDO::PARAM_INT);
$stmn->bindParam(':text', $text , PDO::PARAM_STR);
//Abfragewerte...
$q = array(
array(//1. Eintrag
'author' => 'MrX', //der Name des Autors, alle Einträge dieses Autors werden verändert
'text' => 'Hallo...', //Der neue Text
'time' => 1234567890 //Die neue Zeit
),
array(//2. Eintrag
'author' => 'MrZ',
'text' => 'Hallo... ich bin Z',
'time' => 1275554677
),
)
//Hauptschleife, ein Durchgang => eine Abfrage
foreach($q as $query)
{
//Da die Variablen $author, $text und $time mit dem Statement gebunden wurden, können diese geändert werden und dabei ändert sich -automatisch- auch die Abfrage
$author = $query['author'];
$text = $query['text'];
$time = $query['time'];
//Abfrage ausführen
$stmn->execute();
}
//bereinige Abfrage
$stmn->closeCursor();
?>
//Datenbankverbindung aufbauen
$sql =
'UPDATE `guestbook_entries` //Ändere die Tabelle guestbook_entries ...
SET `time`=:time, `text`=:text //... ändert die Werte time und text...
WHERE `author`=:author' //... sobald author gleich dem übergebenen ist(:author ist ein Platzhalter), Änderung durchführen
//Abfrage vorbereiten
$stmn = $database->prepare($sql);
//Die zu verbindenden Variablen erstellen
$author = '';
$time = 0;
$text = '';
//Parameter an PDO übergeben, BEACHTE, dass hier die Variable und nicht ihr Wert verbunden wird
$stmn->bindParam(':author', $author , PDO::PARAM_STR);
$stmn->bindParam(':time', $time , PDO::PARAM_INT);
$stmn->bindParam(':text', $text , PDO::PARAM_STR);
//Abfragewerte...
$q = array(
array(//1. Eintrag
'author' => 'MrX', //der Name des Autors, alle Einträge dieses Autors werden verändert
'text' => 'Hallo...', //Der neue Text
'time' => 1234567890 //Die neue Zeit
),
array(//2. Eintrag
'author' => 'MrZ',
'text' => 'Hallo... ich bin Z',
'time' => 1275554677
),
)
//Hauptschleife, ein Durchgang => eine Abfrage
foreach($q as $query)
{
//Da die Variablen $author, $text und $time mit dem Statement gebunden wurden, können diese geändert werden und dabei ändert sich -automatisch- auch die Abfrage
$author = $query['author'];
$text = $query['text'];
$time = $query['time'];
//Abfrage ausführen
$stmn->execute();
}
//bereinige Abfrage
$stmn->closeCursor();
?>
Transaktionen top
Die meisten Datenbanken bieten die Möglichkeit, Abfragen zu einer Gruppe zu bündeln. Das hat unter anderem den Vorteile der Geschwindigkeit. Aber auch die Behandlung von Fehlern, so wird, sollte ein Fehler auftreten, die Änderungen der komplette Gruppe zurückgenommen.
<?php
//Datenbankverbindung aufbauen
//Transaktion starten
$database->beginTransaction();
try
{
//Änderungen vornehmen
$database->exec('UPDATE `account`
SET `money`=`money`-512
WHERE `user_name`=' . $database->quote($_GET['user'], PDO::PARAM_STR));
//Gültigkeit überprüfen
$ret = $database->query('SELECT `money`
FROM `account`
WHERE `user_name`=' . $database->quote($_GET['user'], PDO::PARAM_STR))
//Wenn Geldstand im Minusbereich, rufe die Änderungen zurück
if( $ret[0]['money'] < 0 )
$database->rollBack();
}
catch(Exception e)
{
//Änderungen zurücknehmen, sollte ein Fehler/eine Exception auftreten
$database->rollBack();
}
//Änderungen zulassen
$database->commit();
?>
//Datenbankverbindung aufbauen
//Transaktion starten
$database->beginTransaction();
try
{
//Änderungen vornehmen
$database->exec('UPDATE `account`
SET `money`=`money`-512
WHERE `user_name`=' . $database->quote($_GET['user'], PDO::PARAM_STR));
//Gültigkeit überprüfen
$ret = $database->query('SELECT `money`
FROM `account`
WHERE `user_name`=' . $database->quote($_GET['user'], PDO::PARAM_STR))
//Wenn Geldstand im Minusbereich, rufe die Änderungen zurück
if( $ret[0]['money'] < 0 )
$database->rollBack();
}
catch(Exception e)
{
//Änderungen zurücknehmen, sollte ein Fehler/eine Exception auftreten
$database->rollBack();
}
//Änderungen zulassen
$database->commit();
?>