PHP rocks! wünscht allen Mitgliedern einen guten Rutsch ins neue Jahr 2017 !!!
Hinweis: Das Forum zieht um! Um keine Datenverluste zu haben, schalten wir zwecks Übernahme der Daten das Forum am Sonntag, den 24.04.2016 um ca. 21:00 Uhr offline und passen anschliessend die DNS-Einträge an.
www.php-rocks.de wird euch dann nach den Aktualisierungen der DNS-Server wieder wie gewohnt uneingeschränkt zur Verfügung stehen.
Danke für euer Verständnis!

Themabewertung:
  • 0 Bewertung(en) - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
PHP & Datenbanken Ein kurzer Einstieg in PDO und Prepared Statements
#1
Was ist PDO?
PDO steht für PHP Data Objects und stellt eine Schnittstelle bereit, um mit PHP auf Datenbanken zugreifen zu können.
Verwendet wird dafür ein spezifischer Datenbanktreiber, über den die Verbindung zum Datenbankserver hergestellt wird.
Durch die PDO-Abstraktionsschicht kann dann, ganz gleich welche Datenbank verwendet wird, mit ein und denselben Methoden Abfrage erstellen, Daten lesen/schreiben etc.
PDO bietet die Möglichkeit, Prepared Statements zu verwenden, um die Gefahr von SQL-Injection zu verringern.

Wie sieht das in der Praxis aus?
PDO bietet zwei wichtige Objekte. Als erstes natürlich das PDO-Objekt selbst, das grob gesagt die Verbindung zur Datenbank repräsentiert.
Und als zweites das PDOStatement-Objekt, das die Verwendung von Prepared-Statements erlaubt.

Zunächst benötigen wir eine Instanz der Klasse PDO, die wir mit einem einfachen Aufruf des Konstruktors erhalten:
PHP-Code:
$pdoObject = new PDO$sDsn$sUsername$sPassword$aOptions ); 
Im Prinzip ist das schon alles, was wir benötigen, um eine Verbindung mit einem Datenbank-Server aufnehmen zu können. Was aber sollen die Parameter nun bedeuten?

Schauen wir uns die Parameter zum Verständnis mal genauer an:
  • $sDsn
    Der sogenannte Data Name Source enthält eine Zeichenkette mit allen notwendigen Parametern, die wir zum Verbindungsaufbau benötigen, wie z.B. den PDO-Treiber, den Datenbank-Server, u.a.
  • $sUsername
    Der Benutzername für die Anmeldung am Datenbank-Server
  • $sPassword
    Passwort für die Anmeldung am Datenbank-Server
  • $aOptions
    Ein Array mit optionalen Attributen ( wird später noch drauf eingegangen )
Da $sUserame und $sPassword selbsterklärend sein sollten, spare ich mir die weitere Ausführung dazu und gehe nur auf $sDsn und $aOptions ein.

Data Name Source ( DSN ) - $sDsn
In der Data Name Source geben wir die notwendigen Parameter für den Verbindungsaufbau an. Als Beispiel soll folgende gültige DSN genügen:
PHP-Code:
$sDsn 'mysql:host=localhost;dbname=testdb;charset=utf8'
Die Angaben des DSN kurz erklärt:
  • mysql:
    Der PDO-Datenbanktreiber. In unserem Beispiel greifen wir auf eine MySQL-Datenbank zu.
    PDO bietet weitere Treiber für verschiedenste Datenbanken an. Weitere Informationen findet ihr hier: PDO-Treiber
  • host=
    Die Angabe des Datenbankservers, in unserem Beispiel ist dies der localhost.
  • dbname=
    Der Name der Datenbank, auf die wir zugreifen möchten, in unserem Fall testdb.
  • charset=
    Das CharsetEncoding für die Verbindung, in unserem Beispiel utf8 ( Unicode )
Weitere mögliche Parameter sind hier zu finden: PDO-MySQL DSN Elemente

Verbindungsoptionen - $aOptions
Über die Verbindungsoptionen können wir der Verbindung Parameter mitgeben, die das Verhalten beeinflussen.
Einige Verbindungsoptionen findet ihr u.a. hier: PDO - ATTR_*- Konstanten

Für unser Beispiel nutzen wir die folgenden drei Parameter:
PHP-Code:
$aOptions = array(
 
           PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
 
           PDO::ATTR_EMULATE_PREPARES => false,
 
           PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    
); 
Was bedeuten diese Angaben nun wieder?
  • PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ
    Gibt an, daß wir die Rückgabe eines Datensatzes von Mehoden, wie bspw. PDOStatement::fetch() als Objekt erhalten möchten. Per Standard werden diese als assoziatives Array zurückgegeben. Da wir aber OOP versiert sind, möchten wir natürlich unsere Datensätze auch als Objekte verarbeiten.
  • PDO::ATTR_EMULATE_PREPARES => false
    Der PDO-Treiber für MySQL emuliert per Standard die PreparedStatements nur, sendet dies aber nicht zur Datanbank. Mit dieser Einstellung sagen wir, daß wir nicht nur emulieren wollen, sondern PreparedStatements nutzen wollen.
  • PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    PDO liefert per Standard keine Fehler-Ausgaben ( PDO::ERRMODE_SILENT ). Wir veranlassen PDO mit dieser Option, Exceptions zu werfen, die wir dann abfangen und darauf reagieren können.
Das soll an dieser Stelle alles in Bezug auf die Verbindung sein.
Ich möchte im nächsten Step auf PreparedStatement mit PDOStatement eingehen.

PreparedStatements
Prepared Statements enthalten im Gegensatz zu einer Standard-Anweisung eine vorbereitete, die anstelle der Parameterwerte Platzhalter enthält. Mit diesem Prinzip ist es möglich, daß wir SQL-Injection effektiv verhindern.
PDO bietet wie bereits erwähnt das Objekt PDOStatement, welches PreparedStatements unterstützt. Ein Objekt der Klasse PDOStatement muß nicht explizit instanziiert werden, dies geschieht i.d.R. beim Aufruf von PDO::prepare(), das eine Query für die Ausführung vorbereitet und ein Objekt vom Typ PDOStatement zurückgibt.

Die Verwendung ist genau so einfach, wie bisher:
PHP-Code:
$pdoStmnt $pdoObject->prepare"SELECT email FROM mytable" ); 
Jetzt haben wir mit $pdoStmnt ein Objekt vom Typ PDOStatement. Aber wo liegt darin jetzt der Vorteil? Zumal die Abfrage bisher noch nicht einmal an die Datenbank gesendet wurde.
Nun, ein Vorteil liegt darin, daß wir an das Statement Parameter binden können, für die wir zuvor einen Marker oder Platzhalter gesetzt haben. Marker und Platzhalter können entweder als Fragezeichen ( ? ) oder als benannter Platzhalter ( :platzhalter ) gesetzt werden. Das Binden der Parameter-Werte ist über die Methoden PDOStatement::bindParam() und PDOStatement::bindValue() möglich.

Beispiel:
PHP-Code:
$sUserLastName 'Mustermann';

// mit Fragezeichen:
$pdoStmnt $pdoObject->prepare"SELECT email FROM mytable WHERE lastname=?" );
$pdoStmnt->bindParam1$sUserLastNamePDO::PARAM_STR );

// mit benannten Platzhaltern:
$pdoStmnt $pdoObject->prepare"SELECT email FROM mytable WHERE lastname=:userlastname" );
$pdoStmnt->bindParam':userlastname'$sUserLastNamePDO::PARAM_STR ); 

Hinweis
Bei Verwendung von Fragezeichen als Platzhalter gibt der erste Parameter von PDOStatement::bindParam() und PDOStatement::bindValue() den Offset, also die Position des zu ersetzenden Parameters in der Query an. Dabei beginnt die Nummerierung bei 1 und nicht, wie bspw. von Arrays gewohnt bei 0. Mit Verwendung der benannten Parameter sind wir unabhängig von der Offset-Angabe und zudem in der Lage in größeren Queries vorkommende Platzhalter-Duplikate mit nur einer Zuweisung über PDOStatement::bindParam() oder PDOStatement::bindValue() zu ersetzen.
PHP-Code:
$pdoStmnt $pdoObject->prepare"SELECT email FROM mytable WHERE firstname=:username OR lastname = :username" );
$pdoStmnt->bindParam':username'$sUsernamePDO::PARAM_STR ); 

Wir können als dritten Parameter der Methode PDO::bindParam() und PDO::bindValue() den Datentyp des gebundenen Wertes angeben. Die gebräuchlichsten Konstanten sind in diesem Fall PARAM_INT und PARAM_STR, welches gleichzeitig der Default ist, sofern wir nichts angeben. Weitere Konstanten und Beschreibungen findet ihr hier: PDO - Vordefinierte Konstanten

Jetzt haben wir zwar etwas über das Objekt PDOStatement und dessen Vorteile gelernt, aber wie bekomme ich nun das Ergebnis meiner Query zur weiteren Verarbietung?
Nun, dazu müssen wir die Query zunächst mal an die DB senden. Das machen wir mit der Methode PDOStatement::execute():
PHP-Code:
$pdoStmnt->execute(); 
Das Objekt $pdoStmnt enthält nun nach erfolgreicher Ausführung das Ergebnis der Abfrage. Aber wie kommen wir da jetzt ran?
Hierfür stehen uns die Methoden PDOStatement::fetch() und PDOStatement::fetchAll() zur Verfügung.

Hinweis
PDOStatement::fetch() und PDOStatement::fetchAll() geben immer ein assoziatives Array zurück, es sei denn wir geben der gewünschten Datentypen als dritten Parameter an oder ändern diesen explizit über die Optionen des Verbindungsaufbaus bzw. mit der Methode PDO:: setAttribute() unmittelbar nach dem Verbindungsaufbau. Das Attribute heißt ATTR_DEFAULT_FETCH_MODE und die akzeptierten Werte können hier nachgelesen werden: PDOStatement::fetch(). Da wir durchgehend mit Objekten arbeiten wollen, haben wir uns für den Wert PDO::FETCH_OBJ entschieden.

Erwartet man mehrere Datensätze und möchte diese gesammelt zurück bekommen, hilft die Methode PDOStatement::fetchAll(), die im Grunde genau wie PDOStatement::fetch() arbeitet, mit dem Unterschied, daß alle vorhandenen Datensätze zurück gegeben werden.

Hinweis
In der gängigen Praxis kommt es eher seltener vor, daß alle Datensätze zunächst gefetcht und dann verarbeitet werden. In den meisten Fällen wird über einen while-Kontext jede Datenzeile des Ergebnisses direkt verarbeitet:
PHP-Code:
while ( $dsRow $pdoStmnt->fetch() ) {

 
   echo $dsRow->email '<br />';



Das komplette Code-Beispiel
PHP-Code:
// vorbereitende Variablen für den Verbindungsaufbau
$sDsn 'mysql:host=localhost;dbname=testdb;charset=utf8';
$sUsername 'testuser';
$sPassword 'testpassword';

// zusätzliche Optionen
$aOptions = array(
 
           PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
 
           PDO::ATTR_EMULATE_PREPARES => false,
 
           PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    
);

// Suchkriterium für unsere Abfrage
$sUserLastName 'Mustermann';

// Instanziierung
$pdoObject = new PDO$sDsn$sUsername$sPassword$aOptions );

// Prepared Statement instanziieren
$pdoStmnt $pdoObject->prepare"SELECT email FROM mytable WHERE lastname=:userlastname" );
$pdoStmnt->bindParam':userlastname'$sUserLastNamePDO::PARAM_STR );

// Abfrage ausführen
$pdoStmnt->execute();

// Rückgabe verarbeiten
while ( $dsRow $pdoStmnt->fetch() ) {

 
   echo $dsRow->email '<br />';




Gehe zu: