Click here to load reader

Webtechnologien Teil 8: PDO/MySQL - Wirtschaftsinformatikwi.f4.htw-berlin.de/.../Folien/WT-08/08-WT-PHP-III-PDO-1.pdf · Webtechnologien – WS 2017/18 - Teil 8/PHP und MySQL 3 Einführung

  • View
    212

  • Download
    0

Embed Size (px)

Text of Webtechnologien Teil 8: PDO/MySQL -...

  • 24.10.17 1Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Webtechnologien

    Teil 8: PDO/MySQL

  • 2Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    bersicht

    Arten der Kommunikation mit dem Datenbank-Server Grundstzliche Operationen Weitere Routinen Tricks und Tipps

    Siehe: http://dev.mysql.com/doc/refman/5.5/en/apis-php.htmlhttp://de3.php.net/manual/en/book.mysqli.php

  • 3Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Einfhrung I

    PHP hat jeweils eine eigene API zum Zugriff auf die Datenbanken in Form eines Treibers.

    Bei MySQL ist es die MySQLi-Schnittstelle. Da sich die Schnittstellen zu verschiedenen Datenbanken

    unterscheiden, wurde eine einheitliche PDO-Schnittstelle geschaffen.

  • 4Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    PHP-Schnittstellen zu Datenbanken (Auszug)

    Datenbank Datenbank Datenbank

    Adabas D Oracle MySQL

    dBase PostgreSQL MySQLi

    DB2 Sybase MariaDB

    Ingres Interbase

    FilePro Informix

    Und natrlich auch ODBC...Siehe dazu: http://de.wikipedia.org/wiki/Open_Database_Connectivity

  • 5Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Schnittstellen zur MySQL-Datenbank

    mysql-Schnittstelle (Veraltet, wird nicht mehr untersttzt)

    mysqli-Schnittstelle (improved, die aktuelle Version)

    PDO-Schnittstelle (Portable Data Object)Diese abstrahiert von den verschiedenen Schnittstellen zu den Datenbanken aber nur syntaktisch. Die speziellen Eigenschaften der SQL-Dialekte der jeweiligen Datenbanken bleiben jedoch erhalten!

    Diese Schnittstelle wird hier beschrieben.

    Es gibt aus historischen Grnden drei Schnittstellen zu MySQL:

  • 6Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Das PDO-Objekt

    Die Benutzung der Schnittstelle erfolgt nur Objekt-orientiert. Durch das Erzeugen des Objekts wird gleichzeitig eine

    Verbindung zur Datenbank aufgebaut, die beim Entfernen des Objekts geschlossen wird.

    Das allgemeine Schema des Zugriffs sieht daher so aus:

    Aufbau der Verbindung

    Zugriffe auf die Datenbank

    Abbau der Verbindung

  • 7Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Bemerkungen

    In diesem Foliensatz wird ein Teil eines Logins fr eine Website beschrieben.

    Aber(!): Hier wird nur der Aspekt des Zugriffs auf die Datenbank thematisiert, Aspekte der Sicherheit werden ignoriert.

    Dies NICHT zur Grundlage eines echten Logins nehmen.

    Grund: Passwrter drfen nie im Klartext abgespeichert werden, sondern immer nur deren Hashwerte (oder hnlich).

  • 8Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Verbindung zur Datenbank I

    DSN heit Data Source Name: die Definition des Zugriffs auf die Datenbank. Dies ist ein String mit dem Aufbau:

    Datenbanktyp: Keyword=Wert; Keyword=Wert; Datenbanktyp ist hier mysql Fr Keyword knnen hier eingesetzt werden:

    Host: Angabe der IP/DNS-Adresse der Datenbank dbname: Name der ausgewhlten Datenbank

    Nutzer und Passwort sind Strings Der letzte Parameter definiert Optionen, die fr die Kommunikation mit der

    Datenbank wichtig sind (ein PHP-Hash mit definierten Keys)

    object PDO(DSN [,Nutzer [,Passwort[,Optionen]]]);

    Siehe: http://www.php.net/manual/de/book.pdo.php http://de.wikipedia.org/wiki/Data_Source_Name

  • 9Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Verbindung zur Datenbank II - Beispiel

    Die Datenbank liegt auf der eigenen Maschine und heit "accounts": (1). Der User ist "nobody" mit dem Passwort "blabla426" (2) und (3). Mit den Optionen wird der Zeichensatz auf UTF-8 gesetzt: (4) bis (6). Es wird der Warn-Modus eingeschaltet: (9).

    (1) $DSN= 'mysql:host=localhost;dbname=accounts';(2) $DB_USER= 'nobody';(3) $DB_PW= 'blabla426';(4) $DB_options = array((5) PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'(6) ); (7) try {(8) $db= new PDO($DSN,$DB_USER,$DB_PW,$DB_options);(9) $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);(10)} catch(PDOException $err) {(11) echo 'DB ERROR: '.$err->getMessage().PHP_EOL;(12)}

  • 10Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Bemerkungen zu PHP/Optionen

    Derartige Konstanten haben nie ein $ zum Beginn. Sie werden per Konvention in Grobuchstaben geschrieben (wie

    in Java).

    PDO::MYSQL_ATTR_INIT_COMMAND

    PDO::MYSQL_ATTR_INIT_COMMAND ist in PHP eine static-Konstante.

    Klassenname Name der Konstante

  • 11Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Verbindung zur Datenbank III - Erluterungen

    Es sind noch andere Optionen fr MySQL mglich, z.B.: PDO::MYSQL_ATTR_LOCAL_INFILE (alternative Konfigurationsdatei)

    um z.B. zum Testen eine andere Konfiguration als bei normalen Betrieb zu benutzen.

    PDO::MYSQL_ATTR_COMPRESSDaten der Verbindung werden komprimiert

    Siehe dazu: http://de2.php.net/pdo_mysql

    (4) $DB_options = array((5) PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'(6) );

    MySQL-spezifische Konstante

  • 12Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Verbindung zur Datenbank IV - Erluterungen

    Im Fehlerfalle wirft das PDO-Objekt eine Exception, die mit der obigen Konstruktion gefangen werden kann.

    Die Methode getMessage() erzeugt einen String mit der Fehlermeldung. PHP_EOL ist die Konstante mit der Bedeutung End-of-Line. Die try-Catch-Konstruktion ist in PHP genauso wie in Java: Im catch-Teil

    wird ein Objekt deklariert, das weiter gehende Informationen enthlt, hier von der Klasse PDOException (Zeile 10).

    (7) try {(8) $db= new PDO($DSN,$DB_USER,$DB_PW,$DB_options);(10)} catch(PDOException $err) {(11) echo 'DB ERROR: '.$err->getMessage().PHP_EOL;(12)}

  • 13Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Verbindung zur Datenbank V - Erluterungen

    Nachdem das Objekt generiert wurde, knnen dem Objekt Eigenschaften zugewiesen werden, die aber nur das PDO-Objekt betreffen, nicht die Benutzung von MySQL.

    Es gibt drei Modi: PDO::ERRMODE_SILENT

    Es wird lediglich ein Errorcode geliefert, der explizit nach jedem PDO-Methodenaufruf abgefragt werden muss. Default!

    PDO::ERRMODE_WARNINGEs wird eine Warn-Meldung ausgegeben sowie der Errorcode geliefert.

    PDO::ERRMODE_EXCEPTIONEs wird eine Exception geworfen.

    Siehe dazu: http://www.php.net/manual/de/pdo.error-handling.php

    (7) try {(8) $db= new PDO($DSN,$DB_USER,$DB_PW,$DB_options);(9) $db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);(10)} catch(PDOException $err) {

  • 14Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Hinweise

    Ob mit Exceptions oder mit einem der anderen Modi gearbeitet wird, hngt von der Programmierkonvention im betreffenden Projekt ab.

    Die Benutzung von Exceptions fhrt zu einem umstndlichen Code; dafr entfallen die vielen Fehlerabfragen.

    Wer sowieso immer den Erfolg einer Routine in einer Fehlerabfrage abfragt, der sollte whrend der Entwicklung im Warn-Modus whrend der Produktion im Silent-Modus

    arbeiten.

  • 15Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter I

    Der MySQL-Server muss natrlich laufen. Auch muss die Datenbank mit dem User "nobody" und dem Passwort

    "blabla426" eingerichtet sein.

  • 16Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Einrichten einer Datenbank I

  • 17Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Einrichten einer Datenbank II

    Es hat geklappt!

    Festlegen derKodierung

  • 18Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Einrichten einer Datenbank III

  • 19Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Einrichten einer Datenbank III

  • 20Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Hinweis

    Das Gewhren aller Rechte fr einen Benutzer ist nur dann sinnvoll, wenn es wie hier um eine Spielanwendung geht.

    In der Realitt mssen die Rechte beschrnkt werden. Diese Beschrnkung lsst sich gut mit phpMyAdmin konfigurieren.

  • 21Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Einrichten einer Datenbank IV

    So sieht es schon einmal ganz gut aus...

  • 22Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter II Create Table

    Nun bauen wir eine Tabelle in SQL:

    (1) CREATE TABLE account ((2) id int(11) NOT NULL PRIMARY KEY,(3) name varchar(45) NOT NULL,(4) pw varchar(64) NOT NULL,(5) groupID int(11) NOT NULL(6) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    id ist der Schlsselwert in der Tabelle (2). Der Login-Name und das Passwort drfen nicht leer sein (3,4). Dann bereiten wir noch Gruppen vor, indem eine Gruppen-ID als

    Integer zugelassen wird (5). Da wir Transaktionen haben wollen, muss die InnoDB-Version von

    MySQL benutzt werden (6). In den letzten Versionen von MySQL ist dies der Defaultwert und braucht daher nicht angegeben zu werden.

    Und natrlich UTF-8 als Zeichensatz (6).

  • 23Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter III Create Table

    (1) $create_table = "CREATE TABLE account (";(2) $create_table.= "id int(11) NOT NULL PRIMARY KEY,";(3) $create_table.= "name varchar(45) NOT NULL,";(4) $create_table.= "pw varchar(64) NOT NULL,";(5) $create_table.= "groupID int(11) NOT NULL";(6) $create_table.= ") ENGINE=InnoDB DEFAULT CHARSET=utf8;";

    (7) $rtn= $db->exec($create_table);(8) if($rtn===false) {(9) $err= $db->errorInfo();(10) echo 'DB ERROR: #'.$err[1]." ".$err[2].PHP_EOL;(11)}

    Die Tabelle wird mit exec() angelegt (7). Der String fr $create_table wird stckweise mit der verkrzten

    Punkt-Notation zusammengesetzt (Zeilen 1 bis 6). In Zeile (8) steht die Fehlerabfrage mit ===.

  • 24Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Create Table I - Erluterungen

    (7) $rtn= $db->exec($create_table);(8) if($rtn===false) {

    exec("SQL-String") fhrt eine SQL-Operation aus, bei der keine Werte anschlieend abgefragt werden, d.h. exec() kann nicht fr Abfragen von Tabelleninhalten benutzt werden.

    In der Variablen $rtn wird der Return-Code abgespeichert, der im Fehlerfall den boole'schen Wert false hat, ansonsten die Anzahl der betroffenen Zeilen in der Tabelle enthlt.

    Daher muss mit === abgefragt werden(weil die Anzahl ja auch 0 sein kann, was im Falle von == fehlerhafter Weise als false interpretiert wird)

    Siehe: http://www.php.net/manual/de/pdo.exec.php

  • 25Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Create Table II - Erluterungen

    (8) if($rtn===false) {(9) $err= $db->errorInfo();(10) echo 'DB ERROR: #'.$err[1]." ".$err[2].PHP_EOL;(11)}

    Die Methode errorInfo() liefert ein Array zurck (9): Index[0] enthlt den SQL-Error-Code Index[1] enthlt den Error-Code des Treibers Index[2] enthlt die Fehlermeldung

    Siehe: http://www.php.net/manual/de/pdo.errorinfo.php Dann gibt es noch die Methode errorCode(), die lediglich den SQL-Error-

    Code liefert. Siehe:

    http://docstore.mik.ua/orelly/java-ent/jenut/ch08_06.htm http://www.php.net/manual/de/pdo.errorcode.php

  • 26Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter IV - Drop Table

    (1) $create_table = "CREATE TABLE account ("; ... (6) $create_table.= ") ENGINE=InnoDB DEFAULT CHARSET=utf8;";(7) $rtn= $db->exec($create_table);(8) if($rtn===false) {(9) $err= $db->errorInfo();(10) echo 'DB ERROR: #'.$err[1]." ".$err[2].PHP_EOL;(11)}(12)$delete_table= "DROP TABLE account;";(13)$rtn= $db->exec($delete_table);(14)if($rtn===false) {(15) $err= $db->errorInfo(); ...}

    Die Tabelle account wird hier gelscht (12,13). Das machen wir hier nur fr unser Beispiel, damit wir in mehreren

    Durchlufen etwas ausprobieren knnen. Fr ein Login-Modul ist das natrlich unsinnig.

  • 27Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter V Einfgen von Daten

    (1) $insert = "INSERT INTO account(id,name,pw,groupID)"(2) $insert.= "VALUES(0,'root','bitte!',0)";(3) $rtn= $db->exec($insert);(4) if($rtn===false) {(5) $err= $db->errorInfo();(6) echo 'DB ERROR: #'.$err[1]." ".$err[2].PHP_EOL;(7) }

    In die Tabelle account wird ein einziger Record (3) eingefgt. Bitte beachten Sie, dass in SQL andere String-Begrenzer als in PHP

    benutzt werden (2).

  • 28Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Wir glauben ja nichts sehen wir nach

    Das ist die Ausgabe der Tabelle in phpMyAdmin.

  • 29Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter VI Einfgen von Daten

    $val[]=array('id'=>0,'name'=>'root', 'pw'=>'bitte!','groupID'=>0);$val[]=array('id'=>1,'name'=>'user', 'pw'=>'danke!','groupID'=>0);$val[]=array('id'=>2,'name'=>'helmut','pw'=>'sdgafg','groupID'=>0);$val[]=array('id'=>3,'name'=>'evelyn','pw'=>'tztrff','groupID'=>0);

    (1) foreach($val as $v) {(2) $insert = "INSERT INTO account(id,name,pw,groupID) "; (3) $insert.= "VALUES(${v['id']},'${v['name']}','${v['pw']}'";(4) $insert.= ",${v['groupID']})"; (5) $rtn= $db->exec($insert);(6) if($rtn===false) {(7) $err= $db->errorInfo();(8) echo 'DB ERROR: #'.$err[1]." ".$err[2].PHP_EOL;(9) }(10)}

    Es wird nun eine Tabelle zum Fllen der Datenbank-Tabelle verwendet.

  • 30Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Einfgen von Daten - Erluterungen

    (1) foreach($val as $v) {(2) $insert = "INSERT INTO account(id,name,pw,groupID) "; (3) $insert.= "VALUES(${v['id']},'${v['name']}','${v['pw']}'";(4) $insert.= ",${v['groupID']})";

    Die Konstruktion ${v['id']} ist erforderlich, weil ein Ausdruck zur Auswertung innerhalb des Strings benutzt wird, daher die {}.

    Die einzelnen Elemente des Hashs sind ja benannt, so dass relativ schnell klar wird, was wann in das Insert-SQL-Statement eingefgt wird.

  • 31Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Wir glauben ja nichts sehen wir nach

    Das ist die Ausgabe der Tabelle in phpMyAdmin.

  • 32Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter VII Auslesen von Daten

    (1) $db= new PDO($DSN,$DB_USER,$DB_PW,$DB_options);(2) $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);(3) $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC); ...(4) $query= "SELECT * FROM account";(5) $data= $db->query($query);(6) foreach($data as $row) {(7) print_r($row);(8) }

    Es wird das Attribut PDO::ATTR_DEFAULT_FETCH_MODE beim Erzeugen des PDO-Objekts gesetzt, das die Art des Objekts beim Auslesen von Informationen mit der Methode query() bestimmt.Mglich sind: PDO::FETCH_ASSOC: Es wird ein Hash mit den Namen der Spalten

    geliefert (das ist zu empfehlen). PDO::FETCH_BOTH: Ein Hash mit Array-Indices wird geliefert. PDO::FETCH_OBJ: Ein Objekt wird generiert.

  • 33Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Unser Beispiel weiter VIII Auslesen von Daten

    Array( [id] => 0 [name] => root [pw] => bitte! [groupID] => 0)Array( [id] => 1 [name] => user [pw] => danke! [groupID] => 0)

    Array( [id] => 2 [name] => helmut [pw] => sdgafg [groupID] => 0)Array( [id] => 3 [name] => evelyn [pw] => tztrff [groupID] => 0)

    Das ist die Ausgabe des letzten Beispiels durch print_r (Zeile 7).

    Mit jedem Schleifendurchlauf wird eine Zeile aus der Tabelle gelesenund als Hash geliefert.

  • 34Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Auslesen von Daten nach query()

    (5) $data= $db->query($query);

    Ein query() liefert den Inhalt einer (temporren) Tabelle als ein Objekt vom Typ PDOStatement.

    Das foreach des Beispiels Zeile (6) durchluft das Objekt $data, das genau eine Zeile der Tabelle enthlt.

    Mit fetch() wird jeweils die nchste Zeile des letzten Querys gelesen. fetchall() liest die ganze (temporre) Tabelle in ein Array ein. fetchall() ist

    natrlich nur bei kleinen Tabellen sinnvoll.

    $row= $data->fetch();

    $table= $data->fetchall();

    alternativ

  • 35Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Auslesen der Meta-Information I

    (1) $query= "SELECT * FROM account";(2) $data= $db->query($query);(3) echo $data->columnCount().PHP_EOL;(4) for($i= 0;$icolumnCount();$i++) {(5) var_export($data->getColumnMeta($i));(6) }

    Meta-Informationen sind solche ber etwas. Mit columnCount() wird die Anzahl der

    Spalten der temporren Tabelle geliefert, mit getColumnMeta() eine Beschreibung der

    Spalte. Rechts steht der Beginn der Ausgabe. Es ist

    der Aufbau der Meta-Information (Deskriptor) erkennbar.

    4array ( 'native_type' => 'LONG', 'pdo_type' => 2, 'flags' => array ( 0 => 'not_null', 1 => 'primary_key', ), 'table' => 'account', 'name' => 'id', 'len' => 11, 'precision' => 0,)

  • 36Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Auslesen der Meta-Information II Ganze Tabelle

    (1) $data= $db->query($query);(2) $limit= $data->columnCount();(3) for($i= 0;$igetColumnMeta($i);(5) echo $meta['name']." ";(6) }(7) echo PHP_EOL;(8) foreach($data as $row) {(9) foreach($row as $val) {(10) echo $val." ";(11) }(12) echo PHP_EOL;(13)}

    id name pw groupID 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0

    Es wird eine berschrift fr eine Tabelle aus der Datenbank generiert.

    Dann werden in einer doppelten for-Schleife die Zeilen ausgegeben: jeweils eine Zeile mit mehreren Eintrgen.

  • 37Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Auslesen der Meta-Information III Ganze Tabelle

    (1) $data= $db->query($query);(2) $limit= $data->columnCount();(3) for($i= 0;$igetColumnMeta($i);(5) printf('%10s ',$meta['name']);(6) }(7) echo PHP_EOL;(8) foreach($data as $row) {(9) foreach($row as $val) {(10) printf('%10s ',$val);(11) }(12) echo PHP_EOL;(13)}

    Damit es etwas schner aussieht, wird zur Ausgabe die Routine printf() verwendet, die einen Format-String zur Definition des Layouts erlaubt.

    Die einzelnen Optionen des ersten Parameters von printf() sind http://de3.php.net/manual/de/function.printf.php zu entnehmen.

    id name pw groupID 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0

  • 38Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Nun endlich zum Login I

    (1) $LoginName= 'root'; // Simulation des Formulars(2) $LoginPW= 'bitte!';(3) $query= "SELECT * FROM account WHERE name='$LoginName'";(4) $data= $db->query($query);(5) $rows= $data->fetchAll();(6) echo count($rows).PHP_EOL;(7) var_export($rows);(8) if((count($rows)==1)&&($LoginPW==$rows[0]['pw'])) {(9) echo "loggt in".PHP_EOL;(10)} else {(11) echo "NOT loggt in".PHP_EOL;(12)}

    $LoginName und $LoginPW kommen aus dem Formular, was hier durch die Zuweisungen simuliert wird.

    Beachten Sie die Abfrage in (8): es muss ein einziger Eintrag da sein (count()) und das PW muss stimmen.

    1array ( 0 => array ( 'id' => '0', 'name' => 'root', 'pw' => 'bitte!', 'groupID' => '0', ),)loggt in

  • 39Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Nun endlich zum Login II

    (1) $LoginName= 'root';(2) $LoginPW= 'bitte!';(3) $query= "SELECT * FROM account WHERE name='$LoginName'";(4) $data= $db->query($query);(5) $rows= $data->fetch();(6) echo gettype($rows).PHP_EOL;(7) var_export($rows);(8) if(($rows!==false)&&($LoginPW==$rows['pw'])) {(9) echo "loggt in".PHP_EOL;(10)} else {(11) echo "NOT loggt in".PHP_EOL;(12)}

    Das ganze geht auch mit einem fetch(), nur dass der Datentyp des Ergebnisses anders ist. Das zeigt sich in der Abfrage.

    var_export($rows) gibt die Struktur des Ergebnisses aus.

    arrayarray ( 'id' => '0', 'name' => 'root', 'pw' => 'bitte!', 'groupID' => '0',)loggt in

  • 40Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Alternative zum Fllen der Tabelle I

    (1) $insert = "INSERT INTO account(id,name,pw,groupID)";(2) $insert.= "VALUES(?,?,?,?)";(3) $stmt= $db->prepare($insert);(4) $id= 0; $name= 'root'; $pw= 'bitte!'; $groupID= 0;(5) $stmt->bindValue(1,$id, PDO::PARAM_INT);(6) $stmt->bindValue(2,$name, PDO::PARAM_STR);(7) $stmt->bindValue(3,$pw, PDO::PARAM_STR);(8) $stmt->bindValue(4,$groupID,PDO::PARAM_INT);(9) $stmt->execute();

    Bei den gebundenen Parametern wird eine Schablone (1-2) erstellt, die dann dem Datenbank-Server mitgeteilt wird (3).

    In der Schablone wird an die Parameter-Positionen ein ? gesetzt. In der Reihenfolge der ? werden die Parameter per bindValue(), als Wertekopie,

    den ? mit einer Typangabe (z.B. PDO::PARAM_INT) zugeordnet. Beim execute() werden diese Werte zum Datenbank-Server geschickt, das SQL-

    Statement dort ausgefllt und dann ausgefhrt.

    id name pw groupID 0 root bitte! 0

  • 41Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Alternative zum Fllen der Tabelle II

    Das ist das allgemeine Schema bei der Benutzung von gebundenen Parametern.

    Neben dem schon eingefhrten bindValue() gibt es noch das bindParam(), das die Bindung ber eine Referenz realisiert.

    Neben den beiden Typen fr String und Integer gibt es noch den Typ Boolean. Siehe dazu: http://www.php.net/manual/en/pdostatement.bindparam.php

    Schablone erzeugen

    Parameter zuordnen

    Ausfhren

    bindValue()bindParam()

    Integer: PDO::PARAM_INTString: PDO::PARAM_STRBool: PDO::PARAM_BOOL

  • 42Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Alternative zum Fllen der Tabelle III

    (1) $insert = "INSERT INTO account(id,name,pw,groupID)";(2) $insert.= "VALUES(?,?,?,?)";(3) $stmt= $db->prepare($insert);(4) $stmt->bindParam(1,$id, PDO::PARAM_INT);(5) $stmt->bindParam(2,$name, PDO::PARAM_STR);(6) $stmt->bindParam(3,$pw, PDO::PARAM_STR);(7) $stmt->bindParam(4,$groupID,PDO::PARAM_INT);

    (8) foreach($val as $row) {(9) $id= $row['id'];(10) $name= $row['name'];(11) $pw= $row['pw'];(12) $groupID= $row['groupID'];(13) $stmt->execute();(14)}

    id name pw groupID 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0 Das Hash $val ist wie oben gesetzt.

  • 43Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Alternative zum Fllen der Tabelle IV - Bemerkungen

    (3) $stmt= $db->prepare($insert);(4) $stmt->bindParam(1,$id, PDO::PARAM_INT); ...(8) foreach($val as $row) {(9) $id= $row['id'];(10) $name= $row['name'];(11) $pw= $row['pw'];(12) $groupID= $row['groupID'];(13) $stmt->execute();(14)}

    In der Schleife wird immer wieder auf die gebundenen Variablen zugegriffen. Bei bindValue() wird der Wert zum Zeitpunkt des Bindens fest dem ? zugeordnet. Bei bindParam() wird der aktuelle Wert zum Zeitpunkt des execute() benutzt.

    $val[]=array('id'=>0,'name'=>'root', 'pw'=>'bitte!','groupID'=>0);$val[]=array('id'=>1,'name'=>'user', 'pw'=>'danke!','groupID'=>0);

  • 44Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Alternative zum Fllen der Tabelle V

    Das Hash $val ist wie oben gesetzt.

    (1) $insert = "INSERT INTO account(id,name,pw,groupID)";(2) $insert.= "VALUES(:id,:name,:pw,:groupID)";(3) $stmt= $db->prepare($insert);(4) $stmt->bindParam(':id', $id, PDO::PARAM_INT);(5) $stmt->bindParam(':name', $name, PDO::PARAM_STR);(6) $stmt->bindParam(':pw', $pw, PDO::PARAM_STR);(7) $stmt->bindParam(':groupID',$groupID,PDO::PARAM_INT);

    (8) foreach($val as $row) {(9) foreach($row as $key=>$val) {(10) $$key= $val;(11) }(12) $stmt->execute();(13)}

    Mit einem Doppelpunkt eingeleitet lassen sich die ? wie Variablen adressieren.

  • 45Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Alternative zum Fllen der Tabelle VI - Erluterung

    (8) foreach($val as $row) {(9) foreach($row as $key=>$val) {(10) $$key= $val;(11) }(12) $stmt->execute();(13)}

    $val[]=array('id'=>0,'name'=>'root', 'pw'=>'bitte!','groupID'=>0);$val[]=array('id'=>1,'name'=>'user', 'pw'=>'danke!','groupID'=>0);

    Hier wird der Name der Variablenanhand des Wertes einer anderenVariablen bestimmt!

    Auszug vom Setzen des Hash $val:

    So etwas geht nur inSkriptsprachen.

  • 46Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    SQL Injection I - Vorbereitung

    (1) $LoginName= 'root'; $LoginPW= 'bitte!';(2) $update= "UPDATE account SET pw = '*balla*' WHERE id = 0";(3) $data= $db->exec($update);

    id name pw groupID 0 root *balla* 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0

    Zur Vorbereitung einer kleinen Demo fr SQL-Injection wird ein Update ausprobiert.

    SQL-Updates werden ganz analog zum Insert also auch mit gebundenen Parametern - ausgefhrt.

  • 47Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    SQL Injection II

    (1) $LoginPW= 'bitte!';(2) $update= "UPDATE account SET pw = '*balla*' WHERE id = 0";(3) $LoginName= "a';$update";(4) $query= "SELECT * FROM account WHERE name='$LoginName'";(5) echo $query.PHP_EOL;(6) $data= $db->query($query);(7) $rows= $data->fetch();(8) if(($rows!==false)&&($LoginPW==$rows['pw'])) {(9) echo "loggt in".PHP_EOL;(10)} else {(11) echo "NOT loggt in".PHP_EOL;(12)}

    Statt eines vernnftigen Loginnamens wird wie in (3) simuliert ein Mischung aus einem Namen und einer SQL-Anweisung hier ein Update eingegeben. Die Zusammensetzung ist unten in 2 Zeilen dargestellt.

    SELECT * FROM account WHERE name='a';UPDATE account SET pw = '*balla*' WHERE id = 0'Ausgabe(5):

    Quotes

  • 48Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    SQL Injection III - Abwehr

    (1) $LoginPW= 'bitte!';(2) $update= "UPDATE account SET pw = '*balla*' WHERE id = 0";(3) $LoginName= "a';$update";(4) $LoginName= $db->quote($LoginName);(5) $query= "SELECT * FROM account WHERE name=$LoginName";(6) echo $query.PHP_EOL;

    quote() fgt vor allen "gefhrlichen" Zeichen ein Backslash ein, der die Funktion des Zeichens ausschaltet. Daher ist der Loginname der gesamte unterstrichene Bereich. Es wird kein weiteres SQL-Kommando ausgefhrt.

    Zum Glck oder aus Absicht ist MySQL so konfiguriert, dass auch ohne quote() nichts passiert wre, da jeweils nur ein Kommando ausgefhrt werden kann.

    Es mssen trotzdem immer Formulareingaben vor dem Einsetzenin SQL-Strings geprft werden!

    SELECT * FROM account WHERE name='a\';UPDATE account SET pw = \'*balla*\' WHERE id = 0'

    Ausgabe:KeineQuotes

  • 49Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    SQL Injection IV Beste Abwehr

    (1) $LoginPW= 'bitte!';(2) $update= "UPDATE account SET pw = '*balla*' WHERE id = 0";(3) $LoginName= "a';$update";(4) $query= "SELECT name, pw FROM account WHERE name=:LoginName";(5) $stmt= $db->prepare($query);(6) $stmt->bindValue(':LoginName',$LoginName,PDO::PARAM_STR);(7) $stmt->execute();(8) $stmt->bindColumn('name',$RealName,PDO::PARAM_STR);(9) $stmt->bindColumn('pw' ,$RealPW, PDO::PARAM_STR);(10)$rows= $stmt->fetch();(11)echo $RealName." ".$RealPW.PHP_EOL;(12)if($LoginPW==$RealPW) {(13) echo "loggt in".PHP_EOL;(14)...

    Die Benutzung von Prepared Statements ist am Besten, da die Formulardaten immer als Daten behandelt werden.

  • 50Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    SQL Injection V Erluterungen

    (4) $query= "SELECT name, pw FROM account WHERE name=:LoginName";(6) $stmt->bindValue(':LoginName',$LoginName,PDO::PARAM_STR);(8) $stmt->bindColumn('name',$RealName,PDO::PARAM_STR);(9) $stmt->bindColumn('pw' ,$RealPW, PDO::PARAM_STR);

    Spaltennamen von erzeugten temporren Tabellen (durch SELECT) lassen sich auch PHP-Variablen zuordnen.

    bindColumn() verbindet den Namen einer SQL-Spalte mit einer Variablen. Nach jedem fetch() werden die aktuellen Werte der gelesenen Zeile den

    Variablen zugewiesen. Der Angriff via SQL-Injection funktioniert nun nicht mehr, da das SQL-

    UPDATE als Datum in der WHERE-Klausel behandelt wird.

    Das ndert nichts daran, alle Formulardaten zu prfen!

  • 51Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Transaktionen I

    (1) $db->beginTransaction();(2) $update= "UPDATE account SET pw = '*balla*' WHERE id = 0";(3) $data= $db->exec($update);(4) $update= "UPDATE account SET pw = '*' WHERE id = 3";(5) $data= $db->exec($update);(6) var_dump($data);(7) $update= "UPDATE account SET pw = '??' WHERE id = 4";(8) $data= $db->exec($update);(9) var_dump($data);(10)$db->commit();(11)//$db->rollBack();

    Mit beginTransaction() wird eine Transaktion eingeleitet und mit commit() positiv abgeschlossen; erst dann sind die Daten gendert sichtbar.

    Mit rollBack() werden alle nderungen verworfen. Der Rckgabewert von UPDATE ist die Anzahl der betroffenen Zeilen.

    Fehlerhaftes UPDATE

  • 52Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Transaktionen II

    int(1)int(0) id name pw groupID 0 root *balla* 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn * 0

    int(1)int(0) id name pw groupID 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0

    commit()

    rollBack()

    Fehlerhaftes UPDATE

  • 53Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Tipps I Globales Definieren

    Globale Parameterwerte sollten in eine Include-Datei mit der Endung ".inc.php" ausgelagert und dann als "Kopf" eingebunden werden.

    Aus Sicherheitsgrnden sollte die Endung immer ".php" sein (was fr ".inc.php" ja gilt), da der Server diese Datei nie(?) uninterpretiert zum Browser sendet.

  • 54Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Tipps II Benutzung der globalen Definitionen

    Durch die Auslagerung der Parameter in eine externe Datei wird der Code noch allgemeingltiger und dadurch leichter zu warten.

    Die Optionen knnten in einem weiteren Schritt auch ausgelagert werden.

    (1) require('myParameter.inc.php'); (2) $DB_options = array((3) PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'(4) ); (5) try {(6) $db= new PDO($Param['dsn'],$Param['uid'],(7) $Param['pass'],$DB_options);

  • 55Webtechnologien WS 2017/18 - Teil 8/PHP und MySQL

    Nach dieser Anstrengung etwas Entspannung...

    Computer-Systeme Teil 12: Einfhrung in BetriebssystemebersichtEinfhrung IPHP-Schnittstellen zu Datenbanken (Auszug)MySQL-SchnittstellenFolie 6Folie 7Verbindung zur Datenbank IFolie 9Folie 10Folie 11Folie 12Folie 13Folie 14Folie 15Folie 16Folie 17Folie 18Folie 19Folie 20Folie 21Folie 22Folie 23Folie 24Folie 25Folie 26Folie 27Folie 28Folie 29Folie 30Folie 31Folie 32Folie 33Folie 34Folie 35Folie 36Folie 37Folie 38Folie 39Folie 40Folie 41Folie 42Folie 43Folie 44Folie 45Folie 46Folie 47Folie 48Folie 49Folie 50Folie 51Folie 52Tipps I Globales DefinierenTipps II Benutzung der gloablen DefinitionenNach dieser Anstrengung etwas Entspannung...