1. Eigenes Gästebuch schreiben
Ein Gästebuch ist vergleichbar mit einem Newsskript nur dass die Beiträge von den Benutzern kommen, nicht vom Administrator der Seite. Jedoch liegen auch hier die Probleme denn die Benutzer können jeden Text eingeben den sie wollen. Dies kann dazu führen das in den Gästebucheinträgen Texte stehen die man lieber nicht haben möchte. Daher müssen alle Eingaben die vom Benutzer kommen geprüft werden. Des Weiteren muss man sich auch gegen Spam schützen.
2. Aufbaue der Tabelle in MySQL
Die Tabelle in MySQL wird einen vergleichbaren Aufbau wie die News-Tabelle haben. Neben den eigentlichen Text wird der Name und das Datum gespeichert. Daher wird die Tabelle 4 Spalten besitzen.
-
ID
- Die normale Identifikationsspalte vom TypINT
. -
Datum
- Der Zeitpunkt wann der Eintrag hinzugefügt wurde. Typ ist entsprechendDATETIME
. -
Autor
- Der Name desjenigen der den Eintrag geschrieben hat. EinVARCHAR(50)
wird wohl reichen. -
Inhalt
- Der eigentliche Text des Eintrags. Dieser wird entsprechend in einemTEXT
-Feld gespeichert.
Der entsprechende SQL-Query sieht wie folgt aus.
CREATE TABLE Guestbook ( ID INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, Autor VARCHAR(50) NOT NULL, Datum DATETIME NOT NULL, Inhalt TEXT NOT NULL );
Analog kann man auch über phpMyAdmin die Tabelle hinzufügen.
3. Aufbau des PHP-Skriptes
Das PHP-Skript vom Gästebuch muss zwei Aufgaben durchführen. Einmal muss es aus der
Datenbank die Datensätze auslesen. Des Weiteren muss es Daten aus einem Formular
verarbeiten und entsprechend neue Datensätze in die Datenbank eintragen. Dies
unterscheiden wir anhand der $_SERVER['REQUEST_METHOD']
-Variable.
Bei einem POST-Request versuchen wir die Daten aus dem
Formular in die Datenbank hinzuzufügen, bei allen anderen Requesttypen (insbesondere
GET-Requests) zeigen wir das Gästebuch und ein Formular für neue
Gästebucheinträge an.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
if ('POST' == $_SERVER['REQUEST_METHOD']) {
// Code zum hinzufügen in der DB
} else {
// Anzeigen von Gästebuchbeiträgen und dem Formular.
}
?>
Da wir natürlich mit MySQL arbeiten wollen bauen wir eine Verbindung zur Datenbank auf. Außerdem laden wir noch HTML-Header- und HTML-Footer-Code via include oder besser via readfile.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
$db = @new mysqli('localhost', 'username', 'password', 'database');
if (mysqli_connect_errno()) {
die('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')');
}
readfile('header.html'); // enthält auch das <body>-tag
if ('POST' == $_SERVER['REQUEST_METHOD']) {
// Code zum hinzufügen in der DB
} else {
// Anzeigen von Gästebuchbeiträgen und dem Formular.
}
readfile('footer.html');
?>
Nun können wir mit den Teilbereichen anfangen.
4. Code zum Anzeigen der Beiträge
Der PHP-Code zum Anzeigen der Beiträge ist vergleichbar mit dem Code aus dem Newsskript. Daher gibt es nicht viel zu erklären.
<?php
// [...]
} else {
$sql = 'SELECT
Datum,
Autor,
Inhalt
FROM
Guestbook
ORDER BY
Datum DESC';
$result = $db->query($sql);
if (!$result) {
die('Der Query konnte nicht ausgeführt werden: '.$db->error);
}
if ($result->num_rows) {
while ($row = $result->fetch_assoc()) {
echo '<div class="beitrag">'."\n";
echo ' <span class="autor">'.htmlspecialchars($row['Autor'])."</span>\n";
echo ' <span class="datum">'.$row['Datum']."</span>\n";
echo " <p>\n";
echo nl2br(htmlspecialchars(preg_replace('~\S{30}~', '\0 ', $row['Inhalt'])));
echo " </p>\n";
echo "</div>\n";
}
} else {
echo '<p class="info">Es sind keine Gästebucheinträge vorhanden</p>';
}
readfile('formular.html');
}
// [...]
?>
Die Funktion htmlspecialchars dient dazu besondere HTML-Zeichen wie
<
zu escapen damit sie als Text angezeigt werden und nicht
irgendwie unseren HTML-Code durcheinander bringen. Dies ist nötig da die Daten
von Benutzer kommen und die sehr kreativ sind wenn es darum geht ein Gästebuch mit
Müll zu füllen. Die Funktion nl2br fügt hinter jedem Zeilenumbruch im
Beitrag ein HTML-Zeilenumbruch <br />
ein. Dies ist nötig da
der Text in der Datenbank nur normale Zeilenumbrüche mittels \n
enthält. Der preg_replace-Ausdruck ist nicht einfach zu erklären
da er ein regulären Ausdruck enthält. Reguläre
Ausdrücke wird jedoch erst in einem späteren Kapitel durchgenommen.
Um es kurz zu machen: dieser preg_replace Ausdruck fügt in Wörter
die länger als 30 Zeichen sind hinter jedem 30. Zeichen ein Leerzeichen ein. Damit verhindern
wir dass Spaßvögel sowas wie AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
eingeben
und die Internetseite unnötig breit machen.
Die formular.html
-Datei enthält den Code für ein Formular damit die
Besucher etwas ins Gästebuch schreiben können.
<form action="guestbook.php" method="post"> <fieldset> <legend>Ins Gästebuch Eintragen</legend> <label>Name: <input type="text" name="Autor" /></label> <label>Text: <textarea name="Inhalt" rows="6" cols="40"></textarea></label> <label>{FRAGE}: <input type="text" name="Antwort"/></label> <input type="submit" name="formaction" value="Eintragen" /> </fieldset> </form>
Wie man sieht gehen wir davon aus dass das Gästebuch unter der Datei
guestbook.php
erreichbar ist. Der Bereich
{FRAGE}
dient dazu Spam im Gästebuch zu verhindern,
dazu später mehr.
Es ist klar dass das Gästebuch noch leer ist und wir beim testen nur die
Anzeige Es sind keine Gästebucheinträge vorhanden
bekommen.
Daher nun der Code zum hinzufügen von Beiträgen.
5. Code zum hinzufügen von neuen Beiträgen
Aus dem Formular kriegen wir die Felder Autor
, Inhalt
,
Antwort
und formaction
. Daher prüfen wir zuerst ob wir
diese Daten auch wirklich bekommen, denn es könnte sein dass ein Benutzer ein
eigenes Formular verwendet.
<?php
// [...]
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['Autor'], $_POST['Inhalt'], $_POST['Antwort'], $_POST['formaction'])) {
die ('Benutzen sie nur Formulare von der Homepage.');
}
} else {
// [...]
?>
Nun machen wir einfache Textabfragen indem wir gucken ob der Benutzer überhaupt
was eingegeben hat. Dazu verwenden wir einmal die Funktion trim um
Leerzeichen vor und nach der Eingabe zu löschen und vergleichen den Rückgabewert
mit ''
und wissen dann ob was eingegeben wurde oder nicht.
<?php
// [...]
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['Autor'], $_POST['Inhalt'], $_POST['Antwort'], $_POST['formaction'])) {
die ('Benutzen sie nur Formulare von der Homepage.');
}
if (('' == $autor = trim($_POST['Autor'])) or
('' == $inhalt = trim($_POST['Inhalt'])) or
('' == $antwort = trim($_POST['Antwort']))) {
die ('Bitte füllen sie das Formular vollständig aus.');
}
} else {
// [...]
?>
Nun prüfen wir das Antwort-Feld. Dabei erwarten wir eine bestimmte Antwort von dem
Benutzer. Der {FRAGE}
-Teil in dem Formular muss entsprechend geändert
werden. Dies ist vergleichbar mit einem
CAPTCHA jedoch viel
simpler. Anhand dieser Eingabe schließen wir Spam-Bots aus die Gästebücher mit
Müll zuspammen. Hier reicht es wenn man nach einen einzelnen Wort fragt, dass
der Benutzer eintragen soll.
<?php
// [...]
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['Autor'], $_POST['Inhalt'], $_POST['Antwort'], $_POST['formaction'])) {
die ('Benutzen sie nur Formulare von der Homepage.');
}
if (('' == $autor = trim($_POST['Autor'])) or
('' == $inhalt = trim($_POST['Inhalt'])) or
('' == $antwort = trim($_POST['Antwort']))) {
die ('Bitte füllen sie das Formular vollständig aus.');
}
if ('' != $antwort) { // entsprechend Anpassen, sowie den {FRAGE}-Teil im Formular
die ('Sie müssen die Frage richtig beantworten.');
}
} else {
// [...]
?>
Beachtet dass man den Besuchern nicht zu viel abverlangt, insbesondere Groß- und Kleinschreibungen. Für den Fall dass man dann doch Spam im Gästebuch hat hat man ganz andere Probleme als Spam im Gästebuch, denn dann hat jemand speziell ein Skript geschrieben um genau dieses Gästebuch zuzuspammen und dann ist es etwas persönliches gegen euch und hat nichts mehr mit dem Hintergrundrauschen von Spam im Internet zu tun.
Nun können wir die Daten in die Datenbank speichern. Am einfachsten wäre es
den INSERT-Befehl mit Stringverkettung aus Strings
und den Variablen zusammenzubauen. Dies wird problematisch da die Variablen
beliebigen Inhalt haben können, insbesondere '
-Zeichen die den
SQL-Query kaputt machen. Dies nennt man auch
SQL-Injection,
da die Besucher auch versuchen SQL-Befehle wie DROP TABLE Guestbook;
einzuschleusen. Daher muss man die Daten irgendwie verändern/schützen.
In der mysql-Extension wurde dies mit der
mysql_real_escape_string-Funktion gemacht.
<?php
$sql = 'INSERT INTO
Guestbook(Autor,Datum,Inhalt)
VALUES
("'.mysql_real_escape_string($autor).'",
NOW(),
"'.mysql_real_escape_string($inhalt).'");';
?>
In MySQLi verwenden wir dafür Prepared Statements.
Dies sind SQL-Queries die Platzhalter enthalten (einfache ?
-Zeichen).
Durch entsprechende Funktionen werden diese Platzhalter dann mit Werten gefüllt.
Diese Funktionen sorgen dann dafür dass die Inhalte entsprechend verarbeitet werden
damit sie keinen Unsinn anstellen und den SQL-Query nicht zerstören.
Um so ein prepared statement zu erstellen wird die Methode
prepare()
verwendet (mysqli_prepare). Dabei geben
?
-Zeichen die Platzhalter an, die wir dann später mit Daten füllen.
<?php
// [...]
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['Autor'], $_POST['Inhalt'], $_POST['Antwort'], $_POST['formaction'])) {
die ('Benutzen sie nur Formulare von der Homepage.');
}
if (('' == $autor = trim($_POST['Autor'])) or
('' == $inhalt = trim($_POST['Inhalt'])) or
('' == $antwort = trim($_POST['Antwort']))) {
die ('Bitte füllen sie das Formular vollständig aus.');
}
if ('' != $antwort) { // entsprechend Anpassen, sowie den {FRAGE}-Teil im Formular
die ('Sie müssen die Frage richtig beantworten.');
}
$sql = 'INSERT INTO
Guestbook(Autor, Datum, Inhalt)
VALUES
(?, NOW(), ?)';
$stmt = $db->prepare($sql);
if (!$stmt) {
die ('Es konnte kein SQL-Query vorbereitet werden: '.$db->error);
}
} else {
// [...]
?>
Da auch ein prepare()
fehlschlagen kann müssen wir ihn
auf Fehler überprüfen. Die Methode liefert wenn alles gut ist ein
MySQLi_STMT-Objekt zurück, mit denen wir dann arbeiten.
Für unsere Zwecke sind dies die bind_param()
- und
execute()
-Methoden.
<?php
// [...]
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['Autor'], $_POST['Inhalt'], $_POST['Antwort'], $_POST['formaction'])) {
die ('Benutzen sie nur Formulare von der Homepage.');
}
if (('' == $autor = trim($_POST['Autor'])) or
('' == $inhalt = trim($_POST['Inhalt'])) or
('' == $antwort = trim($_POST['Antwort']))) {
die ('Bitte füllen sie das Formular vollständig aus.');
}
if ('' != $antwort) { // entsprechend Anpassen, sowie den {FRAGE}-Teil im Formular
die ('Sie müssen die Frage richtig beantworten.');
}
$sql = 'INSERT INTO
Guestbook(Autor, Datum, Inhalt)
VALUES
(?, NOW(), ?)';
$stmt = $db->prepare($sql);
if (!$stmt) {
die ('Es konnte kein SQL-Query vorbereitet werden: '.$db->error);
}
$stmt->bind_param('ss', $autor, $inhalt);
if (!$stmt->execute()) {
die ('Query konnte nicht ausgeführt werden: '.$stmt->error);
}
echo '<p class="info">Gästebucheintrag hinzugefügt. <a href="guestbook.php">Zurück zum Gästebuch</a>.</p>';
} else {
// [...]
?>
Die zwei s
-Zeichen geben an das 2 Strings Variablen folgen (für die Spalten
Autor
und Inhalt
). Für die Methode bind_param()
existieren noch die Zeichen i
für Integerzahlen, d
für
Floatzahlen (double) und b
für Binärdaten. Wir werden Hauptsächlich
nur i
und s
verwenden. Danach wird mit der execute()
-Methode
der Query ausgeführt und dann geben wir nur noch ein Infotext aus. Da
auch die execute()
-Methode fehlschlagen kann wird hier der Rückgabewert
geprüft. Ist dieser false
wird entsprechend eine Fehlermeldung ausgegeben.
Beim Testen kann es passieren dass wir einen Webserver mit magic quotes haben. Da wir selber uns um das richtige escapen kümmern laden wir in unserem PHP-Skript den Code der die Backslashes von magic quotes löscht. Dies machen wir so weit oben wie möglich.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
include 'magic_quotes_remove_slashes.php'; // oder wie man seine Datei genannt hat
// oder den Quellcode direkt einfügen.
// [...]
?>
Somit ist unser Skript nun fertig.
6. Nachteile vom Gästebuch-Skript
Auch wenn es viele Angriff abwehrt ist es nicht total sicher. Insbesondere kann ein Benutzer mehrere Gästebucheinträge auf einmal hinzufügen. Dies kann man versuchen zu unterbinden wenn die IP-Adresse mitgespeichert wird und vorm Eintragen geprüft wird ob ein Datensatz bereits existiert oder nicht. Außerdem fehlt eine Blätterfunktion und alle Einträge werden auf einer Seite dargestellt.