Webtechnologien Teil 12: PHP 5 mit PDO/MySQL



Ähnliche Dokumente
Webtechnologien. Teil 8: PDO/MySQL

Webtechnologien. Teil 8: PDO/MySQL

Webtechnologien. Teil 8: PDO/MySQL

Webtechnologien. Teil 8: PDO/MySQL

Webtechnologien. Teil 8: PDO/MySQL

OP-LOG

Datenbanken für Online Untersuchungen

PHP - Projekt Personalverwaltung. Erstellt von James Schüpbach

Hinweise zur Installation von MySQL

BEDIENUNG ABADISCOVER

php Hier soll ein Überblick über das Erstellen von php Programmen gegeben werden. Inhaltsverzeichnis 1.Überblick Parameterübergabe...

Oracle: Abstrakte Datentypen:

Internationales Altkatholisches Laienforum

ecaros2 - Accountmanager

Datenbanken SQL Einführung Datenbank in MySQL einrichten mit PhpMyAdmin

1 Vom Problem zum Programm

Tutorial Einrichtung eines lokalen MySQL-Servers für den Offline-Betrieb unter LiveView

5. Übung: PHP-Grundlagen

Whitepaper. Produkt: combit Relationship Manager. Einbindung externer FiBu-/Warenwirtschaftsdaten. combit GmbH Untere Laube Konstanz

Views in SQL. 2 Anlegen und Verwenden von Views 2

Hilfedatei der Oden$-Börse Stand Juni 2014

Wordpress: Blogbeiträge richtig löschen, archivieren und weiterleiten

Unsere Webapplikation erweitern

SQL-Injection. Seite 1 / 16

Einführung in die Java- Programmierung

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

Anleitung für die Einrichtung weiterer Endgeräte in 4SELLERS SalesControl

mysql - Clients MySQL - Abfragen eine serverbasierenden Datenbank

E-Commerce: IT-Werkzeuge. Web-Programmierung. Kapitel 6: Datenbankabfragen mit SQL und PHP. Stand: Übung WS 2014/2015

E Mail Versand mit der Schild NRW Formularverwaltung

CMS.R. Bedienungsanleitung. Modul Cron. Copyright CMS.R Revision 1

Stundenerfassung Version 1.8 Anleitung Arbeiten mit Replikaten

Advoware mit VPN Zugriff lokaler Server / PC auf externe Datenbank

Dipl. Inf. Dipl. Math. Y. Orkunoglu Datum:

Datenbanken Kapitel 2

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Grundlagen der Informatik 2

Professionelle Seminare im Bereich MS-Office

SQL (Structured Query Language) Schemata Datentypen

MORE Profile. Pass- und Lizenzverwaltungssystem. Stand: MORE Projects GmbH

Dokumentation IBIS Monitor

I Serverkalender in Thunderbird einrichten

Stundenerfassung Version 1.8

Anleitung zur Konfiguration eines NO-IP DynDNS-Accounts mit der TOOLBOXflex-3.2

Suche schlecht beschriftete Bilder mit Eigenen Abfragen

5 DATEN Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu

teamsync Kurzanleitung

4. BEZIEHUNGEN ZWISCHEN TABELLEN

Adminer: Installationsanleitung

Auto-Provisionierung tiptel 31x0 mit Yeastar MyPBX

Kapitel 33. Der xml-datentyp. In diesem Kapitel: Der xml-datentyp 996 Abfragen aus xml-datentypen 1001 XML-Indizierung 1017 Zusammenfassung 1023

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom b

Erstellung botoptimierter Partnerlinks

Aufklappelemente anlegen

Guideline. Facebook Posting. mit advertzoom Version 2.3

Anleitung zur Anmeldung beim EPA zur Nutzung von OPS 3.1

PHPNuke Quick & Dirty

Datenübernahme von HKO 5.9 zur. Advolux Kanzleisoftware

Verschlüsseln von Dateien mit Hilfe einer TCOS-Smartcard per Truecrypt. T-Systems International GmbH. Version 1.0 Stand

TYPO3 Tipps und Tricks

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Projektbericht Gruppe 12. Datenbanksysteme WS 05/ 06. Gruppe 12. Martin Tintel Tatjana Triebl. Seite 1 von 11

R-ADSL2+ Einrichthinweise unter Windows 98/ME

Oracle SQL Tutorium - Wiederholung DB I -

Objektorientierte Programmierung

Zur drittletzten Zeile scrollen

PHP und MySQL. Integration von MySQL in PHP. Zellescher Weg 12 Willers-Bau A109 Tel Michael Kluge (michael.kluge@tu-dresden.

Webakte in Advolux Verfasser : Advolux GmbH Letze Änderung : 10. Juli

FuxMedia Programm im Netzwerk einrichten am Beispiel von Windows 7

Java Database Connectivity (JDBC) Walther Rathenau Gewerbeschule 1

GITS Steckbriefe Tutorial

STRATO Mail Einrichtung Microsoft Outlook

Info-Veranstaltung zur Erstellung von Zertifikaten

STRATO Mail Einrichtung Mozilla Thunderbird

Windows Vista Security

Datenbank-Verschlüsselung mit DbDefence und Webanwendungen.

MSXFORUM - Exchange Server 2003 > SMTP Konfiguration von Exchange 2003

7. Datenbank-Zugriff. Vorlesung und Übung Dr. Peter Pfahler Institut für Informatik Universität Paderborn. Zum Beispiel aus PHP-Skripten: Client 7-2

Einführung in die Programmierung

PKV- Projektanlage Assistent

Abschluss Version 1.0

Datenbanksysteme SS 2007

Programmieren für mobile Endgeräte SS 2013/2014. Dozenten: Patrick Förster, Michael Hasseler

In diesem Thema lernen wir die Grundlagen der Datenbanken kennen und werden diese lernen einzusetzen. Access. Die Grundlagen der Datenbanken.

Access [basics] Aktionsabfragen per VBA ausführen. Beispieldatenbank. Aktionsabfragen. Die Execute-Methode. Datenzugriff per VBA

Menü auf zwei Module verteilt (Joomla 3.4.0)

Verschlüsselung mit PGP. Teil 1: Installation

Lehrer: Einschreibemethoden

Hochschule Ravensburg-Weingarten. Technik Wirtschaft Sozialwesen. Projektarbeit

MARCANT - File Delivery System

Einführung in die Scriptsprache PHP

How to install freesshd

Website freiburg-bahai.de

Thermoguard. Thermoguard CIM Custom Integration Module Version 2.70

Enigmail Konfiguration

AutoCAD Dienstprogramm zur Lizenzübertragung

SEMINAR Modifikation für die Nutzung des Community Builders

Transkript:

Webtechnologien Teil 12: PHP 5 mit PDO/MySQL 01.05.15 1

Übersicht Arten der Kommunikation mit dem Datenbank-Server Grundsätzliche Operationen Weitere Routinen Tricks und Tipps Siehe: http://dev.mysql.com/doc/refman/5.5/en/apis-php.html http://de3.php.net/manual/en/book.mysqli.php 2

Einführung 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. 3

PHP-Schnittstellen zu Datenbanken (Auszug) Datenbank Datenbank Datenbank Adabas D Oracle MySQL dbase PostgreSQL MySQLi Empress Solid Informix FrontBase Sybase Velocis FilePro Interbase Ingres DB2 Unix dbm Interbase Und natürlich auch ODBC... Siehe dazu: http://de.wikipedia.org/wiki/open_database_connectivity 4

Schnittstellen zu MySQL Es gibt aus historischen Gründen drei Schnittstellen zu MySQL: mysql-schnittstelle (Veraltet, wird nicht mehr unterstützt) mysqli-schnittstelle (improved, die aktuelle Version) PDO-Schnittstelle (Portable Data Object) Diese abstrahiert von den verschiedenen Schnittstellen zu den Datenbanken aber nur syntaktisch - den Aufruf. Die speziellen Eigenschaften der SQL-Dialekte der jeweiligen Datenbanken bleiben jedoch erhalten! Diese Schnittstelle wird hier beschrieben. 5

Das PDO-Objekt Die Benutzung der Schnittstelle erfolgt nur Objekt-orientiert. Durch das Erzeugen des Objekts wird 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 6

Das PDO-Objekt - Vorbereitungen In der php.ini-datei müssen die Module für den Datenbankzugriff aktiviert sein: extension=php_mysqli.dll extension=php_pdo_mysql.dll extension=php_pdo_odbc.dll Das lässt sich durch einen Aufruf von phpinfo() anzeigen (explizit programmiert oder über die XAMPP-Webschnittstelle): 7

Bemerkungen In diesem Foliensatz wird ein Teil eines Logins für eine Website beschrieben. 8

Verbindung zur Datenbank I object PDO(DSN [,Nutzer [,Passwort[,Optionen]]]); DSN ist der Data Source Name: die Definition des Zugriffs auf die Datenbank. Dies ist ein String mit dem Aufbau: Datenbanktyp: Keywort=Wert; Keywort=Wert; Datenbanktyp ist hier mysql Für Keywort können hier eingesetzt werden: Host: Angabe der IP-Adresse der Datenbank dbname: Name der ausgewählten Datenbank Nutzer und Passwort sind Strings Der letzte Parameter definiert Optionen, die für die Kommunikation mit der Datenbank wichtig sind (ein Hash mit definierten Keys) Siehe: http://www.php.net/manual/de/book.pdo.php http://de.wikipedia.org/wiki/data_source_name 9

Verbindung zur Datenbank II - Beispiel (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)} Die Datenbank liegt auf der eigenen Maschine und heißt "accounts". Der User ist "nobody" mit dem Passwort "blabla426". Mit den Optionen wird der Zeichensatz auf UTF-8 gesetzt. Es wird der Warn-Modus eingeschaltet. 10

Verbindung zur Datenbank III - Erläuterungen (4) $DB_options = array( (5) PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' (6) ); MySQL-spezifische Konstante(n) Es sind noch andere Kommandos an MySQL möglich, 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_COMPRESS Daten der Verbindung werden komprimiert Siehe dazu: http://de2.php.net/pdo_mysql 11

Verbindung zur Datenbank IV - Erläuterungen (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)} Im Fehlerfalle wirft das PDO-Objekt eine Exception, die mit der obigen Konstruktion gefangen werden kann. getmessage() erzeugt einen String mit der Fehlermeldung. PHP_EOL ist die Konstante mit der Bedeutung End-of-Line. 12

Verbindung zur Datenbank V - Erläuterungen (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) { Nachdem das Objekt generiert wurde, können dem Objekt Eigenschaften zugewiesen werden, die aber nur das PDO-Objekt betreffen, nicht die Kommunikation mit 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_WARNING Es wird eine Warn-Meldung ausgegeben sowie der Errorcode geliefert PDO::ERRMODE_EXCEPTION Es wird eine Exception geworfen (siehe erstes Beispiel). Siehe dazu: http://www.php.net/manual/de/pdo.error-handling.php 13

Hinweise Ob mit Exceptions oder mit einem der anderen Modi gearbeitet wird, hängt von der Programmierkonvention im betreffenden Projekt ab. Die Benutzung von Exceptions führt zu einem umständlichen Code; dafür entfallen die vielen Fehlerabfragen. Wer sowieso immer den Erfolg einer Routine in einer Fehlerabfrage abfragt, der sollte während der Entwicklung im Warn-Modus während der Produktion im Silent-Modus arbeiten. 14

Unser Beispiel weiter I Der MySQL-Server muss natürlich laufen. Auch muss die Datenbank mit dem User "nobody" und dem Passwort "blabla426" eingerichtet sein. 15

Falls es doch mal nicht weiter geht... print_r($db->getavailabledrivers()); Array ( [0] => mysql [1] => sqlite ) Mit der Methode getavailabledrivers() lassen sich Informationen über die per PDO erreichbaren Datenbanken ausgeben. print_r() gibt das Array etwas formatiert und daher leichter lesbar aus. Es wird ein Array mit den den Namen der unterstützten Datenbanken geliefert, hier nur zwei. Änderungen sind nur über php.ini möglich. 16

Einrichten einer Datenbank I 17

Einrichten einer Datenbank II Es hat geklappt! 18

Einrichten einer Datenbank III 19

Einrichten einer Datenbank III 20

Hinweis Das Gewähren aller Rechte für einen Benutzer ist nur dann sinnvoll, wenn es wie hier um eine Spielanwendung geht. In der Realität müssen die Rechte beschränkt werden. Diese Beschränkung lässt sich gut mit phpmyadmin konfigurieren. 21

Einrichten einer Datenbank IV So sieht es schon einmal ganz gut aus... 22

Unser Beispiel weiter II Create Table Nun bauen wir eine Tabelle in SQL: CREATE TABLE account ( id int(11) NOT NULL PRIMARY KEY, name varchar(45) NOT NULL, pw varchar(64) NOT NULL, groupid int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; id ist der Schlüsselwert in der Tabelle. Der Login-Name und das Passwort dürfen nicht leer sein(!). Dann bereiten wir noch Gruppen vor, indem eine Gruppen-ID als Integer zugelassen wird. Da wird Transaktionen haben wollen muss die InnoDB-Version von MySQL benutzt werden. Und natürlich UTF-8 als Zeichensatz. 23

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. Der String für $create_table wird stückweise mit der verkürzten.-notation zusammengesetzt. 24

Create Table I - Erläuterungen (7) $rtn= $db->exec($create_table); (8) if($rtn===false) { exec(<sql-string>) führt eine SQL-Operation aus, bei der keine Werte anschließend abgefragt werden, d.h. exec() kann nicht für Abfragen von Tabelleninhalten benutzt werden. In der Variablen $rtn wird der Return-Code abgespeichert, der im Fehlerfall den boole'schen Wert false (0) hat, ansonsten die Anzahl der betroffenen Zeilen in der Tabelle enthält. Daher muss mit === abgefragt werden (weil die Anzahl ja auch 0 sein kann, was ansonsten als false interpretiert wird) Siehe: http://www.php.net/manual/de/pdo.exec.php 25

Create Table II - Erläuterungen (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 zurück: [0] enthält den SQL-Error-Code [1] enthält den Error-Code des Treibers [2] enthält 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. Dazu gibt es eine Dokumentation: http://docstore.mik.ua/orelly/java-ent/jenut/ch08_06.htm Siehe: http://www.php.net/manual/de/pdo.errorcode.php 26

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 gelöscht. Das machen wir hier nur für unser Beispiel, damit wir in mehreren Durchläufen etwas ausprobieren können. Für ein Login-Modul ist das natürlich unsinnig. 27

Unser Beispiel weiter V Einfügen 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 (Zeile) eingefügt. Bitte beachten Sie, dass in SQL andere String-Begrenzer als in PHP benutzt werden. 28

Wir glauben ja nichts sehen wir nach Das ist die Ausgabe der Tabelle in phpmyadmin. 29

Unser Beispiel weiter VI Einfügen 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 eine Tabelle zum Füllen der Datenbank-Tabelle verwendet. 30

Einfügen von Daten - Erläuterungen (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 {}. Ohne diese Auswertung wäre $Variable ausreichend. Die einzelnen Elemente des Hash sind ja benannt, so dass relativ schnell klar wird, was wann in das Insert-SQL-Statement eingefügt wird. 31

Wir glauben ja nichts sehen wir nach Das ist die Ausgabe der Tabelle in phpmyadmin. 32

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 ein neues 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. Möglich sind: PDO::FETCH_ASSOC: Es wird ein Hash mit den Namen der Spalten geliefert (das ist zu empfehlen). PDO::FETCH_BOTH: Hash mit Array-Indices wird geliefert. PDO::FETCH_OBJ: Ein Objekt wird generiert. 33

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. Mit jedem Schleifendurchlauf wird eine Zeile aus der Tabelle gelesen und als Hash geliefert. 34

Explizites Auslesen von Daten (5) $data= $db->query($query); $row= $data->fetch($fetch_style); $table= $data->fetchall($fetch_style); Ein query() liefert den Inhalt einer Tabelle als ein Objekt vom Typ PDOStatement. Das foreach des Beispiels durchläuft in Wirklichkeit ein Objekt. Mit fetch() wird die nächste Zeile des letzten Querys gelesen. Der optionale Parameter gibt an, von welchem Typ das Ergebnis sein soll. Hier sind dieselben Werte wie beim setattribute() möglich. Der Defaultwert ist der, der als Attribut beim Erzeugen des Objektes angegeben wurde. fetchall() liest die ganze Tabelle in ein Array ein. Die Art der Elemente dieses Arrays werden genauso bestimmt wie bei fetch(). fetchall() ist natürlich nur bei kleinen Tabellen anwendbar. 35

Abstraktes Auslesen I Meta-Information (1) $query= "SELECT * FROM account"; (2) $data= $db->query($query); (3) echo $data->columncount().php_eol; (4) for($i= 0;$i<$data->columnCount();$i++) { (5) var_export($data->getcolumnmeta($i)); (6) } Meta-Informationen sind solche über etwas. Mit columncount() wird die Anzahl der Spalten ausgelesen, mit getcolumnmeta() eine Beschreibung der Spalte. Rechts steht der Beginn der Ausgabe. Es ist der Aufbau des Deskriptors erkennbar. 4 array ( 'native_type' => 'LONG', 'pdo_type' => 2, 'flags' => array ( 0 => 'not_null', 1 => 'primary_key', ), 'table' => 'account', 'name' => 'id', 'len' => 11, 'precision' => 0, ) 36

Abstraktes Auslesen II Ausgabe der Tabelle (1) $data= $db->query($query); (2) $limit= $data->columncount(); (3) for($i= 0;$i<$limit;$i++) { (4) $meta= $data->getcolumnmeta($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)} Es wird eine Überschrift für die Ausgabe aus der Datenbank generiert. Dann werden in einer doppelten for-schleife die Zeilen ausgegeben: jeweils eine Zeile mit mehreren Einträgen. id name pw groupid 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0 37

Abstraktes Auslesen III Ausgabe der Tabelle (1) $data= $db->query($query); (2) $limit= $data->columncount(); (3) for($i= 0;$i<$limit;$i++) { (4) $meta= $data->getcolumnmeta($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)} id name pw groupid 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0 Damit es etwas schöner 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. 38

Nun endlich zum Login I (1) $LoginName= 'root'; (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. 1 array ( 0 => array ( 'id' => '0', 'name' => 'root', 'pw' => 'bitte!', 'groupid' => '0', ), )loggt in 39

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. array array ( 'id' => '0', 'name' => 'root', 'pw' => 'bitte!', 'groupid' => '0', )loggt in 40

Alternative zum Füllen 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(); id name pw groupid 0 root bitte! 0 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(), also als Wertekopie, den? mit einer Typangabe (z.b. PDO::PARAM_INT) zugeordnet. Beim execute() werden die Werte zum Datenbank-Server geschickt, das SQL- Statement dort ausgefüllt und dann ausgeführt. 41

Alternative zum Füllen der Tabelle II Schablone erzeugen Parameter zuordnen Ausführen bindvalue() bindparam() Integer: PDO::PARAM_INT String: PDO::PARAM_STR Bool: PDO::PARAM_BOOL Das ist das allgemeine Schema bei der Benutzung von gebundenen Parametern. Neben dem schon eingeführten bindvalue() gibt es noch das bindparam(), das die Bindung über eine Referenz realisiert. Neben den beiden Typen für String und Integer gibt es noch den Typ Boolean. Siehe dazu: http://www.php.net/manual/en/pdostatement.bindparam.php 42

Alternative zum Füllen 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)} Das Hash $val ist wie oben gesetzt. id name pw groupid 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0 43

Alternative zum Füllen der Tabelle IV - Bemerkung (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); 44

Alternative zum Füllen der Tabelle V (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) } Das Hash $val ist wie oben gesetzt. (12) $stmt->execute(); (13)} Mit einem Doppelpunkt eingeleitet lassen sich die? wie Variablen adressieren. 45

Alternative zum Füllen der Tabelle VI - Erläuterung (8) foreach($val as $row) { (9) foreach($row as $key=>$val) { (10) $$key= $val; (11) } (12) $stmt->execute(); (13)} Hier wird der Name der Variablen anhand des Wertes einer anderen Variablen bestimmt! So etwas geht nur in Skriptsprachen. Auszug vom Setzen des Hash $val: $val[]=array('id'=>0,'name'=>'root', 'pw'=>'bitte!','groupid'=>0); $val[]=array('id'=>1,'name'=>'user', 'pw'=>'danke!','groupid'=>0); 46

SQL Injection I (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 für SQL-Injection wird ein Update ausprobiert. SQL-Updates werden ganz analog zum Insert also auch mit gebundenen Parametern - ausgeführt. 47

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(); Quotes (8) if(($rows!==false)&&($loginpw==$rows['pw'])) { (9) echo "loggt in".php_eol; (10)} else { (11) echo "NOT loggt in".php_eol; (12)} Ausgabe: SELECT * FROM account WHERE name='a'; UPDATE account SET pw = '*balla*' WHERE id = 0' Statt eines vernünftigen Loginnamens wird wie in (3) simuliert ein Mischung aus einem Namen und einer SQL-Anweisung hier ein Update eingegeben. Die Zusammensetzung wird unten in 2 Zeilen dargestellt. 48

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; Ausgabe: SELECT * FROM account WHERE name='a\'; UPDATE account SET pw = \'*balla*\' WHERE id = 0' Keine Quotes quote() fügt vor allen "gefährlichen" Zeichen ein Backslash ein, der die Funktion des Zeichens ausschaltet. Daher ist der Loginname der gesamte unterstrichene Bereich. Es wird kein weiteres SQL-Kommando ausgeführt. Zum Glück oder aus Absicht ist MySQL so konfiguriert, dass auch ohne quote() nichts passiert wäre, da jeweils nur ein Kommando ausgeführt wird. Es müssen immer Formulareingaben vor dem Einsetzen in SQL- Strings geprüft werden! 49

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 das Beste, da die Formulardaten immer als Daten behandelt werden. 50

SQL Injection V Erläuterungen (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 temporären 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 prüfen! 51

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(); Fehlerhaftes UPDATE Mit begintransaction() wird eine Transaktion eingeleitet und mit commit() positiv abgeschlossen; erst dann sind die Daten geändert sichtbar. Mit rollback() werden alle Änderungen verworfen. Der Rückgabewert von UPDATE ist die Anzahl der betroffenen Zeilen. Das funktioniert nur bei Tabellentypen, die Transaktionen unterstützen. 52

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 Fehlerhaftes UPDATE commit() int(1) int(0) id name pw groupid 0 root bitte! 0 1 user danke! 0 2 helmut sdgafg 0 3 evelyn tztrff 0 rollback() 53

Tipps I Globales Definieren <?php $Param['host']= 'localhost'; $Param['uid'] = 'nobody'; $Param['pass']= 'bitte!'; $Param['db'] = 'account';?> $Param['dsn'] = "mysql:host=${$param['host']}"; $Param['dsn'].= ";dbname=${$param['db']}"; Globale Parameterwerte sollten in eine Include-Datei mit der Endung ".inc.php" ausgelagert und dann als "Kopf" eingebunden werden. Aus Sicherheitsgründen sollte die Endung immer ".php" sein (was für ".inc.php" ja gilt), da der Server diese Datei nie(?) uninterpretiert zum Browser sendet. 54

Tipps II Benutzung der globalen Definitionen (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); Durch die Auslagerung der Parameter in eine externe Datei wird der Code noch allgemein gültiger und dadurch leichter zu warten. Die Optionen könnten in einem weiteren Schritt auch ausgelagert werden. 55

Nach dieser Anstrengung etwas Entspannung... 56