Internettechnologien, LE 3: PHP, SQL und HTML

Werbung
Inhaltsverzeichnis
Inhaltsverzeichnis
Vorbemerkungen zu dieser Lerneinheit......................................
2
1
Datenbankzugriffe übers Web: HTML & PHP & SQL ........
3
1.1
Zugriff von PHP auf MySQL .....................................
4
1.2
Anzeigen von Daten im Browser............................... 10
1.3
Informationen über gerade abgesetzte SQL-Befehle....... 13
1.4
SQL Injection entschärfen mit escape_string ............... 13
1.5
Zusammenfassung ............................................... 14
2
Datenbankinteraktionen mit dem Browser .................. 15
2.1
Eintragen-Skript mit unformatierten Ausgaben ............. 16
2.2
Eintragen-Skript mit Funktionen und Formatierungen..... 19
2.3
Zusammenfassung ............................................... 23
3
Übungsaufgaben ................................................. 24
4
Lösungen der Aufgaben ......................................... 27
Abbildungsverzeichnis........................................... 41
1
Inhaltsverzeichnis
Vorbemerkungen zu dieser Lerneinheit
Die vorliegende Lerneinheit ist die vom Seitenumfang her kleinste des
Lehrbriefs zu Internettechnologien, vom Zeitaufwand der Programmierung ihrer Aufgabenlösungen aber die größte. Sie ist in gewisser Weise
das Herzstück des gesamten Lehrbriefs. Hier werden die in den vorhergehenden Lerneinheiten dargestellten Webtechniken HTML und PHP
mit einer Datenbank kombiniert, so dass interaktive Datenbankzugriffe
über den Browser ermöglicht werden. Es ist genau diese Kombination
bekannter Konzepte, die knappe und gebrauchstaugliche Handlungsanleitungen gegenüber einer breiten theoretischen Diskussion oder
grundlegenden Erläuterungen in den Vordergrund treten lassen.
Damit setzt allerdings das Verständnis dieser Lerneinheit auch Kenntnisse und Programmiererfahrungen in HTML, PHP und SQL voraus. Das
größte Problem für den Einsteiger in die Webprogrammierung ist erfahrungsgemäß, Struktur in diesen kleinen Zoo von Programmierkonzepten und Techniken zu erlangen und deren einzelne Stärken und
Grenzen zu erkennen und einzusetzen. Das erfordert aber hauptsächlich Praxis, weniger Theorie.
Zur Lösung und Programmierung der Aufgaben ist zudem ein Serversystem mit einem Webserver und einem Datenbankserver notwendig.
Für eine einfache Installation sei dazu das frei verfügbare Paket XAMPP
empfohlen, das aktuell mit dem Apache HTTP-Server und der quelloffenen Datenbank MariaDB ausgeliefert wird. Grundsätzlich können aber
auch andere, PHP integrierende Serverdistributionen verwendet werden, so dass die Inhalte dieser Lerneinheit weitgehend unverändert anwendbar bleiben.
Hagen,
im Dezember 2016
2
Andreas de Vries
1
Datenbankzugriffe übers Web: HTML & PHP & SQL
In diesem Kapitel werden wir die verschiedenen Fäden, die wir in den
vergangenen Kapiteln gesponnen haben, zu einer Gesamtheit zusammen knüpfen. Wir haben PHP als eine Programmiersprache kennen gelernt, die auf einem Webserver läuft und dessen Ressourcen nutzen
kann. Als Modul des Webservers kann PHP insbesondere die Schnittstellen zu einem Client des Webservers nutzen, d.h. einem Browser. Das
Abb. 1.1: Die drei Schichten einer datenbankbasierten Webanwendung
PHP-Modul muss also PHP-Code als HTML-Code ausgeben und durch
den Webserver an den Browser senden.
Nun wollen wir unser HTML-PHP-System erweitern und eine relationale Datenbank anbinden. Die Programmiersprache für Zugriffe auf eine solche Datenbank ist SQL. Jetzt kehren sich die Rollen in unserem
System gewissermaßen um, PHP ist nunmehr der Client für den Datenbankserver, der auf SQL-Befehle reagiert und mit Daten antwortet.
Abb. 1.2: PHP als Schnittstelle, die über reinen Text kommuniziert
Somit stellt PHP eine Schnittstelle zwischen Browser (WebClient) und
einer MySQL Datenbank (DB-Server) dar. In einem PHP-Skript kann natürlich weder HTML- noch SQL-Code verarbeitet werden. Beides kann
nur dynamisch als String erzeugt werden und entweder per echo an den
Browser oder, nach geeigneteter Vorbereitung, per $mysqli->query() an
die Datenbank geschickt werden.
3
1
Datenbankzugriffe übers Web: HTML & PHP & SQL
1.1
Zugriff von PHP auf MySQL
Um auf Daten einer allgemeinen Datenbank zuzugreifen, also nicht nur
auf eine lokale Datenbank wie Access, sondern auch einen Datenbankserver wie MariaDB, MySQL, Oracle oder MSSQL, benötigt man im Wesentlichen die folgenden Schritte.
1. Verbindung zur Datenbank eines Datenbankservers erstellen;
2. Abfrage (query) zur Datenbank per SQL absetzen;
3. Datensätze von Auswahlabfragen (result) aus der Datenbank in
eine geeignete Datenstruktur von PHP umformen.
Für eine Webanwendung in PHP, die Zugriffe auf eine MySQLDatenbank aus dem Web ermöglichen soll, müssen wir diese Schritte programmieren können. Dazu stehen in PHP Funktionen zur Verfügung, die wir im folgenden kennen lernen werden. Listen wir hier einmal die Schritte für einen Datenzugriff auf und erwähnen die dazu verwendeten PHP-Funktionen; in den nächsten Abschnitten werden wir
sie näher betrachten.
Aktionsschritt
1. Verbindung zur Datenbank erstellen
2. SQL-Abfrage zur Datenbank senden
3a. Abfrageergebnis als Objekt in
PHP einlesen
3b. Abfrageergebnis als assoziatives
Array in PHP einlesen
3c. Abfrageergebnis als numerisches
Array in PHP einlesen
PHP-Anweisung
$mysqli = new mysqli(...)
$result = $mysqli->query(...)
$result->fetch_object()
$result->fetch_assoc()
$result->fetch_row()
Die Schritte 3a, 3b und 3c der Ergebniseinlesung nach PHP sind hierbei als Alternativen anzusehen, sie unterscheiden sich lediglich in der
Datenstruktur, in der die Datensätze der Ergebnismenge der Datenbankabfrage in PHP gespeichert werden.
Hat man die Verbindung zu einer Datenbank eingerichtet, so hat man
u.a. die folgenden Abfragefunktionen über SQL zur Verfügung:
1. Eintragen von Daten mit INSERT. Die Syntax dazu lautet:
INSERT INTO tabelle (spalte1, spalte2, ...)
VALUES (’wert1’, ’wert2’, ...)
oder
4
1.1
Zugriff von PHP auf MySQL
INSERT INTO tabelle SET
spalte1 = ’wert1’,
spalte2 = ’wert2’,
...
2. Ansehen gespeicherter Daten mit SELECT:
SELECT spalte_x, spalte_y FROM tabelle # WHERE bedingung
...
Will man alle Daten einer Tabelle erhalten, so kann man statt der
einzelnen Spaltennamen auch einfach die Wildcard * angeben.
3. Ändern von gespeicherten Daten mit UPDATE:
UPDATE tabelle SET
spalte1 = ’neuerWert1’,
spalte2 = ’neuerWert2’,
...
WHERE Primärschlüssel = ’x’
4. Löschen gespeicherter Daten mit DELETE:
DELETE FROM tabelle WHERE bedingungen
(Achten Sie auf die WHERE-Klausel, sonst ist alles gelöscht . . . )
1.1.1
Verbindung zum Datenbanksystem: new
mysqli(...)
Der Verbindungsaufbau zu einer einer Datenbank geschieht durch
die Erzeugung eines mysqli-Objekts, $mysqli = new mysqli(...), eines
Standardobjekts in PHP:
$mysqli = new mysqli("localhost", "user", "passwort", "db");
Der Konstruktor des PHP-Objekts mysqli erwartet vier Eingabeparameter:
1. Den Namen des Datenbankservers: Dies ist hier "localhost".
Hiermit wird der Rechner, auf dem das PHP-Programm selber
läuft, angesprochen. Alternativ kann hier die IP-Adresse eines anderen Rechners stehen; allerdings muss das Serversystem dazu
entsprechend konfiguriert sein.
2. Den Namen des Benutzers, hier "user". Hier kann der Name eines
jeden beim Datenbankserver registrierten Nutzers stehen.
3. Das Passwort des Benutzers, hier: "passwort".
4. Den Namen der Datenbank, hier "db".
5
1
Datenbankzugriffe übers Web: HTML & PHP & SQL
Optional kann als fünfter Parameter der Port des Datenbankservers angegeben werden, sein Standardwert ist 3306 und funktioniert dementsprechend bei den üblichen Datenbankinstallationen.
Es ist zu empfehlen, zur besseren Übersicht und zur vereinfachten Einstellung bei etwaig geänderten Datenbankzugriffen die Aufrufparameter in Variablen zu speichern:
$server
= "localhost";
$user
= "user";
$password = "passwort";
$database = "datenbank";
$mysqli
= new mysqli($server, $user, $password, $database);
$mysqli->set_charset("utf8");
Die letzte Anweisung ist sehr ratsam und lässt die Datenbank Text in
der UTF8-Kodierung zu speichern. Natürlich muss dazu Text aus einem
HTML-Formular, der über PHP in der Datenbank gespeichert werden
soll, dieselbe Kodierung haben, d.h. die Webseiten zur Eingabe sollten
entsprechend das <meta>-Element
<meta charset="utf-8"/>;
enthalten.
Um Fehler beim Zugriff ausgeben zu lassen, was gerade bei der Programmierung eines Datenbankzugriffs sehr hilfreich sein kann, sollte
man nach Erzeugung des mysqli-Objekts und vor Einstellung der Kodierung das Attribut $mysqli->connect_errno verwenden:
$server
= "localhost";
$user
= "user";
$password = "passwort";
$database = "datenbank";
$mysqli
= new mysqli($server, $user, $password, $database);
if ($mysqli->connect_errno) {
echo "Fehler beim Zugriff auf MySQL: (" .
$mysqli->connect_errno . ") " . $mysqli->connect_error;
}
$mysqli->set_charset("utf8");
Das öffentliche Attribut $mysqli->connect_errno gibt die Fehlernummer
der aktuellen Datenbankverbindung an und ist null, wenn der Zugriff
erfolgreich war; in der if-Anweisung wird dies von PHP als false betrachtet und die echo-Anweisung daher nicht ausgeführt. Ist jedoch ein
Fehler aufgetreten, so ergibt in PHP die if-Anweisung true und das Attribut $mysqli->connect_error enthält eine Beschreibung des eingetretenen Fehlers. (Eine solche Fehlerausgabe sollte man bei produktiv eingesetzten und veröffentlichten Systemen selbstverständlich deaktivieren, denn ein gutwilliger Anwender kann damit nichts anfangen, und
6
1.1
Zugriff von PHP auf MySQL
ein böswilliger Hacker kann wertvolle Informationen erlangen.) Weitere Hinweise und Informationen finden Sie auf php.net unter dem Stichwort mysqli.1
Eine weitere Empfehlung ist, die Konfigurationsdaten des Zugriffs
auf den Datenbankserver in eine eigene Datei auszulagern, z.B.
db_login.inc.php, und zu Beginn eines PHP-Skripts per require_once
einzulesen. So können mehrere PHP-Skripte die Einstellungen zentral
einlesen und bei etwaigen Änderungen der Zugriffsparameter die aktuellen Werte erhalten, ohne dass sie selbst geändert werden müssen.
Da meist verschiedene PHP-Skripte eines Webauftritts zwar auf denselben Datenbankserver zugreifen sollen, aber auf verschiedene Datenbanken, sollte in der Zugriffsdatei zentral eine Funktion definiert werden, die den Namen der Datenbank als Parameter erfordert und das
mysqli-Objekt der Datenbankverbindung zurück gibt:
1
<!-- Dateiname: db_login.inc.php -->
2
<?php
3
/** Erstellt eine Datenbankverbindung mit zentralen
4
*
Konfigurationsdaten des DB-Servers.
5
*
@param $database die Datenbank
6
*
@return mysqli-Objekt der Datenbankverbindung
7
*/
8
function login($database) {
$server
= "localhost";
10
$user
= "user";
11
$password = "passwort";
9
12
13
$mysqli
= new mysqli($server,$user,$password,$database);
14
if ($mysqli->connect_errno) {
echo "Keine DB-Verbindung: ({$mysqli->connect_errno}) "
15
. $mysqli->connect_error;
16
exit();
17
// beendet das Programm
}
18
19
20
$mysqli->set_charset("utf8");
21
return $mysqli;
22
}
23
?>
Diese Funktion kann dann von einem beliebigen PHP-Skript importiert
werden, um eine Datenbankverbindung zu erstellen:
require_once("db_login.inc.php");
$mysqli = login("AdressDB");
Danach ist bei erfolgreichem Zugriff auf den Datenbankserver und die
Datenbank ein mysqli-Objekt über die Variable $mysqli verfügbar. Bei
1
http://php.net/manual/de/mysqli.quickstart.connections.php [2016-11-11]
7
1
Datenbankzugriffe übers Web: HTML & PHP & SQL
einem Verbindungsfehler allerdings wird hier wegen der Funktion exit
das Skript sofort abgebrochen.
1.1.2
Abfragen mit $mysqli->query
Mit der Objektmethode $mysqli->query wird ein SQL-Kommando an die
Datenbank geschickt und dort ausgeführt. Die Methode erwartet als
Eingabeparameter das SQL-Kommando, das vom Datenbank-System
ausgeführt werden soll, in Form eines Strings. Ein Beispiel für das Abschicken einer SELECT-Anweisung:
$result = $mysqli->query("SELECT * FROM adressen");
Der SQL-Befehl selektiert alle Datensätze der Tabelle adressen. Natürlich ist der SQL-Befehl für PHP lediglich ein String, also ohne weitere
Bedeutung. Ähnlich wie PHP ein HTML-Dokument auch nur als einen
String aufbaut und per echo an den Browser schickt, muss ein SQLBefehl als String an die Datenbank geschickt werden. Genau das macht
$mysql->query: es schickt die Abfrage an die Datenbank, die sie dort als
SQL interpretiert und ausführt. Die Ergebnismenge (result set) wird üblicherweise in einer Variablen $result gespeichert.
Eine weitere Variante, die oft in der Literatur oder in Programmbeispielen zu sehen ist, ist die Methode mysqli_query() nach dem „prozeduralen Stil“. Sie hat als obligatorischen Paraeter eine gültige Datenbankverbindung und erlaubt damit den Zugriff auf mehrere Datenbanken innerhalb eines PHP-Skripts, ohne sie immer wieder neu
aufbauen zu müssen. Weitere Informationen dazu siehe unter http:
//php.net/manual/en/mysqli.query.php.
1.1.3
Übernahme der Daten nach PHP
Es gibt mehrere Objektmethoden der Ergebnismenge $result einer erfolgreichen Auswahlabfrage einer Datenbank, um auf deren Datensätze
zuzugreifen. Die wichtigsten sind:
$result->fetch_object()
und
$result->fetch_assoc()
Die erste Methode liefert die Ergebnismenge der Datenbankabfrage als
Objekt zurück, die zweite als assoziatives Array. Hierbei wird der jeweils nächste Datensatz der Ergebnismenge $result gelesen und in einem Objekt mit den Spalten als Attributnamen bzw. einem assoziativen
Array mit den Bezeichnungen der Tabellenspalten als Schlüssel gespeichert.
Die beiden Methoden sind hierbei als Alternativen anzusehen. Ihre Wirkung ist fast gleich und am besten anhand eines einfachen Beispiels zu
8
1.1
Zugriff von PHP auf MySQL
erklären: Hat man nach einer Datenbankabfrage eine Ergebnismenge
$result mit der Spalte spalte und dem Eintrag $eintrag, so erzeugt die
Anweisung $result->fetch_object() je Datensatz ein Objekt
$wert = $a->spalte
Entsprechend erzeugt die Anweisung $result->fetch_assoc() je Datensatz ein assoziatives Array
$wert = $a["spalte"]
Wir werden im Folgenden oft die früher weit verbreitete Variante mit
assoziativen Arrays verwenden, auch wenn die objektorientierte Variante eleganter und beim Programmieren leichter hinzuschreiben ist.
Eine weitere Möglichkeit der Konvertierung der Ergebnismenge einer Auswahlabfrage der Datenbank ist mit der Anweisung
$result->fetch_row()
$result->fetch_row()
gegeben, die ein numerisches Array liefert, in dem jedoch die Spaltennamen der Datenbanktabelle nicht mehr verfügbar sind. Der Zugriff auf
die Datenbankinhalte geschieht hier über Indizes 0, 1, . . . . Die assoziative Variante bzw. die fetch_object-Methode sind flexibler, da sie auch
nach späteren eventuellem Einfügen neuer Felder in die Datenbanktabelle korrekte Resultate liefern.
Um nach einem SELECT-Befehl die Ergebnismenge zu durchlaufen, kann
man eine der folgenden Schleifenkonstrukte verwenden:
while ($datensatz = $result->fetch_object()) {
$datensatz->spalte ... // $datensatz ist ein Objekt
}
oder
while ($datensatz = $result->fetch_assoc()) {
$datensatz["spalte"] ... // $datensatz ist ein assoz. Array
}
oder
while ($datensatz = $result->fetch_row()) {
$datensatz[0] ... // $datensatz ist ein numerisches Array
}
Bei allen Varianten wird jeweils der nächste Datensatz in der Ergebnismenge $result als Objekt bzw. Array eingelesen, solange es einen solchen gibt. Nach dem letzten Datensatz liefert die Zuweisung ein leeres
Ergebnis, d.h. sie ist false, und die Schleife wird beendet. Ist die Ergebnismenge leer oder enthält sie eine Fehlermeldung, so wird die Schleife
gar nicht erst begonnen.
9
1
Datenbankzugriffe übers Web: HTML & PHP & SQL
1.2
Anzeigen von Daten im Browser
Das folgende Programmbeispiel erweitert das obige Adressbeispiel, indem es die verfügbaren Adressen aus einer Datenbank liest und anzeigt. Voraussetzung ist, dass bereits eine Datenbank mit einigen Einträgen existiert, siehe Abb. 1.3. Um den Inhalt der Datenbank als Tabelle
Abb. 1.3: Die Struktur der Adressdatenbank (links) und einige Einträge (rechts), mit
phpMyAdmin betrachtet.
im Browser darzustellen, muss also mit einem SELECT-Befehl als SQLStatement auf die Datenbank zugegriffen werden, die Ergebnismenge
mit $mysqli->fetch_assoc als Array in PHP transformiert und in HTML
als Tabelle erzeugt werden.
Wir werden zwei Versionen eines PHP-Skripts betrachten. Die erste
zeigt den Datenbankinhalt einfach zeilenweise im Browser an und erläutert konkret die notwendigen Programmzeilen eines allgemeinen
PHP-Skripts zur Datenanzeige. Das zweite wird dann eine formatierte
Anzeige in einer Tabelle vornehmen und die Feldnamen als Spaltenbezeichnung verwenden. Ferner wird die foreach-Schleife eingesetzt, um
die Datensatzfelder automatisch zu durchlaufen.
Listing 1: Einträge einer Datenbank anzeigen
1
<!-- Adressen aus DB. Dateiname: adresseDB.php -->
2
<?php
3
$mysqli = new mysqli("localhost", "user", "c5dDC8VJemscKPQS",
4
if ($mysqli->connect_errno) {
"AdressDB");
echo "Failed to connect to MySQL: ({$mysqli->connect_errno
5
}) $mysqli->connect_error";
6
}
7
$result = $mysqli->query("SELECT * FROM adressen");
while ($adresse = $result->fetch_assoc()) {
8
echo "$adresse[name], $adresse[vorname], $adresse[email]<
9
br/>";
10
11
10
}
?>
1.2
1.2.1
Anzeigen von Daten im Browser
Die wesentlichen Schritte des PHP-Skripts
Schauen wir uns die wesentlichen Programmschritte an, um den aktuellen Inhalt einer Datenbank im Browser darzustellen. Zunächst wird
die Verbindung zur Datenbank AdressDB auf dem Datenbankserver
localhost erstellt. Die if-Abfrage bewirkt hier, dass im Falle eines Fehlers bei der Verbindungserstellung eine Fehlermeldung erscheint. Als
nächstes wird eine SQL-Anweisung, hier ein SELECT, an den Datenbankserver geschickt und das das Ergebnis in der Variable $result gespeichert. Somit enthält $result die vollständige Ergebnismenge der SQLAnweisung. Die Ergebnismenge kann je nach Abfrage und Datenbank
tausende von Einträgen enthalten, oder vielleicht leer sein, oder aber
einen Fehlercode liefern. Die für die Anzeige der Datensätze aus der
Datenbank nächste wesentliche Anweisung ist die folgende Schleife:
while ( $adresse = mysql_fetch_assoc($result) ) {
echo "$adresse[Name] ... <br/>";
}
Die while-Schleife wiederholt sich hier, solange noch ein nächster Datensatz in der Ergebnismenge $result existiert. Falls nicht, so gibt die
Anweisung in der while-Klammer nämlich false zurück, und der Schleifenrumpf wird nicht weiter ausgeführt.
Während eines Schleifendurchlaufs steht also in der Variablen $adresse
ein bestimmter Datensatz aus der Datenbank als assoziatives Array, mit
den Feldnamen der Datenbanktabelle als Schlüssel und den Datenbankeinträgen des Datensatzes als Inhalte.
1.2.2
Formatierte Ausgabe
Zum Anzeigen der Daten reichen die obigen Anweisungen völlig aus.
Beispiel 1.1 Formatierte Ausgabe von Adressen aus Datenbank
1
<!-- Adressen aus DB. Dateiname: adresseDB1.php -->
2
<?php
3
♦
$mysqli = new mysqli("localhost", "user", "c5dDC8VJemscKPQS",
"AdressDB");
4
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: ({$mysqli->connect_errno
5
}) $mysqli->connect_error";
6
7
}
?>
8
9
10
11
12
<h2>Adressen als 2-dimensionales Array</h2>
<table border="1">
<?php
$result = $mysqli->query("SELECT * FROM adressen");
11
1
Datenbankzugriffe übers Web: HTML & PHP & SQL
13
14
// Tabellenkopf mit den Feldnamen als Spaltenbezeichnungen:
15
echo "
16
while ( $field = $result->fetch_field() ) {
<tr>\n";
echo "
17
18
}
19
echo "
<th>$field->name</th>\n";
</tr>\n";
20
// Tabelleneinträge aus der Datenbank, spezielle Ausgabe für
21
email und web:
while ( $adresse = $result->fetch_object() ) {
22
23
echo "
24
foreach ( $adresse as $key => $value ) {
<tr>\n";
if ( $key === "email" ) {
25
echo "
26
<td><a href=\"mailto:$value\">$value</a></
td>\n";
} else if ( $key === "web" ) {
27
echo "
28
<td><a href=\"http://$value\">$value</a></
td>\n";
} else {
29
echo "
30
}
31
32
}
33
echo "
36
</tr>\n";
}
34
35
<td>$value</td> \n";
?>
</table>
Im Unterschied zum vorigen Beispiel werden hier die Datensätze in einer Tabelle ausgegeben. Die foreach-Schleife läuft das Array sukzessive durch und ermöglicht den Zugriff auf die Inhalte über die Variable
$value. In dem Beispiel wird der Wert von $value in ein <td>-Tag von
HTML gepackt, so dass jeder Datensatz in <tr>-Elementen steht und so
als eine Tabellenzeile im Browser angezeigt wird.
Ermittlung der Feldnamen aus der Datenbank
Möchte man zusätzlich, so wie in diesem Beispiel, die Feldnamen der
Datenbank als Spaltenbezeichner anzeigen, so muss man irgendwie an
die Bezeichnungen der Felder aus der Datenbank gelangen. Die Information ist aber bereits in der Ergebnismenge einer erfolgreich abgeschlossenen SELECT-Anweisung enthalten, in unserem Falle also in der
Variablen $result. Der eleganteste Weg, an diese Information zu kommen, ist mit Hilfe der Funktion $result->fetch_field(). Sie liefert ein
Objekt zurück, dessen Attribute die Metadaten des jeweils nächsten
Feldes der Datenbanktabelle(n) ist, wie z.B. Feldname oder Feldtyp. So
12
1.3
Informationen über gerade abgesetzte SQL-Befehle
kann man sich also mit der Anweisung
while ($spaltenname = $result->fetch_field()->name) {
echo "$spaltenname<br/>";
}
die Feldnamen anzeigen lassen.
1.3
Informationen über gerade abgesetzte SQL-Befehle
In diesem Abschnitt werden zwei sehr nützliche öffentliche Attribute
des mysqli-Objekts bzw. der Ergebnismenge vorgestellt, die Informationen über den jeweils letzten abgesetzte SQL-Befehl speichern.
•
speichert die Anzahl der durch die jeweils letzte SQL-Anfrage an den Server betroffenen Datensätze.
Beispielsweise findet man wie folgt die Anzahl gelöschter Datensätze heraus:
$mysqli->affected_rows
$mysqli->query("DELETE FROM mytable WHERE id < 10");
$anzahl = $mysqli->affected_rows;
echo "$anzahl Datensätze gelöscht";
Die prozedurale Variante davon, mysqli_affected_rows($link),
ist eine Funktion und benötigt als Parameter die Verbindungskennung $link; Näheres dazu siehe unter
http://php.net/manual/de/mysqli.affected-rows.php.
•
speichert die Anzahl der Datensätze einer Ergebnismenge $result. Dieses Attribut ist nur gültig nach einem
SELECT-Befehl.
$result->num_rows
$result = $mysqli->query("SELECT * FROM table1");
$num_rows = $result->num_rows;
echo "$num_rows Zeilen\n";
Direkt nach einer
SELECT-Anfrage
mit
$mysqli->query()
ergibt
$result->num_rows dasselbe wie $mysqli->affected_rows.
1.4
SQL Injection entschärfen mit escape_string
Ein Sicherheitsrisiko jeder Webdatenbank ist die „SQL-Injektion“, also der Versuch eines Angreifers, über eine Formulareingabe die Datenbank abzufragen oder gar zu manipulieren. Ein Schutz vor solchen Angriffen ist nicht trivial, allerdings bietet PHP mit der Funktion escape_string einen einfachen Abwehrmechanismus an, siehe http:
//php.net/manual/en/mysqli.real-escape-string.php; die Funktion ist
ein Alias der Funktion real_escape_string. Sie durchläuft den übergebenen String und ersetzt sicherheitskritische Zeichen, die zu einer SQLInjektion führen könnten. Man sollte generell alle Eingaben analog folgendem Mechanismus entschärfen:
13
1
Datenbankzugriffe übers Web: HTML & PHP & SQL
$eingabe = $mysqli->escape_string($_POST["eingabe"]);
// irgendeine SQL-Anweisung:
$sql = "SELECT * FROM tabelle WHERE spalte=’$eingabe’";
$result = $mysqli->query($sql);
1.5
// zur Datenbank senden
Zusammenfassung
• Ein netzwerkbasiertes System, das Datenbankzugriffe über das
Internet ermöglicht, kann mit HTML, PHP und SQL programmiert
werden.
• Insgesamt stellt PHP eine Schnittstelle zwischen Browser (WebClient) und einer MySQL Datenbank (DB-Server) dar.
• Merke: In einem PHP-Skript kann weder HTML- noch SQL-Code
verarbeitet werden. Beides muss als String erzeugt werden und
entweder per echo an den Browser oder per $mysqli->query an die
Datenbank geschickt werden.
• Die Funktionen zur Durchführung der drei wesentlichen Schritte
zum Datenzugriff auf eine Datenbank via PHP sind:
1.
$mysqli = new mysqli(...)
— erstellt die Verbindung zur
Datenbank auf einem Datenbankserver
2.
$mysqli->query(...)
3.
$result->fetch_object(), $result->fetch_assoc(), $result->
— sendet SQL-Befehle zur Datenbank
(SELECT, INSERT, UPDATE, DELETE)
— liest die Ergebnismenge einer SQL-Abfrage
als Objekt bzw. als assoziatives oder numerisches Array in
PHP ein
fetch_row()
• Der Zugriff auf eine Datenbank sollte der Übersichtlichkeit und
Wartbarkeit halber mit Variablen parametrisiert werden:
$server
= "localhost";
$user
= "user";
$password = "passwort";
$database = "datenbank";
$mysqli
= new mysqli($server, $user, $password,
$database);
$mysqli->set_charset(’utf8’);
14
Zudem sollten diese Anweisungen in eine eigene Datei ausgelagert werden, so dass mehrere PHP-Skripte die Einstellungen einlesen können.
• Zum Anzeigen von Informationen aus einer Datenbanktabelle benötigt man mindestens die zwei Schritte
$result = $mysqli->query("SELECT * FROM Tabelle");
zur Selektion der Daten und die Schleife
1
while ( $eintrag = $result->fetch_assoc() ) {
foreach ($eintrag as $value) {
2
echo "$value<br/>";
3
}
4
5
2
}
Datenbankinteraktionen mit dem Browser
Üblicherweise besteht eine Webanwendung aus einer Interaktion mit
einer Datenbank, also aus enem komplexen Ablauf von Aus- und Eingabeaktionen, die jeweils von den vorherigen Aktionen abhängen. Ablauf und Logik aus Sicht des Anwenders müssen aufgrund der starren Request-Response-Architektur des Webs in dem PHP-Skript umgeschrieben werden. Die Schwierigkeit von PHP liegt abstrakt gesprochen
darin, dass PHP nur auf einen einkommenden Request reagieren kann,
aber keinen permanenten Prozess darstellt, der den Ablauf begleitet.
Ein PHP-Skript muss also den aktuellen Zustand des Ablaufs jedesmal
von Neuem feststellen. Oft ist dazu die Information notwendig, welchen
Button der Anwender eigentlich gedrückt hat. Mit dieser Problematik
beschäftigen wir uns in diesem Kapitel.
Wir möchten dazu im Folgenden ein Skript schreiben, das nicht nur das
Ansehen der Daten in der Adressdatenbank ermöglicht, sondern auch
das Eintragen neuer Daten über den Browser. Grob soll dabei folgender
Ablauf stattfinden:
1. Beim ersten Aufruf soll der aktuelle Inhalt der Datenbank gezeigt
werden. Allerdings soll es nun einen Submit-Button geben, siehe
Abb. 2.4 (links).
2. Anklicken dieses Buttons ruft dasselbe Skript auf, das nun aber
ein Eingabeformular zeigt, in das die neuen Daten eingetragen
werden können, siehe Abb. 2.4 (rechts).
3. Das Anklicken des Submit-Buttons soll erneut das Skript aufrufen, um nun die Formulardaten in die Datenbank zu speichern.
15
2
Datenbankinteraktionen mit dem Browser
Abb. 2.4: Die Seite beim Aufruf und beim Drücken des Eintragen-Buttons.
2.1
Eintragen-Skript mit unformatierten Ausgaben
Wir werden zunächst ein PHP-Skript betrachten, das zwar alle geforderten Funktionalitäten liefert, aber die Browserausgaben nicht formatiert.
Beispiel 2.1 Adressen aus Datenbank, mit Einfügemöglichkeit
1
<!-- Adressen in DB eintragen. Dateiname: adresseDB2.php -->
2
<form action="<?php echo $_SERVER[’PHP_SELF’]?>" method="POST">
3
<?php
4
$mysqli = new mysqli("localhost", "user", "c5dDC8VJemscKPQS",
"AdressDB");
5
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: ({$mysqli->connect_errno
6
}) $mysqli->connect_error";
7
}
8
$mysqli->set_charset("utf8");
9
10
if (isset($_POST[’eintragen’])) { // -- Eingabeform und
Speichern-Button erstellen -
11
16
♦
?>
12
Vorname: <input type="text" name="vorname"/><br/>
13
Name:
<input type="text" name="name"/><br/>
14
PLZ:
<input type="text" name="plz" size="7"/>
15
Ort:
<input type="text" name="ort"/> <br/>
16
Strasse: <input type="text" name="strasse"/><br/>
17
Web:
<input type="text" name="web"/><br/>
18
e-mail:
<input type="text" name="email"/><br/>
19
<input type="submit" name="speichern" value="Speichern"/>
2.1
20
Eintragen-Skript mit unformatierten Ausgaben
<?php
} else {
21
if (isset($_POST[’speichern’])) { // -- Formulardaten
22
speichern -= $mysqli->escape_string($_POST["name"]);
23
$name
24
$vorname = $mysqli->escape_string($_POST["vorname"]);
25
$plz
= $mysqli->escape_string($_POST["plz"]);
26
$ort
= $mysqli->escape_string($_POST["ort"]);
27
$strasse = $mysqli->escape_string($_POST["strasse"]);
28
$web
= $mysqli->escape_string($_POST["web"]);
29
$email
= $mysqli->escape_string($_POST["email"]);
30
31
$sql
= "INSERT INTO adressen ";
32
$sql .= "(name, vorname, plz, ort, strasse, web, email)
";
33
$sql .= " VALUES ";
34
$sql .= "(’$name’,’$vorname’,’$plz’,’$ort’,’$strasse’,’
$web’,’$email’)";
$mysqli->query($sql);
35
// an Datenbank abschicken
} // -- Ende speichern ------------------------------
36
37
//auflisten + Eintragen-Button:
38
39
$sql = "SELECT * FROM adressen ORDER BY Name";
40
$result = $mysqli->query($sql);
41
while ($adresse = $result->fetch_assoc()) {
42
echo "$adresse[name], $adresse[vorname], $adresse[email
43
]<br/>";
}
44
45
?>
<input type="submit" name="eintragen" value="Adresse
46
eintragen"/>
47
<?php
} //-- Ende else ------------------------------------
48
49
?>
50
</form>
51
</body>
52
</html>
Zunächst fällt auf, dass das gesamte Skript durch ein Form-Tag umschlossen wird, so dass es sich beim Submit selbst wieder aufruft.
2.1.1
Der Ablauf des Skripts
Der eigentliche Ablauf des Skripts aus Sicht des Benutzers wird durch
die if-Anweisungen gesteuert. Sie haben die folgende Struktur:
17
2
Datenbankinteraktionen mit dem Browser
1
if (isset($_POST[’eintragen’])) {
... Eingabeformular und Speichern-Button erstellen ...
2
3
} else {
if (isset($_POST[’speichern’])) {
4
... Eingabedaten in DB speichern ...
5
6
}
7
... DB-Inhalt auflisten und Eintragen-Button erstellen
...
8
}
Wie läuft das Skript also aus Benutzersicht ab? Ruft er es das erste
Mal auf, so ist sind $_POST["eintragen"] und $_POST["speichern"] undefiniert. In dem Skript werden also nur die nach dem inneren if-Zweig
ausgeführt, so dass der Benutzer den aktuellen Datenbankinhalt und
den Eintragen-Button sieht.
Klickt er nun auf den Eintragen-Button, so wird das Skript erneut
aufgerufen, doch diesmal ist der Inhalt von $_POST["eintragen"] definiert (wesentlich ist hierbei, dass der Eintragen-Button auch den Namen eintragen hat!). Somit werden die Anweisungen des äußeren ifZweigs ausgeführt und das Eingabeformular für die Daten sowie der
Speichern-Button angezeigt.
Schickt der Benutzer nun dieses Formular ab, so wird das Skript ein
drittes Mal aufgerufen, diesmal ist jedoch $_POST mit den Eingabedaten
gefüllt und $_POST["speichern"] ist nun ebenfalls definiert. Daher wird
der innere if-Zweig ausgeführt, der Benutzer sieht den aktuellen Stand
der Datenbank.
2.1.2
Das Eingabeformular
Das Eingabeformular wird aufgebaut, wenn zuvor der Submit-Button
namens eintragen gedrückt worden ist. Jedes Input-Element hat als
Namen den entsprechenden Feldnamen aus der Datenbanktabelle. Damit hat man stets eine direkt verständliche Verbindung zwischen den
Feldern und den Textfeldern und erleichtert so die Programmierung
und spätere Verwaltung aufgrund etwaiger Änderungen der Datenbankstruktur.
Wesentlich für den korrekten Ablauf des Skripts ist, dass der SubmitButton dieses Eingabeformulars speichern heißt, denn die steuernden
if-Anweisungen benötigen zum korrekten Ablauf des Skripts diesen
Namen.
2.1.3
Das Abspeichern der Daten
Zum Abspeichern der Daten aus dem Eingabeformular muss ein INSERTBefehl in SQL auf die Datenbank abgesetzt werden. (Hier erkennt man
einen der Vorteile, wenn man die Input-Elemente des Formulars gleich
18
2.2
Eintragen-Skript mit Funktionen und Formatierungen
nennt wie die Felder der Tabelle!) Ein häufig gemachter Fehler ist das
Fehlen der Apostrophs bei der Wertzuweisung.
Der SQL-Befehl wird zunächst in eine String-Variable $sql gespeichert.
(Zu beachten sind dabei die Leerzeichen bei Zeilenumbrüchen!). Das
hat einerseits den Vorteil, dass man den relativ langen Befehl übersichtlich aufbauen kann. Andererseits kann man im Fehlerfall leicht
den SQL-Befehl anzeigen („debuggen“) lassen, um zu prüfen, ob es sich
um einen SQL-Fehler handelt. Debugging ist das zeilenweise Verfolgen der Werte eines Programms während der Laufzeit. Es gibt dafür,
ähnlich wie für andere Programmiersprachen, einige ausgereifte Tools,
jedoch in so einfachen Umfeldern wie hier reicht auch ein einfaches
Ausgeben der Werte. Damit kann man sich also in unserem Falle während des Skriptablaufs anzeigen lassen, welches SQL-Statement genau
abgesetzt worden ist. Oft kann man auf diese Weise Syntaxfehler in
SQL schnell entdecken, insbesondere das Vergessen der Apostrophs (’).
Empfehlenswert ist in diesem Zusammenhang auch das Debuggen mit
etwaigen Fehlerausgaben der Funktion mysqli_error nach einer SQLAnfrage:
$result = $mysqli->query($sql);
if ($mysqli->error) {
echo "SQL error: " . $mysqli->error;
}
Mit der if-Bedingung wird die echo-Anweisung nur im Fehlerfall aufgerufen.
Wie auch immer, am Ende muss ein SQL-Befehl natürlich auch mit
$mysqli->query an die Datenbank gesendet werden. Genau dieser Fehler
wird erfahrungsgemäß oft gemacht, dass eine geniale SQL-Anweisung
erzeugt wird, sie aber nicht abgesetzt wird. Denken Sie immer an Abbildung 1.2, PHP ist nur das Medium!
2.1.4
Die Anzeige
Die Anzeige des aktuellen Inhalts der Datenbank wird angezeigt durch
eine SELECT-Anweisung. Sie lautet in unserem Beispiel 2.1
SELECT * FROM adressen ORDER BY Name
und liefert eine nach Namen sortierte Ergebnismenge $result.
2.2
Eintragen-Skript mit Funktionen und Formatierungen
Mit Funktionen kann man das eigentliche PHP-Skript, im folgenden
auch „Hauptskript“ genannt, relativ kurz gestalten.
Beispiel 2.2 Adressen aus Datenbank, mit Einfügemöglichkeit
♦
19
2
Datenbankinteraktionen mit dem Browser
1
<!-- Adressen in DB eintragen. Dateiname: adresseDB3.php -->
2
<form action="<?php echo $_SERVER[’PHP_SELF’]?>" method="POST">
3
<?php
4
require("./adressDBFunktionen.inc.php");
5
6
$mysqli = new mysqli("localhost", "user", "c5dDC8VJemscKPQS",
"AdressDB");
7
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: ({$mysqli->connect_errno
8
}) $mysqli->connect_error";
9
10
}
$mysqli->set_charset("utf8");
11
12
if (isset($_POST[’eintragen’])) {
eintragen($mysqli);
13
14
} else {
if (isset($_POST[’speichern’])) {
15
speichern($mysqli, "adressen", $_POST);
16
17
}
18
auflisten($mysqli);
19
}
20
?>
21
</form>
Im Wesentlichen wird also ein Formular erzeugt und nach dem Aufbau der Datenbankverbindung je nach Inhalt der $_POST-Variablen eine
der Funktionen auflisten, eintragen oder speichern aufgerufen. Diese
Funktionen sind ausgelagert in der Datei
adressDBFunktionen.inc.php
in der gleichen Verzeichnisebene wie das PHP-Skript, zu erkennen am
Pfad des require-Befehls. Bevor wir uns diese näher anschauen, betrachten wir den Ablauf des Skripts eingehender.
Betrachten wir nun die aufgerufenen Funktionen etwas näher.
Beispiel 2.3 Funktionen der Adressdatenbank
1
<!-- Funktionen der Adress-DB. Dateiname: adressDBFunktionen.
2
<?php
inc.php //->
3
4
5
function auflisten($mysqli) {
?>
<h2>Adressen als 2-dimensionales Array</h2>
<table border="1">
6
7
20
<?php
8
$sql = "SELECT * FROM adressen ORDER BY Name";
9
$result = $mysqli->query($sql);
♦
2.2
Eintragen-Skript mit Funktionen und Formatierungen
10
11
// Tabellenkopf mit den Feldnamen als Spaltenbezeichnungen:
12
echo "
13
while ( $attribut = $result->fetch_field() ) {
<tr>\n";
echo "
14
15
}
16
echo "
<th>$attribut->name</th>\n";
</tr>\n";
17
// Tabelleneinträge aus der Datenbank, spezielle Ausgabe
18
für email und web:
while ( $adresse = $result->fetch_assoc() ) {
19
20
echo "
21
foreach ( $adresse as $key => $value ) {
<tr>\n";
if ( $key == "email" ) {
22
echo "
23
<td><a href=\"mailto:$value\">$value</a></
td>\n";
} else if ( $key == "web" ) {
24
echo "
25
<td><a href=\"http://$value\">$value</a></
td>\n";
} else {
26
echo "
27
}
28
29
}
30
echo "
</tr>\n";
}
31
32
<td>$value</td> \n";
?>
33
</table>
34
<p style="text-align:center">
<input type="submit" name="eintragen" value="Adresse
35
eintragen"/>
</p>
36
37
<?php
38
}
// Ende function auflisten
39
40
function eintragen($mysqli) {
41
?>
42
43
<table border="0">
<tr><td>Vorname: </td><td><input type="text" name="
vorname"/></td></tr>
44
<tr><td>Name: </td><td><input type="text" name="name"
45
<tr>
/></td></tr>
46
<td>PLZ/Ort: </td>
47
<td>
48
<input type="text" name="PLZ" size="7"/>
49
<input type="text" name="Ort"/>
21
2
Datenbankinteraktionen mit dem Browser
</td>
50
51
</tr>
52
<tr><td>Strasse: </td><td><input type="text" name="
strasse"/></td></tr>
<tr><td>Web: </td><td><input type="text" name="web"/></
53
td></tr>
<tr><td>e-mail: </td><td><input type="text" name="email"
54
/></td></tr>
55
</table>
56
<p><input type="submit" name="speichern" value="Speichern"
/></p>
57
58
<?php
} // Ende function eintragen
59
60
function speichern($mysqli, $tabelle, $daten) {
61
$spalten = array();
62
$werte = array();
63
foreach ( $daten as $key => $value ) {
if ( $key != "speichern" ) {
64
65
$spalten[] = $key;
66
$werte[] = "’" . $mysqli->escape_string($value) . "’"
;
}
67
68
}
69
$sql
70
$sql .= "(" . implode(",", $spalten ) . ")";
71
$sql .= " VALUES ";
72
$sql .= "(" . implode(",", $werte )
73
$mysqli->query($sql);
74
75
= "INSERT INTO $tabelle ";
. ")";
}
?>
Die Funktion auflisten entspricht im Wesentlichen dem Beispielskript
1. Einzige Änderungen sind das SELECT-Statement, das jetzt
SELECT * FROM adressen ORDER BY Name
lautet und eine nach Namen sortierte Ergebnismenge $result liefert.
Außerdem ist nun ein Submit-Button namens eintragen eingefügt, der
an sich nichts anderes bewirkt, als dass das Hauptskript wieder aufgerufen wird. Die $_POST-Variable enthält nun als einziges Datenfeld
$_POST[eintragen], ist also nicht mehr leer.
Damit wird nun in dem Skript der zweite if-Zweig durchlaufen, d.h. die
Funktion eintragen aufgerufen. Diese Funktion baut nun ein einfaches
Eingabeformular auf, in dem die Adressdaten eingegeben werden können. Der Submit-Button heißt hier nun speichern, und damit wird nach
Anklicken das Hauptskript wieder aufgerufen.
22
2.3
Zusammenfassung
Diesmal wird der dritte if-Zweig durchlaufen und die Funktion
speichern aufgerufen. Diese Funktion übernimmt die Dateninhalte
der $_POST-Variablen des Eingabeformulars, um daraus ein INSERTStatement zu bilden. Mit $mysqli->query wird dieser SQL-Befehl an die
Datenbank geschickt, in der dann der Datensatz gespeichert wird. Diese Variante bildet zwei Arrays, je eins mit den Spaltennamen und eins
mit den Inhalten, und verwendet den implode-Befehl, um daraus die
Klammerausdrücke für ein INSERT zu erzeugen. Nachdem dieser INSERTBefehl als String gebildet worden ist, wird er mit $mysqli->query an die
Datenbank geschickt, dort von dem Datenbanksystem als SQL interpretiert und ausgeführt (wenn denn kein Syntaxfehler in SQL vorliegt ...).
Danach wird direkt die Funktion auflisten aufgerufen, die die nun aktualisierte Tabelle an den Browser schickt. Violà!
2.3
Zusammenfassung
• Der Ablauf eines Skripts, das sich mehrmals selbst aufruft, wird
mit if-Anweisungen gesteuert, die den Inhalt der Submit-Buttons
prüfen. Wesentlich ist also in diesem Fall, dass jeder SubmitButton einen eigenen Namen trägt.
• Der zeitliche Ablauf des Skripts aus Sicht des Benutzers entspricht nicht der Struktur der if-Anweisungen, denn zuerst werden spezielle Belegungen der Submit-Buttons abgefragt, dann
erst kommt der allgemeine Teil.
• Die Anzeige des aktuellen Datenbankinhalts sollte erst am Ende
des Skripts erfolgen, nachdem etwaige Änderungen in der Datenbank erfolgt sind. Würde man in Beispiel 2.1 die Anzeige der Datenbankinhalte vor der Speicherung der Eingabedaten programmieren, wäre zwar der neue Eintrag korrekt in der Datenbank gespeichert, der Anwender sähe aber irreführenderweise noch den
alten Stand der Datenbank.
• Man sollte die Namen der <input>-Tags in einem Eingabeformular
genauso nennen wie die entsprechenden Feldnamen der Datenbanktabelle. Das erleichtert einerseits die Programmübersicht,
andererseits die Synchronisation und Verwaltung bei Änderungen der Datenbankstruktur.
• Erstellt man ein SQL-Statement als einen String (z.B. namens
$sql), so muss er irgendwann mit $mysqli->query an die Datenbank gesendet werden. ;-)
23
3
Übungsaufgaben
3
Übungsaufgaben
Aufgabe 3.1 (Anmeldetracking) (a) Erstellen Sie mit phpmyadmin eine
Datenbank aufgabe_3_1 mit einer Tabelle, die folgende Spalten enthält:
Spaltenname
anmeldename
ip
anzahl
Datenformat
Standardwert
VARCHAR (10)
VARCHAR (39)
INT
1
Der Anmeldename soll hier der Primärschlüssel sein. Tragen Sie,
ebenfalls mit phpmyadmin, zwei Datensätze ein. (Die Spalte ip soll
hierbei eine IP-Adresse nach IPv4 oder IPv6 darstellen, also z.B.
„193.174.68.9“.)
(b) Schreiben Sie ein PHP-Skript, das bei jedem Aufruf die Daten der
Datenbankeinträge auflistet sowie ein Textfeld für einen Anmeldenamen und einen Button „Anmelden“ anzeigt. Durch Anklicken des Buttons soll das Skript wieder aufgerufen werden und der übermittelte Anmeldename in die Datenbank eingetragen werden. Beim Eintragen soll
die in dem PHP-Array $_SERVER["REMOTE_ADDR"] enthaltene IP-Adresse
des Browsers gespeichert werden.
(c) Erweitern Sie das Skript, so dass für den Fall, dass der Anmeldename
in der Datenbank bereits vorhanden ist, sein Betrag im Feld anzahl um
eins erhöht wird.
Programmiertipp. Speichern Sie in PHP erzeugte SQL-Anweisungen in
einer eigenen Variablen und geben Sie sie zunächst per echo als Text
aus und testen sie per SQL-Eingabe in phpmyadmin. Auf diese Weise
können Sie mögliche SQL-Fehler leichter erkennen.
Aufgabe 3.2 (Pizzabestellung) Programmieren Sie ein PHP-Skript, das
das untenstehende Formular anzeigt und die eingegebenen Daten in
eine Datenbanktabelle schreibt. Erstellen Sie in der Datenbank neben
einer Tabelle für die Kundenanmeldung lediglich eine weitere für die
Bestellungen, verwenden Sie passend VARCHAR, ENUM, SET, INT und TEXT
als Datentypen für die Tabelle und definieren Sie einen sinnvollen Primärschlüssel.
24
Aufgabe 3.3 (Werbebanner) (a) Sie arbeiten bei einem Portalanbieter.
Die Geschäfte laufen gut und die Anzahl der zur Verfügung stehenden
Werbeflächen ist erschöpft. Ihr Chef kommt auf die Idee, eine Werbefläche, d.h. eine bestimmte Position der Webseite zu geringeren Preisen
mehrfach zu vermieten.
Dazu werden Sie beauftragt, ein geeignetes PHP-Skript zu programmieren. Dabei muss sichergestellt sein, dass während eines Zyklusses (Periode der Seitenaufrufe = Anzahl der Werbebanner) alle Banner gleich
häufig angezeigt werden. Weiterhin sollen alle entscheidenden Daten
aus einer Datenbank kommen, das Skript soll also bei neuen oder gekündigten Bannern nicht modifiziert, sondern es soll nur der Datenbankinhalt per phpMyAdmin angepasst werden.
(b) Das Konzept ist so erfolgreich, dass mehrere Positionen nun mehrfach vermietet werden sollen. Wie muss die Datenbank und das Skript
hierfür angepasst werden?
Aufgabe 3.4 (a) Programmieren Sie für den Administrator einer Adressdatenbank eine Bedienoberfläche für den Browser. Die Anwendung soll
folgende Funktionalitäten und Eigenschaften haben:
• In der Datenbank werden Name, Vorname und Ort gespeichert
(wenn Sie wollen, auch Straße, Stadt, Email, Telefon, Handy, . . . ).
• Um die Verwaltung nutzen zu können, muss man sich anmelden. Die Passwörter der Anwender sollen in der Datenbank mit
SHA-1 gehasht gespeichert werden, verwenden Sie zum Speichern der initialen Nutzerdaten die Hilfsfunktion unter http:
//haegar.fh-swf.de/sha1.php.
• Der Admin soll Datensätze anlegen, ändern und löschen können.
Erstellen Sie dazu ein PHP-Skript, das die Bearbeitung der Datenbanktabelle gemäß folgender Abbildung ermöglicht.
(b) Programmieren Sie ein zweites Skript, mit dem sich ein User anmelden und seine eigenen Daten editieren kann (d.h. ändern und löschen).
Zudem soll er durch Eingabe von Vornamen oder Nachname nach den
dazu verfügbaren Einträgen suchen und sich die vollständigen Daten
dazu anzeigen lassen können.
25
3
Übungsaufgaben
(c) Sofern der Client-Rechner Cookies zulässt, kann man auf Wunsch
permanent angemeldet bleiben.
26
4
Lösungen der Aufgaben
Aufgabe 3.1 Die Datenbank hat die folgende Struktur:
1 -- phpMyAdmin SQL Dump
2 -- version 4.6.1
3 -- http://www.phpmyadmin.net
4 -5 -- Host: localhost
6 -- Erstellungszeit: 13. Nov 2016 um 14:56
7 -- Server-Version: 5.7.16-0ubuntu0.16.04.1
8 -- PHP-Version: 7.0.9-1+deb.sury.org trusty+1
9
10 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
11 SET time_zone = "+00:00";
12
13 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
14 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
15 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
16 /*!40101 SET NAMES utf8mb4 */;
17
18 -19 -- Datenbank: ‘aufgabe_3_1‘
20 -21 -- ------------------------------------22 -23 -- Tabellenstruktur für Tabelle ‘besucher‘
24 -25 CREATE TABLE ‘besucher‘ (
26
‘anmeldename‘ varchar(10) NOT NULL,
27
‘ip‘ varchar(15) NOT NULL,
28
‘anzahl‘ int(11) NOT NULL DEFAULT ’1’
29 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
30 -31 -- Daten für Tabelle ‘besucher‘
32 -33 INSERT INTO ‘besucher‘ (‘anmeldename‘, ‘ip‘) VALUES
34 (’anna’, ’192.168.0.100’),
35 (’hugo’, ’localhost’),
36 (’otto’, ’194.94.2.20’);
37 -38 -- Indizes für die Tabelle ‘besucher‘
39 -40 ALTER TABLE ‘besucher‘
41
ADD PRIMARY KEY (‘anmeldename‘);
42 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
43 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
44 /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Das PHP-Skript kann nach den Aufgabenstellungen (a) bis (c) lauten:
1 <!DOCTYPE html>
2 <html lang="de-DE">
3 <head>
4
<meta charset="UTF-8">
5
<title>Anmeldetracking</title>
6 </head>
7 <body>
27
4
Lösungen der Aufgaben
8 <form action="<?php echo $_SERVER[’PHP_SELF’];?>" method="POST">
9
10
<input type="text" name="anmeldename" placeholder="Anmeldename"/>
<input type="submit" value="Anmelden" />
11 </form>
12
13 <?php
14 $server
= "localhost";
15 $user
= "user";
16 $password = "passwort";
17 $database = "aufgabe_3_1";
18
19 $mysqli
= new mysqli($server, $user, $password, $database, 3306);
20 $mysqli->set_charset("utf8");
21
22 if (isset($_POST["anmeldename"])) {
23
24
$sql = "SELECT COUNT(*) FROM besucher
WHERE anmeldename = ’$_POST[anmeldename]’";
25
//echo $sql; exit();
26
$result = $mysqli->query($sql);
27
if ($result->fetch_assoc()["COUNT(*)"] === "0") { // Anmeldename nicht da
28
$sql = "INSERT INTO besucher
29
(anmeldename, ip)
30
VALUES
(’$_POST[anmeldename]’, ’$_SERVER[REMOTE_ADDR]’)";
31
32
//echo $sql; exit();
33
34
$result = $mysqli->query($sql);
} else { // Anmeldename vorhanden
35
$sql = "UPDATE besucher SET anzahl = anzahl + 1
WHERE anmeldename = ’$_POST[anmeldename]’";
36
37
//echo $sql; exit();
38
39
$result = $mysqli->query($sql);
}
40 }
41
42 // Auflisten der DB-Einträge:
43 echo "<h3>Eingetragene Anmeldenamen:</h3><ol>";
44 $sql = "SELECT * FROM besucher";
45 //echo $sql; exit();
46 $result = $mysqli->query($sql);
47 while ($eintrag = $result->fetch_assoc()) {
48
echo "<li>" .
49
$eintrag["anmeldename"] . ": " .
50
$eintrag["anzahl"] . " Besuche, " .
51
"IP: " . $eintrag["ip"] .
52
"</li>";
53 }
54 ?>
55 </body>
56 </html>
Beachten Sie dabei die auskommentierten Anweisungen echo $sql;
exit(); in den Zeilen 25, 32, 37 und 45, die zum Debuggen der jeweiligen SQL-Anweisungen eingesetzt werden können, um sie in phpmyadmin direkt zu testen. Die Funktion exit bricht das Skript sofort ab.
Aufgabe 3.2 Das Formular lässt sich wie folgt implementieren:
28
1 <!DOCTYPE html>
2 <html lang="de-DE">
3 <head>
4
<meta charset="UTF-8"/>
5
<title>Pizzabestellung</title>
6
<style>
7
.spalte1
8
.spalte1u {width: 15ex; margin: 1.5ex; margin-bottom: -0.75ex;}
9
10
11
.spalte2
{width: 15ex; margin: 1.5ex;}
{margin: 1.5ex;}
.spalte2u {margin: 1.5ex; margin-bottom: -0.75ex;}
</style>
12 </head>
13 <body>
14 <form action="./bestellen.php" method="POST">
15
<div style="display:flex;">
16
<div class="spalte1">Pizza</div>
17
<div class="spalte2">
18
<select name="pizza">
19
<option>Tonno</option>
20
<option>Salami</option>
21
<option>Prosciuto</option>
22
23
24
<option>Funghi</option>
</select>
</div>
25
</div>
26
<div style="display:flex;">
27
<div class="spalte1">Größe</div>
28
<div class="spalte2">
29
<input type="radio" name="groesse" value="groß" checked="checked"/>
30
groß<br/>
31
<input type="radio" name="groesse" value="mittel"/>
32
mittel<br/>
33
<input type="radio" name="groesse" value="klein"/>
34
klein
35
</div>
36
</div>
37
<div style="display:flex;">
38
<div class="spalte1">Extra Beilagen</div>
39
<div class="spalte2">
40
<input type="checkbox" name="zutaten[]" value="Käse"/>
41
Käse<br>
42
<input type="checkbox" name="zutaten[]" value="Zwiebeln"/>
43
Zwiebeln<br>
44
<input type="checkbox" name="zutaten[]" value="Tomaten"/>
45
Tomaten
46
</div>
47
</div>
48
<div style="display:flex;">
49
50
<div class="spalte1">Bemerkungen</div>
<div class="spalte2"><textarea name="bemerkung" rows="5"></textarea></div>
51
</div>
52
<div style="margin-top: 1.5ex;">
53
54
55
56
<div style="display:flex;">
<div class="spalte1u">Anmeldename</div>
<div class="spalte2u"><input type="text" name="anmeldename"></div>
</div>
29
4
Lösungen der Aufgaben
57
<div style="display:flex;">
58
<div class="spalte1u">Passwort</div>
59
60
<div class="spalte2u"><input type="password" name="passwort"></div>
</div>
61
</div>
62
<br/>
63
<div class="spalte1">
64
65
<input type="submit" name="submit" value="Bestellen"/>
</div>
66 </form>
67 </body>
68 </html>
Entsprechend lautet das PHP-Skript bestellen.php im selben Verzeichnis:
1 <?php
2 $server
= "localhost";
3 $user
= "root";
4 $password = "fbtbw1234";
5 $database = "pizzeria";
6
7 $mysqli
= new mysqli($server, $user, $password, $database);
8 $mysqli->set_charset(’utf8’);
9
10 if (isset($_POST["anmeldename"])) {
11
12
$sql = "SELECT COUNT(*) FROM kunden
WHERE anmeldename = ’$_POST[anmeldename]’
AND passwort = ’$_POST[passwort]’";
13
14
#echo $sql;
15
$result = $mysqli->query($sql);
16
if ($treffer = $result->fetch_assoc()) {
17
if ($treffer["COUNT(*)"] === "0") {
18
echo "Log-in gescheitert! (Anwendername oder Passwort falsch)";
19
exit();
20
}
21
if ($treffer["COUNT(*)"] !== "1") {
22
echo "SQL-Injection: " . $treffer["COUNT(*)"] . " gleiche Anwender!";
23
exit();
24
25
}
} else {
26
echo "Log-in gescheitert: Datenbankfehler: Wir arbeiten dran ...";
27
28
exit();
}
29
30
// Ab hier geht es nur nach einem erfolgreichen Log-in weiter ...
31
$sql
= "INSERT INTO ‘bestellungen‘
32
(‘kunde‘, ‘pizza‘, ‘groesse‘, ‘beilagen‘, ‘bemerkung‘)
33
VALUES (’$_POST[anmeldename]’, ’$_POST[pizza]’, ’$_POST[groesse]’,
’";
34
for ($i = 0; $i < sizeof($_POST["beilagen"]); $i++) {
35
$sql .= $_POST["beilagen"][$i];
36
if ($i < sizeof($_POST["beilagen"]) - 1) {
37
$sql .= ",";
38
30
}
39
}
40
$sql .= "’, ’$_POST[bemerkung]’)";
41
#echo "$sql";
42
$result = $mysqli->query($sql);
43
if ($result) {
44
echo "Ihre Bestellung wurde erfolgreich gespeichert:";
45
echo "<pre>"; print_r($_POST); echo "</pre>";
46
} else {
47
echo "Bei Ihrer Bestellung lief etwas schief! Wir arbeiten dran ...";
48
}
49 }
50 ?>
1 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
2 SET time_zone = "+00:00";
3
4
5 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
6 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
7 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
8 /*!40101 SET NAMES utf8mb4 */;
9
10 -11 -- Datenbank: ‘pizzeria‘
12 -13
14 -- ------------------------------------15
16 -17 -- Tabellenstruktur für Tabelle ‘bestellungen‘
18 -19
20 CREATE TABLE ‘bestellungen‘ (
21
‘bestellid‘ int(11) NOT NULL,
22
‘kunde‘ varchar(10) NOT NULL COMMENT ’Referenzschlüssel’,
23
‘pizza‘ enum(’Tonno’,’Salami’,’Prosciuto’,’Funghi’) NOT NULL,
24
‘groesse‘ enum(’groß’,’mittel’,’klein’) NOT NULL,
25
‘beilagen‘ set(’Käse’,’Zwiebeln’,’Tomaten’) NOT NULL,
26
‘bemerkung‘ text NOT NULL
27 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
28
29 -- ------------------------------------30
31 -32 -- Tabellenstruktur für Tabelle ‘kunden‘
33 -34
35 CREATE TABLE ‘kunden‘ (
36
‘anmeldename‘ varchar(10) NOT NULL,
37
‘passwort‘ varchar(20) NOT NULL
38 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
39
40 -41 -- Daten für Tabelle ‘kunden‘
42 -43
44 INSERT INTO ‘kunden‘ (‘anmeldename‘, ‘passwort‘) VALUES
45 (’anna’, ’123’),
46 (’otto’, ’abc’);
31
4
Lösungen der Aufgaben
47
48 -49 -- Indizes der exportierten Tabellen
50 -51
52 -53 -- Indizes für die Tabelle ‘bestellungen‘
54 -55 ALTER TABLE ‘bestellungen‘
56
ADD PRIMARY KEY (‘bestellid‘);
57
58 -59 -- Indizes für die Tabelle ‘kunden‘
60 -61 ALTER TABLE ‘kunden‘
62
ADD PRIMARY KEY (‘anmeldename‘);
63
64 -65 -- AUTO_INCREMENT für exportierte Tabellen
66 -67
68 -69 -- AUTO_INCREMENT für Tabelle ‘bestellungen‘
70 -71 ALTER TABLE ‘bestellungen‘
72
MODIFY ‘bestellid‘ int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10;
73 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
74 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
75 /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Aufgabe 3.3 Für beide Aufgabenteile (a) und (b) wird auf dieselbe Datenbank zugegriffen, die die folgende Struktur besitzt:
1 -- phpMyAdmin SQL Dump
2 -- version 4.5.1
3 -- http://www.phpmyadmin.net
4 -5 -- Host: 127.0.0.1
6 -- Erstellungszeit: 10. Nov 2016 um 14:42
7 -- Server-Version: 10.1.9-MariaDB
8 -- PHP-Version: 5.6.15
9
10 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
11 SET time_zone = "+00:00";
12
13 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
14 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
15 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
16 /*!40101 SET NAMES utf8mb4 */;
17
18 -19 -- Datenbank: ‘werbebanner‘
20 -21 -- ------------------------------------22 -23 -- Tabellenstruktur für Tabelle ‘aufgabe_3_2_a‘
24 --
32
25 CREATE TABLE ‘aufgabe_3_2_a‘ (
26
‘bannerID‘ int(10) UNSIGNED NOT NULL,
27
‘file‘ varchar(50) NOT NULL,
28
‘ranking‘ tinyint(3) UNSIGNED NOT NULL
29 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
30
31 -32 -- Daten für Tabelle ‘aufgabe_3_2_a‘
33 -34 INSERT INTO ‘aufgabe_3_2_a‘ (‘bannerID‘, ‘file‘, ‘ranking‘) VALUES
35 (1, ’schroeder.jpg’, 1),
36 (2, ’de-vries.jpg’, 2),
37 (3, ’erniebert.jpg’, 0);
38
39 -- ------------------------------------40
41 -42 -- Tabellenstruktur für Tabelle ‘aufgabe_3_2_b‘
43 -44 CREATE TABLE ‘aufgabe_3_2_b‘ (
45
‘bannerID‘ int(10) UNSIGNED NOT NULL,
46
‘file‘ varchar(50) NOT NULL,
47
‘ranking‘ tinyint(3) UNSIGNED NOT NULL,
48
‘position‘ tinyint(3) UNSIGNED NOT NULL
49 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
50
51 -52 -- Daten für Tabelle ‘aufgabe_3_2_b‘
53 -54 INSERT INTO ‘aufgabe_3_2_b‘ (‘bannerID‘, ‘file‘, ‘ranking‘, ‘position‘) VALUES
55 (1, ’schroeder.jpg’, 2, 0),
56 (2, ’de-vries.jpg’, 2, 0),
57 (3, ’erniebert.jpg’, 0, 0),
58 (4, ’bogart.jpg’, 3, 1),
59 (5, ’dean.jpg’, 0, 1),
60 (6, ’taylor.jpg’, 3, 1),
61 (7, ’monroe.jpg’, 1, 1);
62
63 -64 -- Indizes für die Tabelle ‘aufgabe_3_2_a‘
65 -66 ALTER TABLE ‘aufgabe_3_2_a‘
67
ADD PRIMARY KEY (‘bannerID‘);
68
69 -70 -- Indizes für die Tabelle ‘aufgabe_3_2_b‘
71 -72 ALTER TABLE ‘aufgabe_3_2_b‘
73
ADD PRIMARY KEY (‘bannerID‘);
74
75 -76 -- AUTO_INCREMENT für exportierte Tabellen
77 -78
79 -80 -- AUTO_INCREMENT für Tabelle ‘aufgabe_3_2_a‘
33
4
Lösungen der Aufgaben
81 -82 ALTER TABLE ‘aufgabe_3_2_a‘
83
MODIFY ‘bannerID‘ int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT
=4;
84 -85 -- AUTO_INCREMENT für Tabelle ‘aufgabe_3_2_b‘
86 -87 ALTER TABLE ‘aufgabe_3_2_b‘
88
MODIFY ‘bannerID‘ int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT
=8;
89 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
90 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
91 /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Die Namen der Bilddateien sind in den Zeilen 55 bis 61 angegeben und
müssen in einem Unterverzeichnis ./banner vorhanden sein.
(a) Das PHP-Skript lautet
1 <?php
2
//DB connect
3
$mysqli = new mysqli("localhost","user","password","werbebanner");
4
5
// select banner:
6
$sql = "SELECT bannerID, file FROM aufgabe_3_2_a
7
ORDER BY ranking DESC LIMIT 1";
8
$result = $mysqli->query($sql);
9
$banner = $result->fetch_assoc();
10
11
// increase / decrease ranking:
12
$sql = "UPDATE aufgabe_3_2_a SET ranking = ranking+1
13
14
WHERE bannerID <> $banner[bannerID]";
$mysqli->query($sql);
15
16
$sql = "UPDATE aufgabe_3_2_a SET ranking=0
17
18
WHERE bannerID = $banner[bannerID]";
$mysqli->query($sql);
19
20
// Show Banner
21
echo "<img src=\"./banner/".$banner[’file’]."\" />";
22 ?>
(b) Das PHP-Skript lautet
1 <?php
2
//DB connect
3
$mysqli = new mysqli("localhost","user","password","werbebanner");
4
5
// select banner:
6
for ($p = 0; $p < 2; $p++) { // enumerate positions $p
7
$sql = "SELECT bannerID, file FROM aufgabe_3_2_b
8
WHERE position = $p ORDER BY ranking DESC LIMIT 1";
9
$result = $mysqli->query($sql);
$banner[$p] = $result->fetch_assoc();
10
11
}
12
34
13
// increase / decrease ranking:
14
for ($p = 0; $p < 2; $p++) { // enumerate positions $p
$sql = "UPDATE aufgabe_3_2_b SET ranking = ranking + 1
15
16
WHERE position = $p AND bannerID <> " . $banner[$p]["bannerID"];
17
$mysqli->query($sql);
18
$sql = "UPDATE aufgabe_3_2_b SET ranking = 0
19
WHERE position = $p AND bannerID = ".$banner[$p]["bannerID"];
20
$mysqli->query($sql);
21
}
22
23
// Show Banner:
24
echo "<img src=\"./banner/" . $banner[0]["file"] . "\" />
25
<img src=\"./banner/" . $banner[1]["file"] . "\" />";
26 ?>
Aufgabe 3.4 (a) und (b) Die Datenbank
1 -- phpMyAdmin SQL Dump
2 -- version 4.6.1
3 -- http://www.phpmyadmin.net
4 -5 -- Host: localhost
6 -- Erstellungszeit: 29. Nov 2016 um 13:40
7 -- Server-Version: 5.7.16-0ubuntu0.16.04.1
8 -- PHP-Version: 7.0.9-1+deb.sury.org trusty+1
9
10 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
11 SET time_zone = "+00:00";
12
13 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
14 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
15 /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
16 /*!40101 SET NAMES utf8mb4 */;
17
18 -19 -- Datenbank: ‘AdressDB‘
20 -21 -- ------------------------------------22 -23 -- Tabellenstruktur für Tabelle ‘adressen‘
24 -25
26 CREATE TABLE ‘adressen‘ (
27
‘id‘ int(11) NOT NULL,
28
‘vorname‘ varchar(20) NOT NULL DEFAULT ’’,
29
‘name‘ varchar(20) NOT NULL DEFAULT ’’,
30
‘email‘ varchar(30) NOT NULL DEFAULT ’’,
31
‘web‘ varchar(50) NOT NULL DEFAULT ’’,
32
‘strasse‘ varchar(20) NOT NULL DEFAULT ’’,
33
‘plz‘ varchar(30) NOT NULL DEFAULT ’’,
34
‘ort‘ varchar(30) NOT NULL DEFAULT ’’,
35
‘nutzer‘ varchar(10) DEFAULT NULL COMMENT ’Fremdschlüssel’
36 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
37
38 -39 -- Daten für Tabelle ‘adressen‘
40 -41
42 INSERT INTO ‘adressen‘ (‘id‘, ‘vorname‘, ‘name‘, ‘email‘, ‘web‘, ‘strasse‘, ‘
plz‘, ‘ort‘, ‘nutzer‘) VALUES
35
4
Lösungen der Aufgaben
43 (1, ’Andreas’, ’de Vries’, ’[email protected]’, ’haegar.fh-swf.de/
deVries.html’, ’Haldener Straße 182’, ’D-58095’, ’Hagen’, ’’),
44 (2, ’Ingo’, ’Schröder’, ’[email protected]’, ’www3.fh-swf.de/fbtbw/
Schroeder.htm’, ’Haldener Straße 182’, ’D-58095’, ’Hagen’, ’’),
45 (4, ’Volker’, ’Weiß’, ’[email protected]’, ’www3.fh-swf.de/fbtbw/Weiss.
htm’, ’Haldener Straße 182’, ’D-58095’, ’Hagen’, ’’),
46 (5, ’Bernd’, ’Blümel’, ’[email protected]’, ’hochschule-bochum.de/fbw
/personen/bluemel.html’, ’Lennershofstraße 140’, ’D-44801’, ’Bochum’, ’’)
,
47 (7, ’Otto’, ’Klein’, ’’, ’’, ’’, ’’, ’Iserlohn’, ’otto’);
48
49 -- ------------------------------------50 -51 -- Tabellenstruktur für Tabelle ‘nutzer‘
52 -53
54 CREATE TABLE ‘nutzer‘ (
55
‘name‘ varchar(10) NOT NULL,
56
‘rolle‘ set(’admin’,’user’) NOT NULL DEFAULT ’user’,
57
‘passwort‘ varchar(40) NOT NULL COMMENT ’SHA-1 hashed’,
58
‘angemeldet‘ tinyint(1) NOT NULL DEFAULT ’0’,
59
‘seit‘ timestamp NULL DEFAULT CURRENT_TIMESTAMP
60 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
61
62 -63 -- Daten für Tabelle ‘nutzer‘
64 -65
66 INSERT INTO ‘nutzer‘ (‘name‘, ‘rolle‘, ‘passwort‘, ‘angemeldet‘, ‘seit‘)
VALUES
67 (’admin’, ’admin’, ’beccee1a5ed045c43b71ccbeb1bab5c632e2bd25’, 0, ’2016-11-29
12:29:17’),
68 (’devries’, ’admin’, ’beccee1a5ed045c43b71ccbeb1bab5c632e2bd25’, 1, ’
2016-11-29 11:13:38’),
69 (’otto’, ’user’, ’beccee1a5ed045c43b71ccbeb1bab5c632e2bd25’, 1, ’2016-11-29
12:37:35’);
70
71 -72 -- Indizes der exportierten Tabellen
73 -74
75 -76 -- Indizes für die Tabelle ‘adressen‘
77 -78 ALTER TABLE ‘adressen‘
79
ADD PRIMARY KEY (‘id‘);
80
81 -82 -- Indizes für die Tabelle ‘nutzer‘
83 -84 ALTER TABLE ‘nutzer‘
85
ADD PRIMARY KEY (‘name‘);
86
87 -88 -- AUTO_INCREMENT für exportierte Tabellen
89 --
36
90
91 -92 -- AUTO_INCREMENT für Tabelle ‘adressen‘
93 -94 ALTER TABLE ‘adressen‘
95
MODIFY ‘id‘ int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
96 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
97 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
98 /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Das Startskript ist die folgende Datei login.php:
1 <?php
2 require_once("../db_login.inc.php");
3 $mysqli = login("AdressDB");
4
5 if (isset($_POST["name"]) && isset($_POST["passwort"])) {
= $mysqli->escape_string($_POST["name"]);
6
$name
7
$passwort = sha1($_POST["passwort"]); // gehashtes Passwort
8
$sql = "SELECT COUNT(*) FROM nutzer
9
WHERE name
10
= ’$name’
AND passwort = ’$passwort’";
11
$result = $mysqli->query($sql);
12
if ($treffer = $result->fetch_assoc()) {
13
if ($treffer["COUNT(*)"] === "0") {
14
echo "Log-in gescheitert! (Anmeldename oder Passwort falsch)<br/>";
15
echo "Versuchen Sie es <a href=\"$_SERVER[PHP_SELF]\">noch mal!</a>";
16
exit();
17
}
18
if ($treffer["COUNT(*)"] !== "1") {
error_log("Möglicher Cyberangriff: "
19
20
21
. $treffer["COUNT(*)"] . " gleiche Anwender!"
. " user: " . $_POST["name"]
22
. " passwort: " . $_POST["passwort"]
23
);
24
exit();
25
26
}
} else {
27
28
echo "Log-in gescheitert: Datenbankfehler: Wir arbeiten dran ...";
}
29
30
// Log-in erfolgreich ...
31
$sql
32
$mysqli->query($sql);
33
header("Location: ./editieren.php?name=$name&pwd=$passwort");
34
exit();
= "UPDATE ‘nutzer‘ SET angemeldet = TRUE, seit = CURRENT_TIMESTAMP
WHERE name=’$name’";
35 }
36 if (isset($_POST["name"]) XOR isset($_POST["passwort"])) {
37
"Bitte Anmeldename und Passwort eingeben!";
38 }
39 ?>
40 <form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="POST">
41
<input type="text" name="name" />
42
<input type="password" name="passwort" />
43
<input type="submit" value="Anmelden" />
44 </form>
37
4
Lösungen der Aufgaben
Nach erfolgreichem Login werden Anmeldename und das geshashte
Passwort per URI und GET an das Skript editieren.php geschickt:
1 <!DOCTYPE html>
2 <html lang="de-DE">
3 <head>
4
<meta charset="UTF-8">
5
<title>Editierung einer Datenbank</title>
6
<style>
7
8
9
.idspalte {width: 5ex; margin: 0.1ex; border: 1px solid gray;}
.spalte
{width: 20ex; margin: 0.1ex; border: 1px solid gray;}
</style>
10 </head>
11 <body>
12 <h1></h1>
13 <?php
14 require_once("../db_login.inc.php");
15 $mysqli = login("AdressDB");
16
17 // Bei jedem Aufruf dieser Seite Log-in erneut prüfen:
18 $nutzer = ""; // Nutzer in Nutzertabelle
19 $rolle
= ""; // Nutzerrolle: admin dar alles editieren, user nur seine Daten
...
20
21 if (isset($_GET["name"]) && isset($_GET["pwd"])) {
22
$nutzer = $mysqli->escape_string($_GET["name"]);
23
$pwd
= $mysqli->escape_string($_GET["pwd"]);
24
25
$sql = "SELECT COUNT(*), rolle FROM nutzer WHERE name=’$nutzer’ AND
26
$result = $mysqli->query($sql);
27
$anzahl = 0;
28
if ($treffer = $result->fetch_row()) {
passwort=’$pwd’ AND angemeldet=1";
29
$anzahl = $treffer[0];
30
31
$rolle
= $treffer[1];
}
32
33
if ($anzahl != 1) { // Login fehlerhaft, Sitzung abbrechen
34
$sql = "UPDATE nutzer SET angemeldet=false WHERE name=’$name’";
35
$mysqli->query($sql);
36
header("Location: ./login.php");
37
38
exit();
}
39 } else { // Login fehlerhaft, Sitzung abbrechen
40
header("Location: ./login.php");
41
exit();
42 }
43
44 // Hier gelangt man nur mit gültigen Anmeldedaten hin!
45
46 // UPDATE:
47 if (isset($_POST["aendern"])) {
$id
49
$vorname = $mysqli->escape_string($_POST["vorname"]);
50
$name
= $mysqli->escape_string($_POST["name"]);
51
$ort
= $mysqli->escape_string($_POST["ort"]);
52
38
= $mysqli->escape_string($_POST["id"]);
48
53
$sql = "UPDATE adressen
54
SET vorname = ’$vorname’, name=’$name’, ort=’$ort’
55
WHERE id=$id";
56
if ($rolle != "admin") {
57
$sql .= " AND nutzer=’$nutzer’";
58
}
59
$mysqli->query($sql);
60 }
61 // DELETE:
62 if (isset($_POST["loeschen"])) {
= $mysqli->escape_string($_POST["id"]);
63
$id
64
$sql = "DELETE FROM adressen WHERE id=$id";
65
if ($rolle != "admin") {
66
$sql .= " AND nutzer=’$nutzer’";
67
}
68
$mysqli->query($sql);
69 }
70 // INSERT:
71 if (isset($_POST["hinzufuegen"])) {
72
$vorname = $mysqli->escape_string($_POST["vorname"]);
73
$name
= $mysqli->escape_string($_POST["name"]);
74
$ort
= $mysqli->escape_string($_POST["ort"]);
75
76
$sql = "INSERT INTO adressen
77
(vorname, name, ort, nutzer)
78
VALUES
79
(’$vorname’, ’$name’, ’$ort’, ’$nutzer’)";
80
$mysqli->query($sql);
81 }
82 // SELECT:
83 ?>
84 <div style="display:flex;">
85
<div class="idspalte" style="font-weight:bold;">ID</div>
86
<div class="spalte" style="font-weight:bold;">Vorname</div>
87
<div class="spalte" style="font-weight:bold;">Nachname</div>
88
<div class="spalte" style="font-weight:bold;">Ort</div>
89
<div class="spalte" style="font-weight:bold;"></div>
90 </div>
91 <?php
92 $sql = "SELECT id, vorname, name, ort FROM adressen";
93 if ($rolle != "admin") {
94
$sql .= " WHERE nutzer=’$nutzer’";
95 }
96 $result = $mysqli->query($sql);
97 while ($eintrag = $result->fetch_row()) {
98
echo "
99 <form action=\"$_SERVER[PHP_SELF]?name=$_GET[name]&pwd=$_GET[pwd]\" method=\"
POST\">
100 <div style=\"display:flex;\">
101
<div class=\"idspalte\">$eintrag[0]<input type=\"hidden\" name=\"id\" value
102
<div class=\"spalte\"><input type=\"text\" style=\"width:20ex;\" name=\"
103
<div class=\"spalte\"><input type=\"text\" style=\"width:20ex;\" name=\"name
104
<div class=\"spalte\"><input type=\"text\" style=\"width:20ex;\" name=\"ort
=\"$eintrag[0]\"/></div>
vorname\" value=\"$eintrag[1]\" /></div>
\" value=\"$eintrag[2]\" /></div>
39
4
Lösungen der Aufgaben
\" value=\"$eintrag[3]\" /></div>
105
<div class=\"spalte\">
106
<input type=\"submit\" name=\"aendern\"
107
<input type=\"submit\" name=\"loeschen\" value=\"Löschen\"/>
108
value=\"Ändern\"/>
</div>
109 </div>
110 </form>";
111 }
112 echo "
113 <form action=\"$_SERVER[PHP_SELF]?name=$_GET[name]&pwd=$_GET[pwd]\" method=\"
POST\">
114 <div style=\"display:flex;\">
115
<div class=\"idspalte\"></div>
116
<div class=\"spalte\"><input type=\"text\" style=\"width:20ex;\" name=\"
117
<div class=\"spalte\"><input type=\"text\" style=\"width:20ex;\" name=\"name
118
<div class=\"spalte\"><input type=\"text\" style=\"width:20ex;\" name=\"ort
119
<div class=\"spalte\">
vorname\" /></div>
\" /></div>
\" /></div>
120
121
<input type=\"submit\" name=\"hinzufuegen\" value=\"Hinzufügen\"/>
</div>
122 </div>
123 </form>";
124 ?>
(c) Bevor nach erfolgreichem Login das Editieren-Skript in Zeile 33 des
Skripts login.php aufgerufen wird, können zwei Cookies gesetzt werden, die den Anwendernamen und das gehashte Passwort speichern. Im
Editierskript können die beiden Cookies dann statt der GET-Parameter
abgefragt und verwendet werden.
40
Abbildungsverzeichnis
Internetquellen
[PHP]
[PS]
[W3C]
http://php.net/manual/de/ – PHP-Handbuch
– PHP Sandbox, Möglichkeit zum Testen von PHP-Quelltextfragmenten (Snippets). Die
Sandbox wird bereitgestellt von Jereon (John) Post, die Website
liefert Informationen und Tutorials über PHP.
http://sandbox.onlinephpfunctions.com
– World Wide Web Consortium, Gremium
zur Standardisierung der Web-Techniken; eine deutsche Übersetzung wichtiger Teile dieses Webauftritts findet man unter http:
//www.edition-w3c.de/, auf die auch das Deutsch-Österreichische
Büro W3C.DE/AT http://www.w3c.de/ bzw. http://www.w3c.at/
verlinkt.
http://www.w3c.org/
Abbildungsverzeichnis
1.1 Die drei Schichten einer datenbankbasierten Webanwendung . . . . .
1.2 PHP als Schnittstelle, die über reinen Text kommuniziert . . . . . . . .
1.3 Die Struktur der Adressdatenbank (links) und einige Einträge (rechts),
mit phpMyAdmin betrachtet. . . . . . . . . . . . . . . . . . . . . . . . .
2.4 Die Seite beim Aufruf und beim Drücken des Eintragen-Buttons. . . .
3
3
10
16
41
Herunterladen