Dieser Kurs ist eine Einführung in PHP und mySQL. Die nötigen Grundlagen werden anhand der Planung und Umsetzung eines Newssystem besprochen. So lernt man das nötige Handwerkzeug, um zukünftig eigene Projekte umzusetzen.
Grundlage für diesen Artikel ist Teil I dieser Artikelserie. Artikel I beschäftigt sich mit den Grundlagen zu PHP und MySQL. Hier geht es zum Artikel I >>> Im zweiten Teil dieses Kurses geht es darum eine Datenbank anzulegen, Nachrichten von User entgegenzunehmen, diese zu kontrollieren und sie letztendlich in der Datenbank zu speichern. Als erster Schritt wird eine Datenbank erstellt, die die eingegeben Daten aufnehmen kann. Hierzu kann man verschiedene Werkzeuge benutzen. Eines der besten Programme um eine mySQL-Datenbank zu bearbeiten ist PhpMyAdmin, welches auch in diesem Beispiel genutzt wird.
Was soll jetzt aber in der Datenbank abgelegt werden? Nachrichten bestehen in der Regel aus einer Titelzeile und einem Nachrichtentext. Wichtig wäre es auch zu wissen von wann die Nachricht ist und von wem. Wir benötigen also 4 Felder für die Datenbank. Vier? Nein wir nehmen fünf! Das erste Feld, und das sollte man sich immer angewöhnen, ist der Index. Hier wird einfach eine Zahl benutzt. Über diese Zahl kann später jeder Datensatz eindeutig identifiziert werden. Natürlich nur sofern keine Zahl doppelt vorkommt; dazu aber gleich mehr.
Zunächst legt man in mySQL eine Tabelle mit dem Namen tp_news an. Der zugehörige Befehl lautet CREATE TABLE 'tp_news' () TYPE=MyISAM;. Auf die Angabe des Types kann verzichtet werde. Hierzu später vielleicht mehr. Innerhalb der Klammern können jetzt noch Datenfelder für diese Tabelle definiert werden. Anders als beispielsweise eine Wordtabelle kennt mySQL verschiedene Datenfeldtypen. Das Feld welches den Index aufnimmt wird in diesem Beispiel 'id' genannt. Über den Begriff int für integer teilt man mySQL mit das eine Zahl als Inhalt vorgesehen ist. In diesem Feldtyp kann eine bis zu 11-stellige Zahl gespeichert werden. Das ist für das Newsscript zuviel und verbraucht unnötigen Speicherplatz. Mit int(6) begrenzen wir die Länge auf 6 Stellen, was auch für extrem viele News reichen sollte. Damit man bei der Eingabe der News nicht immer von Hand eine (noch freie) Zahl für den Index angeben muss, weißt man mySQL mit auto_increment an dieses Feld selber zu füllen. Die ganze Zeile für das Anlegen des ersten Datenbankfeldes lautet also: `id` int(11) NOT NULL auto_increment. NOT NULL wurde noch nicht erklärt: Bei allen Felder hat man die Möglichkeit NULL oder NOT NULL anzugeben. Ersteres bedeutet das das Feld leer sein kann, wobei leer nicht 0 oder ein Leerzeichen bedeutet, sondern wirklich leer - nichts, nada. Die zweite Möglichkeit verhindert das und zwingt mySQL zumindest etwas einzufügen. Für unsere Zwecke eigentlich egal, aber dennoch später einfacher mit NOT NULL.
Wichtig für den Index der Tabelle ist das jetzt Zahl nur einmal vorkommt und so jeder Datensatz eindeutig zu identifizieren ist. Daher wird die Zeile UNIQUE KEY `id` (`id`) eingefügt. Diese veranlasst, dass jeder Datensatz in dem Feld id einen eindeutigen, einmaligen Wert haben muss. Da sich später ein Großteil der Datenbankabfragen auf den Index, also das Feld id, beziehen, wird noch PRIMARY KEY (`id`) eingefügt, welches die Abfragen beschleunigt.
Das zweite Feld soll die Überschrift der Nachricht aufnehmen. Der Name wird auf 'titel' festgelegt. Als Art des Feldes wird tinytext genommen. Dieses Begriff steht für einen kleinen Text und ist platzsparender als die anderen Textfeldertypen: text oder longtext.
Für das dritte Feld, welches den Nachrichtentext ('nachricht') aufnehmen soll, wird der Feldtyp text benutzt.
Im vierten und fünften Feld ('startdatum' und 'startzeit') wird das Datum der Nachricht gespeichert. Man könnte diese Felder natürlich als Text definieren, aber mySQL bietet eine elegantere Lösung. Es gibt die Feldtypen date und time. Die Anzeige erfolgt im 'YYYY-MM-DD' bzw. 'HH:MM:SS'-Format. Mit dem Feldtyp datetime gibt es auch eine Kombination von beiden (YYYY-MM-DD HH:MM:SS) Aufgrund späterer Weiterentwicklungen werden die Felder hier aber getrennt. Für beide Felder ist ein default angegeben, dieser Wert wird in das Feld eingetragen wenn kein Wert explizit angegeben ist.
Als letztes Feld wird noch 'autor' als tinytext definiert.
Im Ganzen sieht die Befehls kette jetzt so aus: Codebox 2.1 :: Tabelle erstellen CREATE TABLE `tp_news` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`titel` tinytext NOT NULL,
`nachricht` text NOT NULL,
`startdatum` date NOT NULL DEFAULT '0000-00-00',
`startzeit` time NOT NULL DEFAULT '00:00:00',
`autor` tinytext NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) TYPE=MyISAM; Um die Befehlskette jetzt ausführen zu lassen gibt es viele Möglichkeiten. Hier wird eine komfortable im Form von phpmyadmin benutzt. Sollte phpmyadmin auf dem Webspace installiert sein, läßt es sich in der Regel über www.meinedomain.tld/phpMyAdmin/ aufrufen. (Im Zweifelsfall mal den Provider fragen.) Ich möchte hier nicht im Detail auf dieses Tool eingehen, aber wer sich etwas damit beschäftigt wird es lieben lernen.
Aber zurück zum Anlegen der Tabelle. Im Startbildschirm von phpmyadmin klickt man jetzt auf die Datenbank (links im Frame) die man benutzen möchte. Je nach Version findet man jetzt unter auf dieser Seite oder auf dem Dateireiter SQL eine Textarea mit der Überschrift: "SQL-Befehl(e) in Datenbank xxx ausführen". Das einzige was man noch machen muss ist die Codebox 2.1 per Copy & Paste einfügen und ok klicken. Im Anschluß sollte man die Tabelle im Frame links sehen. Fertig.
Für alle die nicht phpmyadmin benutzen können: Es geht auch mit jedem anderem Programm oder mit einem eigenen Script. Codebox 2.2 :: Tabelle mit PHP erstellen <?php
$query = "CREATE TABLE `tp_news` (
`id` int(11) NOT NULL auto_increment,
`titel` tinytext NOT NULL,
`nachricht` text NOT NULL,
`startdatum` date NOT NULL default `0000-00-00`,
`startzeit` time NOT NULL default `00:00:00`,
`autor` tinytext NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) TYPE=MyISAM";
$result=mysql_query($query);
?> Wichtig ist natürlich das die Datenbank wie im letzten Kurs gezeigt vorher geöffnet wird. Das komplette Script steht unter www.lauff.info/tp_php1.php zum Download bereit.
Nachdem die Grundüberlegungen zur Datenbank abgeschlossen sind, ist es an der Zeit über die Struktur des Scriptes nachzudenken. Klar könnte man einfach drauf los programmieren und würde dann wohl auch alles zusammenbekommen, aber wenn es an die Erweiterung oder Fehlerkorrektur geht hat man schlechte Karten, weil man sich in dem eigene Code nicht mehr zurecht findet.
Folgendes Szenario: Es besteht schon eine Internetseite und das Newsscript soll eingefügt werden. Die Änderungen die an der bestehenden Seite zu machen sind sollen so gering wie möglich sein. Ebenso sollten Script und Seite getrennt sein, d.h. die Dateien sollten nicht in einem Ordner sein. Also wird für dieses Script ein Unterordner mit dem Namen tpnews erstellt. Das Script selber hat zwei Bereiche. Einen öffentlichen, der für die Ausgabe zuständig ist und eine privaten, der für die Verwaltung zuständig ist. Zweiterer sollte Passwort geschützt sein. Um das zu realisieren erstellen wir einem Ordner in tpnews mit dem Namen tpadmin. Dieser Ordner wird zunächst mal, weil es einfacher ist, mit einer htaccess Datei geschützt. Später, im Rahmen der Mehruser-Funktionen, wird dann ein mit php geschützter Bereich erstellt.
Weiterhin wird noch der Ordner inc erstellt. PHP bietet die Möglichkeit sich wiederholende Codeteile auszulagern und dieser mit dem Befehl include oder require einzubinden. der inc-Ordner wird der zentrale Ort für alle diese Dateien. Zum Abschluss wird noch der Ordner tpl angelegt. In diesem Ordner werden im Laufe des Kurses alle von dem Leser gestalteten Vorlagen gespeichert. Im Ordner admin wird jetzt eine zentrale php-Datei mit dem Namen index.php angelegt, die alle Adminfunktionen aufruft. Der Inhalt der Datei steht in Codebox 2.3 und wird nachfolgend erläutert. Codebox 2.3 :: tpadmin/index.php <?php
// HTML-Kopf einbinden
include ("../tpl/admin_header.inc.php");
// Einlesen der übergebenen Variablen
$strAction=$_POST['strAction'];
$intStep=$_POST['intStep'];
// ******** Neue Nachricht
if ($strAction=="new") {
// eingeben
if ($intStep=="") {
include ("../inc/admin_newinput.inc.php");
}
// prüfen
elseif ($intStep=="1") {
include ("../inc/admin_newerror.inc.php");
}
// speichern
elseif ($intStep=="2") {
include ("../inc/admin_newsave.inc.php");
}
}
// ******** Auswahlmenu
else {
include ("../inc/admin_choise.inc.php");
}
// HTML-Fuss einbinden
include ("../tpl/admin_footer.inc.php");
?>Auf den ersten Blick scheint der Code vielleicht unübersichtlich, bringt aber vorteile mit sich. Die index-Datei ist nichts anderes als eine Kiste, die sich immer die richtigen Module läd (bzw. included). Jetzt aber rein in den Quelltext:
Als erstes eine Erklärung zu allen Zeilen die mit // beginnen oder in /* und */ eingeschlossen sind. Das Kommentare, die von PHP nicht beachet werden. Im Prinzip das ist /* Kommentar */ das gleiche wie in HTML <!-- Kommentar -->. Wie in allen anderen Programmiersprachen bietet PHP mit // die Möglichkeit ab diesen Zeichen den Rest der Zeile auszukommentieren. Einen Rat in diesem Zusammenhang möchte ich noch geben: Macht von dieser Möglichkeit gebrauch! Wie oft habe ich schon vor einem Quellcode gesessen, den ich selber geschrieben habe und ihn nicht mehr verstanden. Mit Kommentaren ist es viel einfacher, gerade als Einsteiger.
In der nächsten Zeile findet man den Befehl include. Über die Befehlskette include ("pfad/zu/einer/Datei.Endung"); kann man eine (fast) beliebige Datei in den Quellcode einbinden. Der Inhalt der Datei wird an dieser Stelle eingefügt als wenn er dort stehen würde. In diesem Beispiel wir die Datei ../tpl/admin_header.inc.php eingefügt. Der Pfad zeigt auf .. (= ein order nach oben) und tpl. Dort wird die Datei admin_header.inc.php angelegt. Codebox 2.4 :: tpl/admin_header.inc.php <?php echo "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?".">"; ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>TP-News Adminseite</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<style type="text/css">
label.left {
float : left;
text-align : right;
width : 10em;
margin-right : .3em;
}
input[type=text], select {
float : left;
}
input.right {
float : left;
width : auto;
clear : both;
margin-left : 5.3em;
margin-right : .3em;
height : 20px;
}
input[type=checkbox].right, input[type=radio].right, input[type=submit].right {
margin-left : 6.3em;
}
form br {
clear : both;
}
label,select,input[type=checkbox],input[type=radio],input[type=button],input[type=submit]{
cursor : pointer;
}
form {
line-height : 160%;
width : 98%;
}
</style>
</head>
<body>Wie unschwer zu erkennen ist handelt es sich um den Anfang (Header) einer normalen HTML Datei. Natürlich hätte man dieser Datei auch admin_header.htm nennen können, aber in PHP ist es üblich Dateien die included werden mit der Endung .inc zu versehen. Aber warum heißt diese Datei dann .inc.php? Leider sind die meisten Server so eingestellt, dass sie .inc-Dateien wie normale Textdateien anzeigen. In diesem Beispiel wäre es nicht schlimm, wenn man die Datei selber aufruft und den Inhalt angezeigt bekommt, anderes sähe das aber mit einer Datei passwoerter.inc aus. Das php verhindert einfach, dass die Datei einsehbar ist.
Wenn man jetzt wieder einen Blick auf die Datei tpadmin/index.php wirft fängt die Datei im Prinzip mit einen normalen HTML-Kopf an. Nur ist Sie a. übersichtlicher und b. kann man die tpl-Datei ändern, ohne in die Gefahr zu gelangen etwas am Quellcode es Programms zu ändern.
Genauso verhält es sich mit der letzen Zeile des Scripts (include ("../tpl/admin_footer.inc.php");) hier wird der Fuß des HTML-Dokuments eingebunden. Codebox 2.5 :: tpl/admin_footer.inc.php Natürlich könnte man die Dateien noch um ein Menü, eine Kopf- oder Fußzeile ergänzen, aber für unsere Absichten reicht das hier völlig.
Der zweite Kommentar in der Datei tpadmin/index.php weist und darauf hin das jetzt die POST-Variablen eingelesen werden. Neuerdings ist PHP um ein Sicherheitsaspekt erweitert wurden, der verhindern soll das Variablen von außen in das Script eingefügt werden können. Ich will an dieser Stelle nicht genauer darauf eingehen, aber man sollte immer allen Eingaben die von außen gemacht werden können misstrauen. Auch wenn noch nicht alle sich im Umlauf befindlichen PHP-Versionen an die neuen strikten Sicherheitsregeln halten, so sollte man Sie doch einhalten, damit die Scripte auch zukünftig ohne Probleme laufen.
Früher (gute alte Zeit ;)) standen die per GET oder POST gesendeten Variablen sofort in PHP zur Verfügung. Hatte eine Formular das Inputfeld 'name' gab es automatisch die Variable $name in PHP. Heute muss man die Daten erst einlesen. Das geschied mittels $_POST bei POST und $_GET bei GET. Mit $strAction=$_POST['strAction']; und $intStep=$_POST['intStep']; werden also die beiden POST-Felder strAction und intStep verfügbar gemacht. Logisch das beide zurzeit noch leer sind - was es mit denen auf sich hat erfahren wir gleich.
Zuvor will ich aber noch auf eine weitere Programmierart eingehen, die ich vor einiger Zeit das erste mal ausprobiert habe und die - wie ich finde - einem das Leben ähnlich leichter machen kann wie Kommentare.
Jede Variable hat einen eindeutigen Name. Im einem größeren Programm fallen endliche Verschiedene Variablen an und irgendwann steht man vor dem Problem: welche hat welchen Inhalt? Schritt 1 den man lernen wird ist also das $n nicht so gut ist wie $nachname. Aber auch bei der Variable $nachname weiß man eventuell nicht mehr welchen Inhalt sie hat. Hierbei meine ich nicht ob es sich um Müller oder Schmitz handelt, sondern ob es eine String ist (ein Name) oder ein Array (mehrere Namen). Ich habe es mir angewöhnt die Variablen so zu benennen, das man das erkennen kann. Jetzt kann man etwas weiteres über die beiden eben eingefügten Variablen zu wissen schon sagen, dass $strAction ein String (str) ist und die etwas mit einer Aktion zu tun hat. Dementsprechend ist $intStep eine Zahl (Integer = int) und hat wohl was mit einem Schritt zu tun.
Ich benutze Buchstaben (z.b. $i oder $j) für Schleifen und Variablen ohne vorgestellten Typ (z.b. $temp) für temporäre Zwischenspeicherungen.
Was jetzt in der tpadmin/index.php noch folgt, ist eine if-Kontruktion. Diese wurde teilweise von im ersten Teil besprochen.
Durch if ($strAction=="new") wird der folgende Teil nur ausgeführt wenn $strAction den Wert new hat. In dieser if-Klammer stehen dann drei weitere if-Konstruktionen. Neu ist hier das elseif. In dem ersten if wird, wenn $intStep=="" ist die Datei ../inc/admin_newinput.inc.php eingebuden. Mit dem folgende elseif wird angegeben, dass wenn das vorherige nicht zutritt (als $intStep nicht leer ist) und $intStep=="1" ist die Datei ../inc/admin_newerror.inc.php eingebunden wird. Tritt dieses auch nicht zu und ist $intStep=="2" wird die Datei ../inc/admin_newsave.inc.php eingebunden. Die elseif-Anweisung hat den Vorteil, das wenn eine if Erfolg hat, die restlichen if-Abfragen nicht mehr abgearbeitet werden.
Unter der Verschachtelten If-Kontrution steht noch eine Konstruktion mit else. else ist ähnlich wie elseif eine Anweisung, die nur ausgeführt wird, wenn das if darüber nicht greift. Allerdings ist else nicht an eine weitere Bedingung geknüpft, sondern wird in jedem Fall ausgeführt.
Zurzeit sind die beiden Variablen leer. An dem ersten if scheitert das Script also. $strAction ist ja nicht gleich new. Alles was in der Klammer steht wird ignoriert (also die weiteren if und elseif) und das Script geht weiter zu dem else. Den Inhalt dieser Klammern wird ausgeführt. Also: include ("../inc/admin_choise.inc.php");. Mit dieser Datei beschäftigt sich der nächste Abschnitt. Die Datei tpadmin/index.php ist somit erläutert. Ich bitte Sie aber immer im Auge zu behalten, da sie weiterhin der zentrale Punkt ist.
Wenn die if-Schleifen in der Datei tpadmin/index.php nicht greifen wird als letzte else-Möglichkeit die Datei admin_choise.inc.php included. Diese Datei zeigt zurzeit nur einen Auswahlpunkt an (neue Nachricht anlegen), wird aber im Laufe des Kurses erweitert. Ein Blick in die Datei: Codebox 2.6 :: inc/admin_choise.inc.php <fieldset>
<legend>Auswahl</legend>
<form name="choise1" id="choise1" method="post" action="<? echo $_SERVER['PHP_SELF'] ?>">
<input name="strAction" type="hidden" value="new" />
<label for="strAction" class="left">Neue Nachricht anlegen:</label>
<input type="submit" name="Submit" value="Go!" class="button" />
</form>
</fieldset> Hier wird ein einfaches HTML-Formular ausgegeben. Durch das sezten von strAction auf den Wert new wird veranlasst, dass die Datei nach dem Neuladen (action="<? echo $_SERVER['PHP_SELF']; ?>") die Datei inc/admin_newinput.inc.php included wird. Als Action wird mittels echo die Variable $_SERVER['PHP_SELF'] ausgegeben. Diese Variable enthält immer automatisch die URL der aktuellen Seite und eigenet sich daher besonders gut um ein Formular zur Verarbeitung abzusenden. Auch die Datei inc/admin_newsinput.inc.php die nach der Auswahl "Neue Nachricht anlegen" included wird, enthält ein HTML-Formular. Codebox 2.7 :: inc/admin_newinput.inc.php <fieldset>
<legend>Neue Nachricht</legend>
<form name="input1" id="input1" method="post" action="<? echo $_SERVER['PHP_SELF'] ?>">
<input name="intStep" type="hidden" value="1" />
<input name="strAction" type="hidden" value="new" />
<label for="strTitel" class="left">Titel:</label>
<input type="text" name="strTitel" align="right" />
<br />
<label for="strNews" class="left">Nachricht:</label>
<textarea name="strNews" cols="30" rows="10" align="right" ></textarea>
<br />
<label for="strDate" class="left">Startdatum:</label>
<input name="strDate" type="text" size="10" maxlength="10" align="right" value="<? echo date('d.m.Y'); ?>" />
(MM.DD.YYYY)<br>
<label for="strTime" class="left">Startzeit:</label>
<input name="strTime" type="text" size="10" maxlength="5" align="right" value="<? echo date('H:i'); ?>" />
(HH:MM)<br>
<label for="strAuthor" class="left">Autor:</label>
<input name="strAuthor" type="text" align="right" />
<br />
<br />
<label for="" class="left"></label>
<input type="submit" name="Submit" value="Abschicken" class="button" />
<input type="reset" name="Submit2" value="Löschen" class="button" />
</form>
</fieldset> Für den in HTML geübten Leser sollte dieses Formular keine Schwierigkeiten aufwerfen. Daher hier nur alle PHP-Zeilen und Besonderheiten:
Die Variable strAction wird weiter auf new gesetzt, zusätzlich wird die Variable intStep auf 1 gesetzt. Das Script ist noch beim Anlegen einer neuen Nachricht (new) und ist beim Schritt Nummer 1 (= News eingeben).
Außer dem schon erläuterten $PHP_SELF gibt es in dieser Datei noch zwei weitere echo-Ausgaben. Beide geben mit date() erzeugten Wert aus. Über date() kann man alle möglichen Strings aus dem Bereich Zeit und Datum ausgeben. In den Klammern vermischen sich Text und formatierte Ausgaben der aktuellen Zeit. date('d.m.Y') gibt beispielswiese 31.12.2004 aus. Die Punkte sind Text und können beliebig ausgewechselt werden. Das kleine d steht für den aktuellen Tag des Monats mit führender Null. Das kleine m steht für dem Monat als Zahl mit führender Null. Das große Y steht für das Jahr im vierstelligen Format. Um die Anwendung deutlicher zu machen hier die Ausgabe eines englisch formatierten Datum date('m/d/Y').
echo date('H:i') gibt dementsprechend die Uhrzeit aus. H steht für die Stunde im 24iger Format und i für Minuten.
Die Möglichkeiten von date() sind umfangreich. Eine Liste alle Platzhalter findet man in der PHP-Dokumentation. Wird das obige Forumlar abgesendet, dann wird (strAction=new und intStep=1) die Datei inc/admin_newerror.inc.php included. Codebox 2.8 :: inc/admin_newerror.inc.php <?
// einlesen von Post´s
$strTitel=htmlentities($_POST['strTitel']);
$strNews=preg_replace('#<br />#','' , $_POST['strNews']);
$strNews=nl2br(htmlentities($strNews));
$strDate=htmlentities($_POST['strDate']);
$strTime=htmlentities($_POST['strTime']);
$strAuthor=htmlentities($_POST['strAuthor']);
// error auf 0
$intError=0; ?>
<fieldset>
<legend>Neue Nachricht überprüfen</legend>
<form name="input1" id="input1" method="post" action="<? echo($PHP_SELF) ?>">
<label for="strTitel" class="left">Titel:</label>
<? if ($strTitel!="") {
echo $strTitel;
?><input name="strTitel" type="hidden" value="<? echo $strTitel; ?>" /><?
}
else {
$intError=1;
?><input type="text" name="strTitel" align="right" style="background: #ff0000;" /><?
} ?>
<br />
<label for="strNews" class="left">Nachricht:</label>
<? if ($strNews!="") {
echo "<br /><br />".$strNews;
?><input name="strNews" type="hidden" value="<? echo $strNews; ?>" /><?
}
else {
$intError=1;
?><textarea name="strNews" cols="30" rows="10" align="right" style="background: #ff0000;"></textarea><?
} ?>
<br />
<label for="strDate" class="left">Startdatum:</label>
<? if ($strDate=="") {
$strDate=date('d.m.Y');
$intError=1
?><input name="strDate" type="text" size="10" maxlength="10" align="right" value="<? echo $strDate; ?>" style="background: #ff0000;" /><?
}
else {
$arrDatePart=explode(".", $strDate);
if (checkdate($arrDatePart[1], $arrDatePart[0], $arrDatePart[2])) {
echo ($strDate);
?><input name="strDate" type="hidden" value="<? echo $strDate; ?>" /><?
}
else {
?><input name="strDate" type="text" size="10" maxlength="10" align="right" value="<? echo date('d.m.Y'); ?>" style="background: #ff0000;" /><?
$intError=1;
}
} ?>
(MM.DD.YYYY)<br>
<label for="strTime" class="left">Startzeit:</label>
<?
if ($strTime=="") {
$strTime=date('H:i');
$intError=1
?><input name="strTime" type="text" size="10" maxlength="10" align="right" value="<? echo $strTime; ?>" style="background: #ff0000;" /><?
}
else {
$arrTimePart=explode(":", $strTime);
if (($arrTimePart[0]<=23) AND ($arrTimePart[0]>=0) AND ($arrTimePart[1]>=0) AND ($arrTimePart[1]<=59)) {
echo $strTime;
?><input name="strTime" type="hidden" value="<? echo $strTime; ?>" /><?
}
else {
?><input name="strTime" type="text" size="10" maxlength="10" align="right" value="<? echo date('H:i'); ?>" style="background: #ff0000;" /><?
}
} ?>
(HH:MM)<br>
<label for="strAuthor" class="left">Autor:</label>
<?
if ($strAuthor!="") {
echo $strAuthor;
?><input name="strAuthor" type="hidden" value="<? echo $strAuthor; ?>" /><?
}
else {
$intError=1;
?><input type="text" name="strAuthor" align="right" style="background: #ff0000;" /><?
}
?>
<br /><br />
<label for="" class="left"></label>
<? if ($intError==1) { ?>
<input name="intStep" type="hidden" value="1" />
<? }
else { ?>
<input name="intStep" type="hidden" value="2" />
<? } ?>
<input name="strAction" type="hidden" value="new" />
<? if ($intError==1) { ?>
<input type="submit" name="Submit" value="Berichtigen" class="button" />
<? }
else { ?>
<input type="submit" name="Submit" value="Speichern" class="button" />
<? } ?>
</form>
</fieldset>In der Datei inc/admin_newserror.inc.php werden die Angaben des Users überprüft und für die Speicherung in der Datenbank vorbereitet. Zunächst werden die per POST übergebenen Eingaben in das Script eingelesen. Im Prinzip wurde diese Prozedur mit $_POST schon besprochen. In einem Schritt wird hier aber gleichzeitig die Aufbereitung der Daten gemacht. Alle Daten werden mit dem Befehl htmlentities() umgewandelt. Dieser bewirkt, dass HTML-Symbote umgewandelt werden. Im Gegensatz zu htmlspecialchars(), dass nur & zu & " zu "quot; und < > zu $lt; und $gt; umwandelt, werden beispielsweise auch die deutschen Umlaute berücksichtigt. (Ä zu $Auml;)
Durch diese Umwandlung kann man zuverlässig verhindern, dass kein feindlicher Code ausgeführt wird. Man stelle sich vor in die Textarea würde eine JS eingegeben werden. PHP gibt das JS dann umgeprüft in den Quellcode und der Browser installiert im Extremfall von der Anzeigeseite aus eine Dailer beim Besucher.
Die verschachtelte Schreibwiese $strTitel=htmlentities($_POST['strTitel']); entspricht nicht anderem als zunächst $strTitel=$_POST[strTitel]; und dann $strTitel=htmlentities($strTitel);.
Bevor die zweite Zeile der Sektion "einlesen von Post´s" betrachtet wird, zunächst die dritte Zeile. Dort begegnet einem der Befehl nl2br(). Dieser wandelt jeden Zeilenumbruch (Return in dem Textarea) zu einem <br />-Tag um. (nl = linefeed; 2 = to; br = br ;]). Damit wird erreicht, dass der von User eingegebene Text in der Textarea 1:1 mit allen Zeilenumbrüchen dargestellt wird. Ohne diesen Befehl würde PHP alles in einer Zeile ausgeben.
Um die zweite Zeile zu erläutern greife ich im Ablauf des Sripts etwas vor. Das jetzt eingeladene Include prüft die Angaben und gibt, wenn Fehler in den Einträgen sind, oder keine Einträge vorhanden sind, ein weiteres Formular aus, welches Änderungen an den fehlerhaften Stellen abfragt. Schickt man dieses Formular wieder ab, wird erneut dieses Include geladen und die Daten erneut geprüft. Natürlich werden dazu wiederum die Daten eingelesen und umgewandelt. Das ist Prinzipiell kein Problem, aber die Umwandlung mit nl2br() verdoppelt sich. nl2br() fügt zwar für jedes Linefeed [ chr(10) oder chr(13) ] eine <br /> in den Text ein, belässt aber die Linefeeds im Text, damit die Lesbarkeit des Codes nicht leidet. Als Ergebnis zieht sich der Text immer weiter auseinander. (Wer es testen möchte Kommentiert die Zeile einfach mit // aus.) Über preg_replace() suche und ersetzen wir jetz einfach die vorhandenen <br /> durch nichts. Der vollständige Befehl lautet $strNews=preg_replace('#<br />#', '',
$_POST['strNews']); Die #-Zeichen vor und nach dem <br/> sind zur Abgrenzung des Suchstrings. Das ganze fällt dann unter den Oberbegriff Regular Expressions und ist ein nicht einfaches, aber sehr mächtiges Hilfsmittel, auf das ich an dieser Stelle nicht genauer eingehe, da in einem späteren Teil des Kurses ausführlichst davon Gebrauch gemacht wird.
Jetzt weiter im Script. Zunächst wird die Hilfsvariable $intError auf 0 gesetzt. Null bedeutet keine Fehler gefunden. Die HTML-Ausgabe des Formluars dürfte sich von selbst erklären. In dem Formular kommen verschiedene if-Blöcke vor, die alle den gleichen Zweck erfüllen: das Prüfen der Angaben.
Mit if ($strTitel!="") { } wird geprüft ob ein Titel angegeben wurde. Zur Erinnerung != bedeutet "ist nicht". Wenn also $strTitel nicht leer ist wird der Titel mit echo ausgegeben und eine hidden-Formularfeld mit dem Titel gefüllt. Wenn das nicht der Fall ist (also $strTitel) leer ist wird die else-Klammer ausgeführt. Hier wird eine Forumlarfeld ausgegeben und dieses Rot markiert. Zusätzlich wird die Hilfvariable $intError auf 1 gesetzt. Ein Fehler wurde gefunden.
Bis auf die Prüfung des Datum und der Zeit ist diese Erläuterung analog auf die anderen Felder anzuwenden. Bei Datum und Zeit wird zusätzlich die Richtigkeit der Daten geprüft.
Ist das Datum leer wird das aktuelle Datum angegeben. Ein leeres Datumfeld wird als Fehler angesehen, weil der User das jetzt erzeugte Datum noch nicht prüfen konnte. Die Herstellung des Datums mit date() wurde schon besprochen.
Wenn etwas im Datumfeld eingetragen ist wird dieser String an den im Datum vorkommenden Punkten zerteilt. $arrDatePart=explode("."; $strDate); übernimmt das udn schreibt die Teile (Tag, Monat, Jahr) in das angegebene Array. Mit if (checkdate($arrDatePart[1], $arrDatePart[0], $arrDatePart[2])) { } prüft man anschließend ob das Datum wirklich existiert. Hierbei ist auf die Reihenfolge des Datums zu achten. Erst Monat, dann Tag und Jahr. checkdate() gibt TRUE oder FALSE zurück. Je nach Ergebnis wird wieder eine hidden oder eine markiertes Formularfeld ausgegeben und bei FALSE $intError auf 1 gesetzt.
Ähnlich funktioniert die Prüfung der Uhrzeit. Auch hier wird $strTime mit explode am Doppelpunkt zerlegt und die beiden Teile (Stunden und Minuten) geprüft. Dazu gibt es aber keine Funktion. Mittels der if-Anweisung if (($intTimePart[0]<=23) AND ($intTimePart[0]>=0) AND ($intTimePart[1]>=0) AND ($intTimePart[1]<=59)) { } wird geprüft, ob die Stundenzahl zwischen 0 und 23 und die Minutenzahl zwischen 0 und 59 liegt. Der Rest ist bekannt.
Mit den beiden letzten if-Abfragen wird jetzt die nächste Aktion bestimmt. Je nachdem welchen Wert $intErorr hat wird der Wert von $intStep angepaßt. Ist $intError = 1 (es sind Fehler da) wird $intStep mit 1 ausgegeben, was bedeutet, dass die Indexdatei erneut inc/admin_newserror.inc.php included und die Prüfung erneut durchläuft. Ist kein Fehler aufgetreten ($intErorr=0) wird $intStep auf 2 gesetzt und als nächstes wird inc/admin_newsave.inc.php geladen. Und damit beschäftigt sich der nächste Abschnitt. Da jetzt die korrekten Daten vorliegen, müssen diesen nur noch in der Datenbank gespeichert werden. Dazu muss man wie im ersten Teil dieses Kurses gezeigt deine Verbindung zum Datenbankserver aufbauen. Codebox 2.9 :: inc/opendb.inc.php <?php
/* Edit
***************************************************************/
$con_server = "localhost"; // Adresse des mySQL-Server
$con_user = "root"; // Benutzername
$con_pass = ""; // Passwort
$con_db = "phpmysql"; // Datenbank
/***************************************************************/
$mysql_conn = @mysql_connect($con_server, $con_user, $con_pass);
if ($mysql_conn != TRUE) {
echo "Es kommte keine Verbindung mit der Datenbank hergestellt werden! Fehlermeldung: <br /><br />".mysql_error();
die;
}
mysql_select_db($con_db, $mysql_conn);
?>Damit dieser Kurs vollständig ist hier nochmals den Inhalt der Datei. Der Inhalt sollte nach Teil 1 klar sein. Codebox 2.10 :: inc/admin_newsave.inc.php <?
include ("../inc/opendb.inc.php"); // DB öffen
// Zeiten umrechnen so das Sie ins mySQL Format passen.
$strTime=$_POST['strTime']; // aus POST
$strTime.=":00"; // Sekunden an Zeit anhängen
$intDatePart=explode(".", $_POST['strDate']);
$strMysqlDate="$intDatePart[2]-$intDatePart[1]-$intDatePart[0]"; // Datum umformatieren
$q_insert="INSERT INTO tp_news (titel, nachricht, startdatum, startzeit, autor) VALUES
('$_POST[strTitel]', '$_POST[strNews]', '$strMysqlDate', '$strTime', '$_POST[strAuthor]')";
$r_insert=mysql_query($q_insert);
if ($r_insert==TRUE) { // alles ok
mysql_close($mysql_conn); // DB schließen
?>
<fieldset>
<legend>Nachricht gespeichert
</legend>
<form name="choise1" id="choise1" method="post" action="<? echo($PHP_SELF) ?>">
<input name="strAction" type="hidden" value="" />
<label for="strAction" class="left">Zurück zur Auswahl</label>
<input type="submit" name="Submit" value="Go!" class="button" />
</form>
</fieldset>
<?
}
else {
echo "Fehler beim Speichern der Daten! Fehlermeldung: <br /><br />".mysql_error();
die;
}
?>Zunächst wird also die eben angelegte Datei inc/opendb.inc.php included und so die Verbindung zur Datenbank hergestellt.
In dem nächste Schritten werden die von User eingegebenen Zeit und Datumsangaben in die für mySQL benötigte Form umgewandelt. In die Variable $strTime holt man zunächst mittels $_POST die übergebenen Inhalte. Da der User nur Stunden und Minuten angegeben hat, die Datenbank aber auch Sekunden erwartet, hängt man an $strTime einfach ":00" an. Die Kurzform $strTime.=":00"; ist gleichbedeutend mit der ausführlichen Form $strTime=$strTime.":00";
Mit dem in letzten Abschnitt besprochenen Befehl explode teilt man dann das Datum an den Punkten in seine Einzelteile. In der nächsten Zeile fügt man dieses Teile wieder in mysql-gerechter Form zusammen. (Format YYYY-MM-DD).
Als nächstes kommen die entscheidenden Schritte, um die Daten wirklich in die Datenbank zu bekommen. Zunächst baut man sich die Query auf. INSERT INTO datenbankname (namen, der, felder) VALUES (variablen, oder, daten). Hier werden die Datenbankfelder titel, nachricht, startdatum, startzeit, und autor mit den Angaben gefüllt. Außer $strMysqlDate und $strTime müssen die Variablen noch mittels $_POST eingelesen werden.
Mit der Zeile $r_insert=mysql_query($q_insert); wird die Query jetzt endgültig an die Datenbank gesendet.
In einer nachfolgenden If-Abfrage wird getestet ob $r_insert TRUE zurückgibt. Ist das der Fall wurden die Daten in der Datenbank gespeichert und PHP gibt ein Formular aus, welches eine OK-Meldung an den Unser ausgibt und ihn zurück zur Auswahl springen lässt. Damit die tpadmin/index.php die richtige Datei included muss $strAction="" gesetzt werden. (= keine Aktion gewählt.)
Gibt $r_insert FALSE zurück ist das speichern fehlgeschlagen und es wir eine Fehlermeldung mit mysql_error() ausgegeben. Da dieser Teil des Kurses schon recht lang geworden ist, mache ich hier mal den perfekten Cliffhanger.
Frei nach dem Motto: "So jetzt brennt das Haus und wie wir das wieder löschen erfährt ihr beim nächsten Mal." wird der nächste Teil sich mit der Ausgabe der Daten über ein Templatesystem beschäftigen. An dieser Stelle werden die, in dem jeweiligen Teil neu besprochenen PHP und mySQL-Befehle aufgelistet. Die Befehle sind ggf. mit der entsprechenden Seite auf der offiziellen Dokumentation verlinkt. Sebastian Lauff ist Diplom-Ingenieur der Fachrichtung Architektur. Seit 1996 arbeitet er angestellt und frei im Bereich Web-Entwicklung. Weitere Informationen zu Ihm findet man unter dem URL lauff.info.
Anfragen und Anregungen zu diesem Kurs bitte unter tp@lauff.info.
|