PHP

Inhaltsverzeichnis

PHP als Script-Sprache

Dieses Kapitel richtet sich an Betreiber von Websites, die einen Bedarf an serverseitiger Programmierung haben und sich dabei für eine Lösung mit PHP entscheiden. Ein solcher Bedarf kann schnell entstehen:

Die Liste ließe sich beliebig weiter fortführen.

PHP ist für all diese Anwendungsfälle und für viele andere gerüstet. Keine andere Webprogrammiersprache bietet so viele vordefinierte Funktionen an, um mit möglichst wenig eigenem Programmieraufwand möglichst viel zu erreichen. Nicht einmal, wenn Sie dynamisch PDF-Dokumente oder Grafiken oder Flash-Movies erzeugen wollen, lässt sie PHP im Stich, und erst recht nicht, wenn es um Datenbankanbindung, Mailversand, Socketverbindungen für beliebige Anwendungsprotokolle, XML oder DOM geht. Vom einfachen Form-Mailer über einen anwenderfreundlichen Shop bis hin zum Content-Management-System lässt sich PHP für so ziemlich alle Bereiche einsetzen.

PHP ist so konzipiert, dass der Einstieg leicht fällt.

Erste Erfolge sind meist schnell erzielt. Vor allem macht PHP den Umstieg von statischem HTML sehr einfach, da sich PHP-Code auch einfach mitten im ansonsten statischen HTML-Code notieren lässt.

Gleichzeitig ist die Sprache aber auch flexibel genug, um den Anforderungen professioneller Softwareentwicklung gerecht zu werden. Von objektorientierter Programmierung bis hin zu einem ausgeklügelten Include-Automatismus steht fast alles zur Verfügung, was jenseits von „Quick & Dirty-Programmierung benötigt wird.

PHP ist zwar vom Ansatz her leicht erlernbar, kann aber auch sehr komplex werden. Das betrifft weniger die syntaktischen Konstrukte – diese sind in der Regel besser lesbar als etwa bei der Shell-Programmierung unter Unix oder auch bei manchen Konstrukten in Perl. Schwer überschaubar ist aber dagegen die unglaubliche Menge an vordefinierten Funktionen, von denen viele ihre Besonderheiten haben.

Vieles ist in Programmiersprachen, die letztlich auf der Welt der Programmiersprache C basieren, syntaktisch gleich oder ähnlich gelöst, beispielsweise die verfügbaren Variablentypen, die möglichen Konstrukte bei Kontrollstrukturen, das Prinzip von Funktionen, Parametern und Rückgabewerten und ein Basis-Set an Funktionen für verschiedene Zwecke. PHP gehört ebenso wie JavaScript zu diesen Sprachen. Wenn Sie bereits die Kapitel über JavaScript und DOM durchgearbeitet haben, werden Sie in dieser Hinsicht in PHP einiges Bekanntes wiederfinden und können von dem bereits erworbenen Know-how profitieren.

Auf den meisten Root-Servern, die sich heute bei Hosting-Providern anmieten lassen, ist PHP längst vorinstalliert. Da die Sprache jedoch weiterentwickelt wird und in zahlreichen neuen Zwischenversionen erscheint, sind hin und wieder Updates sinnvoll. Allerdings muss dabei auch vor Unvorsichtigkeit gewarnt werden: PHP sorgte schon in manch einer Zwischenversion für böse Überraschungen. Scripts liefen plötzlich nicht mehr oder falsch, weil bestimmte Funktionen andere Parameter erwarteten oder eine andere Art von Rückgabewert erzeugten. Vor einem PHP-Upgrade in einer Produktivumgebung ist daher stets erst ein Upgrade in einer möglichst ähnlichen Testumgebung zu empfehlen.

Auf jeden Fall sollten Sie sich PHP auch in Ihrer lokalen Entwicklungsumgebung, also auf Ihrem PC installieren. Nachdem dort nach Lektüre der vorangegangenen Kapitel auch schon der Apache Webserver installiert sein sollte, ist die Installation von PHP nur der nächste logische Schritt.

Download und Versionen

Für MS Windows stehen fertig kompilierte Fassungen zur Verfügung. Am einfachsten ist es, eine „Installer“-Variante herunterzuladen. Damit können Sie PHP wie andere Windows-Anwendungen auch bequem über ein Setup-Programm installieren. Für Unix/Linux-Umgebungen stehen die üblichen tar.gz– oder tar.bz2-Formate zur Verfügung. Auch für andere Betriebssysteme, wie Macintosh OS oder AS/400 werden Versionen angeboten.

PHP ist Open Source.

Sie können es also ohne Lizenzgebühren downloaden und nutzen, auch für kommerzielle Webanwendungen.

Voraussetzung für den Betrieb von PHP als Script-Sprache für Webseiten ist eine funktionierende Webserver-Umgebung. Da PHP mit dem Apache Webserver optimal zusammenarbeitet, ist dieser Webserver wärmstens zu empfehlen.

Installation unter Unix/Linux

Zur Installation benötigen Sie root-Rechte. Gehen wir von der Annahme aus, dass Sie eine tar.gz-Datei von PHP auf den Rechner geladen haben, auf dem es installiert werden soll. Zunächst müssen Sie die Datei dekomprimieren:

gunzip php-xxx.tar.gz  

Dabei steht xxx für die Versionsnummer. Achten Sie also auf den genauen Dateinamen.

Als Nächstes entpacken Sie die entsprechende Archivdatei:

tar -xvf php-xxx.tar  

Stellen Sie vor der Installation sicher, dass ein Ansi-C-Compiler auf dem System installiert ist. Das Standardprodukt auf Linux-Distributionen ist der GNU C-Compiler GCC. Über die Shell können Sie beispielsweise Folgendes eingeben:

gcc --help  

Wenn dann Versionsinformationen und Aufrufmöglichkeiten zu GCC erscheinen, ist alles in Ordnung.

Als Nächstes sind grundsätzliche Konfigurationen für die Installation nötig. Das betrifft vor allem den Ablageort der endgültigen Installation sowie den Ablageort der zentralen PHP-Konfigurationsdatei php.ini. Dazu dient das .configure-Script. Zahlreiche weitere Einstellungen, die mit diesem Script durchführbar sind, können jedoch später in Ruhe auch in der php.ini oder durch erneuten Aufruf des Scripts vorgenommen werden. Daher genügt zunächst eine Grundkonfiguration wie diese:

./configure --prefix=/usr/local/bin/php --with-config-file-path=/etc  

In diesem Beispiel wird PHP im Verzeichnis /usr/local/bin/php installiert. Die wichtige Konfigurationsdatei php.ini wird im zentralen Konfigurationsverzeichnis /etc abgelegt. Dies ist übrigens durchaus der übliche Ablageort dieser Datei. Anschließend kann der Kompilierungsvorgang gestartet werden:

make  

Nachdem dieser Vorgang abgeschlossen ist, kann die Installation gestartet werden:

make install  

Die zentrale Konfigurationsdatei php.ini existiert nach der Installation noch nicht. Bei der Distribution ist lediglich eine Beispieldatei unter dem Namen php.ini-dist dabei. Kopieren Sie diese aus dem Installationsverzeichnis ins Verzeichnis /etc unter dem endgültigen Namen:

cp php.ini-dist /etc/php.ini  

Installation unter MS Windows

Wenn Sie wie empfohlen eine Windows-Installer-Version benutzen, können Sie sich auf das Setup verlassen. Dabei können Sie auch das Verzeichnis für den installierten Zustand festlegen.

Auch für die Windows-Distribution gilt: Die wichtige Konfigurationsdatei php.ini existiert zunächst noch nicht. Sie muss erst erstellt werden. Außerdem muss sie im Pfad liegen. Kopieren Sie dazu aus dem endgültigen Ablageverzeichnis von PHP die dort vorhandene Datei php.ini-dist in Ihr Windows-Verzeichnis, also z.B. in c:\windows oder c:\winnt. Benennen Sie die Datei dort um in php.ini.

Ferner wird empfohlen, aus dem endgültigen Ablageverzeichnis von PHP die dort vorhandene Datei php4ts.dll bzw. php5ts.dll in Ihr Windows-Systemverzeichnis zu kopieren, also z.B. in c:\windows\system32 oder c:\winnt\system32.

Es gibt zwei Möglichkeiten, PHP-Scripts von Apache ausführen zu lassen: entweder als Apache-Modul oder als CGI-Scripts. Die Lösung als Apache-Modul ist dann vorzuziehen, wenn PHP intensiv eingesetzt werden soll und auch Priorität gegenüber anderen Script-Sprachen auf dem Server haben soll. Außerdem entfallen bei der Variante „PHP als Modul“ die typischen CGI-Merkmale wie Beschränkung auf bestimmte Verzeichnisse usw. Nachteil ist jedoch, dass Apache damit insgesamt mehr Arbeitsspeicher pro Prozess benötigt, und zwar auch dann, wenn gar keine PHP-Scripts ausgeführt werden müssen. Wenn PHP als Apache-Modul läuft, laufen PHP-Scripts auf Unix-/Linux-Umgebungen auch unter dem Benutzernamen von Apache, sie haben also die gleichen Rechte wie dieser.

Auf den meisten öffentlichen Server-Rechnern ist PHP als Apache-Modul konfiguriert.

Einbindung unter Unix/Linux

Um PHP als Apache-Modul zu konfigurieren, müssen Sie die Installation von PHP korrigieren, indem Sie in dem Verzeichnis, in dem Sie PHP dekomprimiert und entpackt haben, das .configure-Script erneut aufrufen mit:

./configure --with-apxs=[Pfad/zu/apxs]  

Dabei ist apxs eine ausführbare Datei von Apache. Wo sie genau liegt, können Sie herausbekommen durch Eingabe von:

find / -name apxs  

Nachdem das .configure-Script durchgelaufen ist, müssen Sie erneut make und make install ausführen.

Wechseln Sie nun in das Verzeichnis, in dem der Apache Webserver installiert ist, also z.B. in /usr/local/bin/apache, und dort ins Unterverzeichnis libexec. Dort sollte sich nun eine Datei libphp4.so oder libphp5.so befinden, je nachdem, ob Sie PHP 4.x oder PHP 5.x installiert haben.

Sind diese Voraussetzungen erfüllt, müssen Sie in der httpd.conf, also in der zentralen Konfigurationsdatei des Apache Webservers, folgende Einträge vornehmen:

LoadModule php4_module  libexec/libphp4.so  

Und für PHP 5.x durch folgenden Eintrag:

LoadModule php5_module  libexec/libphp5.so  

Notieren Sie die Einträge innerhalb der httpd.conf dort, wo LoadModule-Einträge für dynamische Module (DSO) stehen sollten.

AddType application/x-httpd-php .php  

Möchten Sie dagegen PHP nicht als Apache-Modul einbinden, sondern einfach als möglichen Handler für CGI-Scripts, sind folgende Änderungen in der httpd.conf erforderlich:

LoadModule cgi_module modules/mod_cgi.so  
Action php-script /cgi-bin  AddHandler php-script .php  

Nachdem Sie die httpd.conf geändert haben, speichern Sie die Datei und führen Sie einen Neustart des Apache Webservers durch

Einbindung unter MS Windows

Unter MS Windows sollten Sie zunächst die Dateien php4apache.dll bzw. php4apache2.dll (je nach Apache-Version) aus dem PHP-Unterverzeichnis sapi in Ihr Windows-Systemverzeichnis kopieren, also z.B. in c:\windows\system32 oder c:\winnt\system32. Dadurch liegen diese Dateien zusätzlich im Pfad und mögliche Probleme des Apache Webservers beim Laden dieser Bibliotheken werden vermieden.

Auch unter Windows können Sie PHP wahlweise als Apache-Modul oder als Handler für CGI-Scripts einbinden. In beiden Fällen müssen Sie die zentrale httpd.conf des installierten Apache Webservers bearbeiten.

Um PHP als Apache-Modul zu installieren, sind folgende Einträge erforderlich:

LoadModule php4_module  [Pfad zu PHP]/sapi/php4apache*.dll  

Und für PHP 5.x durch folgenden Eintrag:

LoadModule php5_module  [Pfad zu PHP]/sapi/php5apache*.dll  

Dabei ist [Pfad zu PHP] der vollständige Laufwerkspfad zum Programmverzeichnis von PHP, also etwa c:/php oder c:/Programme/php. Benutzen Sie einfache Schrägstriche zur Verzeichnistrennung, nicht den unter Windows üblichen Backslash. Welche Datei Sie genau adressieren müssen, hängt von der PHP- und der Apache-Version ab. Bei PHP 4.x und Apache 1.3.x heißt sie php4apache.dll, bei PHP 4.x und Apache 2.0.x php4apache2.dll, bei PHP 5.x und Apache 1.3.x php5apache.dll und bei PHP 5.x und Apache 2.0.x php5apache2.dll.

Notieren Sie die Einträge innerhalb der httpd.conf dort, wo LoadModule-Einträge für dynamische Module (DSO) stehen sollten.

AddType application/x-httpd-php .php  

Möchten Sie dagegen PHP nicht als Apache-Modul einbinden, sondern einfach als möglichen Handler für CGI-Scripts, sind folgende Änderungen in der httpd.conf erforderlich:

LoadModule cgi_module modules/mod_cgi.so  
Action php-script /cgi-bin  AddHandler php-script .php  

Nachdem Sie die httpd.conf geändert haben, speichern Sie die Datei und führen Sie einen Neustart des Apache Webservers durch.

Austesten von PHP unter Apache: ein erstes Script

Wenn Sie alles richtig installiert haben, sollten PHP-Scripts nun ausführbar sein. Wir nehmen in unseren nachfolgenden Beispielen an, dass PHP als Apache-Modul installiert ist. PHP-Dateien können dann überall unterhalb der Document Root, also unterhalb des Startverzeichnisses für Webdokumente, abgelegt werden. Erstellen und bearbeiten lassen sie sich mit jedem Texteditor. Bestens geeignet sind auch code-basierte HTML-Editoren. Viele davon unterstützen von Haus aus Syntax-Highlighting für PHP-Code, was in der Praxis sehr hilfreich ist.

Folgendes Listing eignet sich, unter einem Dateinamen wie php-test.php irgendwo unterhalb der Document Root abgespeichert, zu einem ersten Test, ob PHP korrekt funktioniert:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">  <html lang="de">  <head>  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">  <title>PHP-Test</title>  </head>  <body>  <h1><?php echo "Hier ist PHP!" ?></h1>  </body>  </html>  

Wenn es in Ihrem Browser so aussieht wie abgebildet, ist PHP korrekt installiert und in Apache eingebunden.

Das Beispiel zeigt auch schon, wie simpel PHP sein kann. Unser Listing sieht aus wie eine gewöhnliche HTML-Datei. Nur an einer Stelle, nämlich im Elementinhalt der h1-Überschrift, ist ein Bereich notiert, der mit <?php beginnt und mit ?> endet. Innerhalb davon kann PHP-Code stehen. In unserem Beispiel wird einfach nur etwas Text ausgegeben.

Wenn Sie sich im Browser den HTML-Quelltext ansehen („view source„), dann werden Sie feststellen, dass von dem PHP-Script-Bereich nichts mehr zu sehen ist. Dort steht innerhalb von <h1></h1> nur noch der von PHP ausgegebene Text.

Genau das ist das Prinzip serverseitiger Scripts.

Die Scripts werden ausgeführt, bevor die Daten zum Browser übertragen werden. Ein serverseitiges Script muss dafür sorgen, dass sinnvolle Daten zum Browser gelangen, also z.B. HTML-Code. Beim Browser selbst kommt dann nur noch der ausgegebene HTML-Code an, aber nichts mehr vom Programmcode des Scripts. Der Browser weiß nicht einmal, dass ein Script den HTML-Code erzeugt hat, bzw. es ist ihm völlig egal. Er erhält seine Daten vom Webserver genauso, als würde es sich um eine statische HTML-Datei handeln.

Umgekehrt ist es Aufgabe des Webservers, richtig zu reagieren. Versuchen wir zu verstehen, was genau passiert:

Die Scripts testen

Während Sie beim Erstellen von HTML-Seiten diese direkt im Browser betrachten können, benötigen Sie zum Testen von PHP-Dateien einen Server, der PHP unterstützt. Arbeiten Sie an Ihrem heimischen Computer und nicht am Server, müssen Sie die Scriptdateien also unter Umständen auf den Server hochladen. Sie benutzen dazu irgendein Standard-FTP-Programm, z.B. WS_FTP.

Info FTP (File Transfer Protocol) ist ein Dateiübertragungsprotokoll, mit dem Sie Texte oder binäre Dateien zwischen Ihrem Computer und einem FTP-Computer im Internet austauschen können. In den meisten Fällen benötigen Sie für den FTP-Zugang eine bestimmte Zugangsberechtigung; es gibt jedoch auch so genannte anonyme FTP-Server, die jedem Benutzer Zugang gewähren, um beispielsweise kostenlose Software herunterzuladen oder Dokumente zu beziehen.

Sobald Sie eine Verbindung zum FTP-Server aufgebaut haben, können Sie Dateien mehr oder minder so, wie im Windows-Explorer üblich, in ein entsprechendes Verzeichnis auf den Server kopieren.

Das Grundgerüst eines PHP-Scripts

Sie brauchen zum Schreiben eines PHP-Scripts, wie Sie es von HTML gewohnt sind, einen Texteditor. Ein reines PHP-Script ohne HTML-Elemente sieht dann in seiner Struktur zunächst ganz einfach aus: Die ausführbaren PHP-Codes werden in die folgenden Anfangs- und Endezeichen eingeschlossen:

<?php  PHP-Code;  ?>  

Wie Sie noch sehen werden, spielt die Groß- und Kleinschreibung vielfach eine Rolle, allerdings nicht beim Anfangszeichen. Es könnte auch <?PHP heißen. Eventuell können Sie auch bei Ihrem Provider nachfragen (bzw. in der Tabelle mit Informationen über die spezifische Installationsumgebung nachsehen, die im nächsten Abschnitt vorgestellt wird), inwieweit Sie kurze Tags benutzen können, also <? und ?> an Stelle von <?php und ?>, oder ob ASP-Tags akzeptiert werden: <% und %>. Manche Programme, beispielsweise Dreamweaver, funktionieren mit den ASP-Tags besser als mit den PHP-Tags.

Jede PHP-Anweisung wird mit einem Semikolon beendet. Damit wird PHP quasi mitgeteilt,

wo der Befehl, der ausgeführt werden soll, zu Ende ist. Es bietet sich wegen der Übersichtlichkeit an, danach jeweils in eine neue Zeile zu springen (auch wenn dies von der Sache her nicht unbedingt erforderlich ist). Ein Semikolon nach einem Code zu vergessen, ist eine oft vorkommende Fehlerquelle, versuchen Sie also immer daran zu denken. Manche Programmierer schreiben das Semikolon grundsätzlich zuerst und dann die anderen Eingaben der Programmzeile. Dies ist ein Trick, der sicherlich dafür sorgt, das Semikolon weniger häufig zu vergessen!

Zusammen mit HTML enthält ein Script die entsprechenden HTML-Tags und den PHP-Code, der in den Body-Container geschrieben und abgegrenzt wird. Das sieht dann als Grundgerüst so aus:

<html>  <head>  <title>HTML_PHP</title>  </head>  <body>  <?php  PHP-Code;  ?>  </body>  </html>  

Sie können beliebig viele PHP-Codes in ein Dokument einfügen und mit den jeweils benötigten HTML-Tags kombinieren. Sie müssen nur jedes Mal auf das Startzeichen <?php und das Endezeichen ?> achten. Oftmals ist auch eine andere Reihenfolge als die hier vorgestellte sinnvoll, z.B. das Script mit <?php zu beginnen, anstatt mit dem üblichen HTML-Header.

Grundsätzlich arbeitet der PHP-Prozessor die Datei der Reihe nach von oben bis unten ab; wann Sie welche Anweisung wohin schreiben, spielt also eine große Rolle. Reines HTML wird unverändert zurückgegeben, die PHP-Anweisungen werden ausgewertet und ausgeführt.

Speichern

HTML-Seiten, die PHP-Elemente enthalten bzw. reine PHP-Scripts sind, speichern Sie mit der Erweiterung .php. Dies ist die Standarderweiterung für PHP4, und deswegen werden wir in diesem Kapitel diese Erweiterung benutzen. Arbeiten Sie mit einem Server, auf dem PHP3 installiert ist, benötigen Sie gegebenenfalls die Erweiterung .php3. Sofern Sie nicht direkt am Server arbeiten, muss das Dokument mithilfe eines FTP-Programms auf den Server hochgeladen werden. Davon war weiter oben schon die Rede. Bei den folgenden Beschreibungen gehen wir davon aus, dass Sie am Server arbeiten, sodass Sie die Scripts testen können, indem Sie die Dateien einfach im Browser aufrufen. (Folglich heißt es bei uns dann lediglich: Speichern Sie das Script und testen Sie es im Browser oder so ähnlich.)

Ausgabe

Damit Sie so bald wie möglich ein Script im Browser testen können, lernen Sie hier (und in den meisten Büchern über PHP) als Erstes den PHP-Befehl echo kennen. Echo gibt alle Zeichenketten im Browser aus. Unter einer Zeichenkette, auch als String bezeichnet, versteht man in PHP eine Reihe von Zeichen, die aus einer beliebigen Kombination von Buchstaben, Zahlen, Symbolen und Leerstellen (und Variablen) bestehen kann und in einfache oder doppelte Anführungszeichen eingeschlossen wird.

Schauen Sie sich das einmal an:

<?php  echo "ich lerne PHP!";  ?>  
ich lerne PHP!  

Um eine bestimmte Formatierung (und/oder Seitengestaltung) zu erreichen, können Sie PHP und HTML auch mischen, indem Sie zwischen PHP und HTML hin und her wechseln. Eine Möglichkeit könnte so aussehen:

<html>  <head>  <title>HTML_PHP</title>  </head>  <body>  <b>  <?php  echo "ich lerne PHP";  ?>  </b>  <p><b>hier kommt normale HTML-Ausgabe</b></p>  <?php  echo "hier steht ein neuer PHP-Code";  ?>  </body>  </html>  

Bei dieser Methode beenden Sie also PHP, schreiben den HTML-Teil mit den jeweils benötigten Tags und beginnen PHP erneut.

Es ist aber auch möglich, HTML als PHP auszugeben. Dies ist für PHP-Einsteiger mitunter etwas verwirrend. Sie müssen sich klar machen, dass in dem Fall alles, was der Browser anzeigen soll, Teil des PHP-Codes sein und ausgegeben werden muss, auch Formatierungen, Zeilenumbrüche und Ähnliches. Ein Scriptfragment würde dann etwa so aussehen:

<?php  echo "<b>ich lerne PHP</b><br>";  echo "<h2>eine Überschrift</h2>";  echo "<hr>";  echo "neuer PHP-Code";  ?>  

Der Befehl echo funktioniert durch die Verwendung des HTML-Tags für einen Zeilenumbruch auch über mehrere Zeilen. Wenn Sie schreiben:

echo "mein Hut der hat vier Ecken <br> vier Ecken hat mein Hut!";  

wird als Ausgabe im Browser zu lesen sein:

Mein Hut der hat vier Ecken  vier Ecken hat mein Hut!  

Info Leerzeilen im Script haben keinerlei Einfluss auf das Erscheinungsbild der Seite, sie können aber das Script selbst mitunter übersichtlicher machen. Auch per Tabulator eingerückte Teile erhöhen die Lesbarkeit eines Scripts und sicherlich werden Sie feststellen, dass es sinnvoll ist, HTML und PHP optisch zu trennen.

PHP-Info

Es gibt eine gute Möglichkeit bzw. eine spezielle Funktion, sich grundlegende Informationen über PHP einzuholen. Diese Funktion lautet:

phpinfo()  

Mit dieser Funktion wird eine Tabelle an den Browser gesendet, die Informationen über die spezifische PHP-Installation auf dem fraglichen Server enthält. Sie können durch den Einsatz dieser Funktion eine ganze Menge über die Server-Umgebung und die Konfiguration erfahren, z.B. welche Erweiterungen benutzt werden können, welche Datenbank verwendet wird etc. Vor allem, wenn Sie nicht am Server arbeiten und PHP nicht selbst installiert haben, bietet es sich an, einen Blick auf diese Tabelle zu werfen. Sie brauchen ein ganz einfaches „Script“:

<?php  echo phpinfo();  ?>  

Der Browser zeigt daraufhin die erwähnte Tabelle mit den Informationen über PHP. Riskieren Sie ruhig einen Blick.

In der Vergangenheit war die Installation eines LAMP-Systems (Linux + Apache + MySQL + PHP/Perl) ein Kinderspiel. Alle gängigen Distributionen lieferten die erforderlichen Pakete gleich mit. Im Wesentlichen bestand die ganze Arbeit darin, den Paketmanager zu starten, die Pakete zu installieren und schließlich Apache und MySQL zu starten. Je nach Distribution sehen die Startkommandos so aus:

root# /etc/init.d/httpd start     # Apache bei Red Hat, Fedora  root# /etc/init.d/apache2 start   # Apache bei SUSE  root# /etc/init.d/mysql start     # MySQL bei Red Hat, Fedora und SUSE  

Damit die Server in Zukunft immer automatisch gestartet werden, führen Sie die folgenden Kommandos aus:

root# chkconfig --add httpd   # Apache bei Red Hat, Fedora  root# chkconfig --add mysql   # MySQL bei Red Hat, Fedora  root# insserv apache2         # Apache bei SUSE  root# insserv mysql           # MySQL bei SUSE  

Info Bei Red Hat bzw. beim von Red Hat geleiteten Fedora-Team ist man der Ansicht, dass die Lizenz von MySQL ab Version 4.0 eine Auslieferung nicht zulässt. Der strittige Punkt sind die Client-Bibliotheken, die seit MySQL 4.0 der GPL unterstehen. Diese relativ strenge Open-Source-Lizenz ist inkompatibel mit einigen anderen Open-Source-Projekten, die eine liberalere Lizenz verwenden, unter anderem PHP.

MySQL hat deswegen eine Ausnahmeregel (FOSS) für die Client-Bibliotheken definiert, die die gemeinsame Weitergabe mit anderen Open-Source-Projekten explizit erlaubt. Aber offensichtlich hat auch das Red Hat nicht überzeugen können. Vielleicht sollte man noch erwähnen, dass Red Hat in der Vergangenheit sehr stark auf PostgreSQL gesetzt hat und daher möglicherweise kein ganz neutrales Verhältnis zu MySQL hat …

Aktuelle Versionen von PHP und MySQL installieren

Welche Möglichkeiten bestehen nun, aktuelle Versionen von PHP und MySQL auf einer gängigen Linux-Distribution zu installieren?

Info Im Folgenden wird vorausgesetzt, dass die von Ihrer Distribution mitgelieferten Apache-, PHP- und MySQL-Pakete nicht installiert sind!

XAMPP

XAMPP installieren

Die Installation ist wirklich denkbar einfach – ein einziges tar-Kommando reicht!

root# tar xvfz xampp-linux-1.4.9a.tar.gz -C /opt  

Anschließend befinden sich sämtliche XAMPP-Komponenten im Verzeichnis /opt/lampp. Ein weiteres Kommando startet das System:

root# /opt/lampp/lampp start  

Indem Sie die Seite http://localhost mit einem Webbrowser öffnen, können Sie sich davon überzeugen, dass alles funktioniert hat.

XAMPP absichern

Die Defaultinstallation von XAMPP ist nicht durch Passwörter abgesichert und daher unsicher. Das sollten Sie mit dem folgenden Kommando ändern:

root# /opt/lampp/lampp security  

Sie können nun verschiedene Aspekte von XAMPP absichern:

XAMPP verwenden

Nun können Sie mit der Entwicklung Ihrer ersten PHP-Testprogramme beginnen. Ihre eigenen PHP-Dateien speichern Sie beispielsweise in /opt/lampp/htdocs/meinebeispiele. Dabei müssen Sie darauf achten, dass die Dateien von Apache gelesen werden können, der unter dem Account nobody ausgeführt wird.

root# mkdir /opt/lampp/htdocs/meinebeispiele  root# chown benutzername /opt/lampp/htdocs/meinebeispiele  root# chgrp nobody /opt/lampp/htdocs/meinebeispiele  

Nach diesen Vorbereitungsarbeiten sollten Sie aus Sicherheitsgründen unter ihrem normalen Account weiterarbeiten. (Anders als unter Windows ist es unter Linux verpönt, als root zu arbeiten, wenn dies nicht zu Administrationszwecken unbedingt erforderlich ist.)

XAMPP beenden

Bevor Sie den Rechner herunterfahren, sollten Sie XAMPP stoppen. Das ist insbesondere deswegen wichtig, damit der MySQL-Server alle offenen Datenbankdateien ordnungsgemäß schließen kann.

root# /opt/lampp/lampp start  

XAMPP automatisch starten und stoppen

Jedes Mal, wenn Sie Ihren Rechner starten, müssen Sie auch XAMPP neu starten. Beim Ausschalten müssen Sie daran denken, XAMPP zu beenden. Wenn Sie dazu keine Lust haben, können Sie das auch automatisch erledigen. Dazu müssen Sie in das Init-V-System Ihrer Linux-Distribution zwei Links einfügen. (Das Init-V-System steuert, wann welche Systemdienste gestartet bzw. gestoppt werden.)

Die folgenden Kommandos zeigen die Vorgehensweise für SUSE Linux. Andere Distributionen verwenden ein wenig abweichende Verzeichnisnamen (z.B. Red Hat und Fedora /etc/rc5.d).

root# cd /etc/init.d/rc5.d  root# ln -s /opt/lampp/lampp S99lampp  root# ln -s /opt/lampp/lampp K01lampp  

XAMPP deinstallieren

Ebenso einfach wie die Installation ist die Deinstallation:

root# rm -rf /opt/lampp/  

Beachten Sie, dass Sie dadurch auch alle Ihre PHP-Dateien, MySQL-Datenbanken etc. verlieren. Gegebenenfalls sollten Sie vorher ein Backup machen. Die PHP-Dateien kopieren Sie einfach von /opt/lampp/htdocs/beispiele/ in ein anderes Verzeichnis (z.B. Ihr Heimatverzeichnis). MySQL-Datenbanken sichern Sie am bequemsten mit phpMyAdmin. Alternativ können Sie auch das Kommando mysqldump zuhilfe nehmen.

user$ cp -a /opt/lampp/htdocs/meinebeispiele ~  user$ /opt/lampp/binmysqldump -u root -p \ --default-characterset=latin1 \ meinedatenbank > ~/meinedatenbank.sql  Password: ********  

MySQL

MySQL 4.1 oder 5.0 installieren

Vorweg ein kurzer Überblick: Dieser und der folgende Abschnitt beschreiben, wie Sie zuerst die vorkompilierten MySQL-Pakete von mysql.com installieren und dann Apache und PHP kompilieren. Für die ungewöhnliche Reihenfolge (zuerst MySQL) gibt es natürlich einen Grund: PHP kann nur dann mit MySQL-Unterstützung kompiliert werden, wenn MySQL bereits installiert ist.

MySQL-Freaks können natürlich auch MySQL selbst kompilieren (mehr dazu im übernächsten Abschnitt). Das ist allerdings nur in Ausnahmefällen notwendig, weil es für MySQL (anders als für PHP!) vorkompilierte Pakete gibt.

MySQL deinstallieren

Dieser Abschnitt setzt voraus, dass die von Ihrer Distribution mitgelieferten MySQL-Pakete nicht installiert sind. Bei den meisten Distributionen können Sie das mit dem folgenden Kommando überprüfen:

user$ rpm -qa | grep -i mysql  

Das Kommando listet alle installierten Pakete auf, in deren Namen mysql vorkommt. Diese Liste sollte leer sein. Wenn das nicht der Fall ist, verwenden Sie rpm -e oder den Paketmanager Ihrer Distribution, um die MySQL-Pakete zu deinstallieren.

MySQL-Pakete herunterladen

Dateiname Bedeutung
MySQL-server-version.rpm Der eigentliche MySQL-Server
MySQL-client-version.rpm Client-Werkzeuge (mysql, mysqldump etc.)
MySQL-shared-compat-version .rpm Client-Bibliotheken (inklusive älterer Versionen dieser Bibliotheken zur Kompatibilität mit älteren Programmen, die auf den MySQL-Server zugreifen möchten)
MySQL-devel-version .rpm Dateien zur Entwicklung/Kompilierung eigener MySQL-Clients (wichtig für die PHP-Kompilierung)

Die Installation ist denkbar einfach (und unabhängig davon, ob Sie sich für MySQL 4.1 oder 5.0 entscheiden):

root# rpm -i MySQL-*.rpm  

MySQL-Server starten/beenden

Um den MySQL-Server zu starten, führen Sie das folgende Kommando aus:

root# /etc/init.d/mysql start  

Um sich zu überzeugen, ob alles funktioniert hat, starten Sie mit dem Kommando mysql den MySQL-Kommandozeileninterpreter und führen dort das Kommando status aus. Mit exit oder Strg+D beenden Sie das Programm.

Ganz ähnlich wie das Startkommando sieht auch das Kommando aus, um den Server zu stoppen:

root# /etc/init.d/mysql stop  

Falls Sie möchten, dass der Server in Zukunft automatisch gestartet bzw. beendet wird, wenn der Rechner hoch- bzw. heruntergefahren wird, führen Sie eines der beiden folgenden Kommandos aus:

root# insserv mysql         (für SUSE)  root# chkconfig --add mysql (für Red Hat, Fedora, Mandrakelinux etc.)  

Info Der MySQL-Server ist nach einer Neuinstallation nicht abgesichert. Aus Sicherheitsgründen sollten Sie die beiden Benutzer root mit einem Passwort ausstatten (einmal für den Netzwerkzugang und einmal für die Kommunikation über eine Socket-Datei) und die beiden anderen Defaultbenutzer löschen.

MySQL Administrator und MySQL Query Browser installieren

Der MySQL Administrator und der MySQL Query Browser erleichtern die lokale Administration sowie das Testen von SQL-Kommandos und SPs ungemein. Deswegen sollten Sie auch diese beiden Programme installieren.

Nachdem Sie die tar-Archivdateien von http://dev.mysql.com heruntergeladen haben, entpacken Sie die Archive in das Verzeichnis /usr/local:

root# cd /usr/local  root# tar xzf mysql-query-browser-<version>-linux.tar.gz  root# tar xzf mysql-administrator-<version>-linux.tar.gz  

Anschließend ändern Sie in den Script-Dateien zum Start der beiden Programme die Variable MYPATH:

# Änderung in /usr/local/mysql-query-browser/bin/mysql-query-browser  ...  MYPATH=/usr/local/mysql-query-browser/bin    # Änderung in /usr/local/mysql-administrator/bin/mysql-administrator  ...  MYPATH=/usr/local/mysql-administrator/bin  

Zu guter Letzt richten Sie noch zwei Links ein, damit jeder Benutzer des Systems die Programme bequem starten kann:

root# cd /usr/bin/X11  root# ln -s /usr/local/mysql-administrator/bin/mysql-administrator .  root# ln -s /usr/local/mysql-query-browser/bin/mysql-query-browser .  

Apache 2 und PHP 5 kompilieren

Dieser Abschnitt beschreibt, wie Sie Apache und PHP 5 selbst kompilieren. Dazu müssen einige Voraussetzungen erfüllt sein:

user$ rpm -qa | grep -i apache  user$ rpm -qa | grep -i php  

Als Verzeichnis für alle weiteren Arbeiten wird /usr/local/src verwendet. Damit Sie dort als gewöhnlicher Benutzer arbeiten können, ändern Sie als root den Besitzer dieses Verzeichnisses:

root# chown benutzername.users /usr/local/src  

Info Fast alle Linux-Distributionen sehen einen Update-Service vor, der es sehr einfach macht, bei bekannten Sicherheitsmängeln die betroffenen Programme zu aktualisieren. Dieser Update-Service gilt aber natürlich nur für die von der Distribution mitgelieferten Pakete, nicht für selbst kompilierte Programme. Für deren Aktualisierung bei Sicherheitsmängeln sind Sie selbst verantwortlich.

Bei einem Entwicklungsrechner ist das nicht so wichtig, aber falls Sie selbst einen root-Server betreiben, sollten Sie größte Vorsicht walten lassen! Sofort nach Bekanntwerden von Sicherheitsmängeln müssen Sie den neuesten Quellcode herunterladen, kompilieren, installieren und neu starten.

Apache 2 kompilieren

Nach dem Download extrahieren Sie das tar-Archiv, bereiten die Kompilation durch configure vor, kompilieren mit make und installieren die resultierenden Dateien schließlich mit make install. (Beachten Sie, dass make install von root ausgeführt werden muss. Falls Ihr tar-Archiv die Dateiendung .bz2 statt .gz hat, lauten die richtigen tar-Optionen xjf statt xzf.)

Das configure-Kommando ist hier nur aus Platzgründen auf zwei Zeilen verteilt. Bei einer Eingabe in einer Zeile entfällt das \-Zeichen. Die configure-Option --prefix gibt an, wohin Apache installiert werden soll. --enable-so gibt an, dass Apache DSO-Erweiterungen (dynamic shared objects) unterstützen soll. Eine derartige Erweiterung wird PHP sein. --with-mpm=prefork bestimmt das Threading-Modell, also die Art und Weise, wie der Webserver auf mehrere gleichzeitige Anfragen reagiert. prefork ist das Modell, das für die Zusammenarbeit mit PHP am besten geeignet ist. Es gibt unzählige weitere Optionen, die für unsere Standardinstallation aber nicht relevant sind. configure --help liefert die schier endlose Liste von Möglichkeiten.

user$ cd /usr/local/src  user$ tar xzf httpd-2.0.52.tar.gz  user$ cd httpd-2.0.52/  user$ ./configure --prefix=/usr/local/apache2 --enable-so \ --with-mpm=prefork  user$ make  root# make install  

Damit Sie später als gewöhnlicher Benutzer Dateien in /usr/local/apache2/htdocs ändern dürfen, sollten Sie gleich die Zugriffsrechte für dieses Verzeichnis ändern.

root# chmod a+rw /usr/local/apache2/htdocs  

PHP 5 kompilieren

Nach dem Download wiederholen sich die schon bekannten Kommandos tar, .configure, make und make install. Das configure-Kommando ist aus Platzgründen auf mehrere Zeilen verteilt. Bei einer Eingabe in einer Zeile entfallen die \-Zeichen.

Einige Worte zu den eingesetzten configure-Optionen: --prefix gibt an, wohin PHP 5 installiert werden soll. --with-apxs2 bedeutet, dass PHP 5 als Erweiterungsmodul für Apache 2 kompiliert werden soll. Das nachfolgende Verzeichnis gibt den vorgesehenen Ort für Apache-Module an. --with-libxml-dir gibt an, wo sich die XML-Bibliotheken befinden. Analog gibt --with-zlib-dir den Pfad zur zlib-Bibliothek an.

--with-mysql gibt an, dass PHP mit der traditionellen mysql-Schnittstelle kompiliert werden soll. Der nachfolgende Pfad zeigt zum MySQL-Installationsverzeichnis, das bei den vorkompilierten MySQL-Paketen von dev.mysql.com einfach /usr lautet.

--with-mysqli gibt an, dass in PHP auch die neue mysqli-Schnittstelle integriert werden soll. Die nachfolgende Datei ist Teil des MySQL-devel-Pakets. Es handelt sich dabei um ein Script, das Informationen über die installierte MySQL-Version und deren Installationsorte gibt.

Die weiteren --with-xxx bzw. --enable-xxx-Optionen aktivieren diverse Zusatzfunktionen von PHP. Eine endlose Liste mit weiteren Optionen liefert das Kommando ./configure --help.

user$ cd /usr/local/src  user$ tar xzf php-5.0.2.tar.gz  user$ cd php-5.0.2  user$ ./configure --prefix=/usr/local/php5 \  --with-apxs2=/usr/local/apache2/bin/apxs \  --with-libxml-dir=/usr/lib \  --with-zlib --with-zlib-dir=/usr/lib \  --with-mysql=/usr --with-mysqli=/usr/bin/mysql_config \  --with-jpeg-dir=/usr --enable-exif \  --with-gd --enable-soap --enable-sockets \  --enable-force-cgi-redirect  user$ make  root# make install  

Info Wenn beim Kompilieren unzählige multiple-defined-Fehler für die Bibliothek libmysql auftreten, hat .configure einen Fehler in das Makefile eingebaut. (Das ist bei unseren Tests manchmal passiert; es ist allerdings unklar geblieben, warum.)

Die Lösung besteht darin, vor der Ausführung von make die Datei Makefile in einen Editor zu laden. Dort sichen Sie die Zeile EXTRA_-LIBS = ... In dieser Zeile ist -lmysqlclient zweimal enthalten. Löschen Sie -lmysqlclient einmal, und speichern Sie die Datei.

Wenn Sie ./configure mit veränderten Optionen neu ausführen, müssen Sie vor make das Kommando make clean ausführen. Es entfernt die Ergebnisse der vorherigen Kompilation.

PHP 5 wurde damit in Verzeichnis /usr/local/php5 installiert. Der Ort für die Konfigurationsdatei php.ini ist /usr/local/php5/lib/. Per Default existiert diese Datei allerdings noch nicht, d.h., PHP 5 läuft mit den Defaulteinstellungen (die einstweilen ausreichen). Muster für *.ini-Dateien finden Sie in /usr/local/src/php-5.n.

Apache-Konfiguration ändern

Der nächste Schritt besteht darin, die Apache-Konfigurationsdatei /usr/local /apache2/conf/httpd.conf so zu ändern, dass Apache das PHP-5-Modul verwendet. Dazu laden Sie die Datei in einen Editor und fügen die fett markierte Zeilen hinzu:

# Änderungen in /usr/local/apache2/conf/httpd.conf  ...  LoadModule php5_module modules/libphp5.so  AddType application/x-httpd-php .php  ...  ### Section 2: ...  

Apache starten, testen und beenden

Um Apache samt PHP zu starten, führen Sie als root das folgende Kommando aus:

root# /usr/local/apache2/bin/apachectl start  

Um zu testen, ob Apache tatsächlich läuft, sehen Sie sich die Seite http://localhost auf Ihrem Webbrowser an. Um zu testen, ob auch PHP 5 funktioniert, erzeugen Sie die Testdatei /usr/local/apache2/htdocs/phptest.php. Diese Datei muss vom Apache-Account nobody lesbar sein.

<? phpinfo();  ?>  

Sehen Sie sich nun die Seite http://localhost/testphp.php auf Ihrem Webbrowser an. Das Ergebnis sollte wie in Abbildung aussehen.

Mit dem folgenden Kommando beenden Sie Apache wieder:

root# /usr/local/apache2/bin/apachectl stop  

Denken Sie daran, dass Änderungen an httpd.conf oder php.ini erst wirksam werden, nachdem Sie Apache neu gestartet haben!

Apache automatisch starten und beenden

Jetzt wäre es noch wünschenswert, dass Apache automatisch gestartet wird, wenn Sie Ihren Rechner einschalten, und automatisch beendet wird, wenn Sie den Rechner herunterfahren. Dazu müssen Sie die Datei apachectl in den Init-V-Prozess Ihrer Distribution einbauen. (Das Init-V-System steuert, wann welche Systemdienste gestartet bzw. gestoppt werden.)

Die folgenden Kommandos zeigen die Vorgehensweise für SUSE Linux:

root# cp /usr/local/apache2/bin/apachectl /etc/init.d/apache  root# insserv apache  

Bei Red Hat und Fedora gehen Sie so vor:

root# cp /usr/local/apache2/bin/apachectl /etc/rc.d/init.d/apache  root# chkconfig --add apache  root# chkconfig --level 35 apache on  

mysql-Datenbank zur Verwaltung der Zugriffsrechte einrichten

Bevor der MySQL-Server erstmals gestartet werden kann, muss die Datenbank mysql zur Verwaltung der MySQL-Zugriffsrechte erstellt werden. Die folgenden Zeilen gehen davon aus, dass die Datenbankdateien in /usr/local/mysql gespeichert werden sollen und dass der MySQL-Server den Account mysql nutzt. (Falls auf Ihrem Rechner bereits bisher MySQL gelaufen ist, können Sie dessen Datenbanken weiterverwenden. In diesem Fall verzichten Sie auf die beiden folgenden Kommandos.)

root# ./scripts/mysql_install_db --ldata=/usr/local/mysql  root# chown -R mysql /usr/local/mysql  

Konfigurationsdateien und Init-V-Script einrichten

Zum Start des MySQL-Servers benötigen Sie ein so genanntes Init-V-Script. In den MySQL-Quelldateien finden Sie mit support-files/mysql.server eine passende Vorlage. Zur Konfiguration verschiedener MySQL-Parameter dient die Datei /etc /my.cnf. Auch hierfür enthält das support-files-Verzeichnis eine Vorlage.

root# cp support-files/mysql.server /etc/init.d/mysql  root# cp support-files/my-medium.cnf /etc/my.cnf  

Jetzt müssen Sie in den beiden Dateien noch kleine Änderungen vornehmen, die in den folgenden Zeilen fett hervorgehoben sind. /etc/my.cnf steuert, welche Socket-Datei Client und Server für ihre lokale Kommunikation verwenden sollen. Per Default verwendet der MySQL-Server /tmp/mysql.sock, PHP ist aber meist auf /var/lib/mysql/mysql.sock eingestellt.

# Änderungen in /etc/my.cnf  ...  [client]  socket = /var/lib/mysql/mysql.sock  ...  [mysqld]  socket = /var/lib/mysql/mysql.sock  ...  

/etc/init.d/mysql ist für den Start des MySQL-Servers zuständig. Dort geben Sie an, wo sich die MySQL-Binärdateien und wo sich die MySQL-Datenbankdateien befinden. (Falls auf Ihrem Rechner bereits bisher MySQL gelaufen ist, können Sie dessen Datenbanken weiterverwenden. In diesem Fall stellen Sie datadir so ein, dass es auf das bisherige Datenbankverzeichnis zeigt. Im Regelfall ist das /var/lib /mysql.)

# Änderungen in /etc/init.d/mysql  ...  basedir=/usr/local  datadir=/usr/local/mysql  ...  

MySQL-Server starten

Um den MySQL-Server nun tatsächlich zu starten, führen Sie das folgende Kommando aus:

root# /etc/init.d/mysql start  

Wenn Sie möchten, dass der Server in Zukunft automatisch gestartet wird, wenn der Rechner hochgefahren wird, führen Sie eines der beiden folgenden Kommandos aus:

root# insserv mysql         (für SUSE)  root# chkconfig --add mysql (für Red Hat, Fedora, Mandrakelinux etc.)  

Die folgenden Abschnitte beschreiben die Einzelinstallation von Apache 2, PHP 5 und MySQL 4.1. Die Einzelinstallation hat den Vorteil, dass Sie die Version jeder Komponente selbst auswählen können und eher verstehen, welche Programme auf Ihrem Rechner installiert sind und wie sie konfiguriert werden. Das erleichtert auch die spätere Wartung bzw. das Update einer bestimmten Komponente.

Info

Apache 2.0 installieren IIS deaktivieren

Falls Sie auf einer Windows-Server-Version arbeiten, sollten Sie als Erstes sicherstellen, dass der Microsoft Internet Information Server (kurz IIS) nicht läuft. Die Installation von Apache in der Standardeinstellung (Port 80) scheitert, wenn IIS parallel läuft (wozu es auch selten einen Grund gibt).

Zur Kontrolle bzw. zur Deinstallation des IIS führen Sie EINSTELLUNGEN|SYSTEM-STEUERUNG|SOFTWARE|WINDOWS-KOMPONENTEN aus. Gegebenenfalls deaktivieren Sie im ASSISTENT FÜR WINDOWS-KOMPONENTEN die Option IIS, woraufhin das Programm gestoppt und deinstalliert wird.

Apache installieren

Nach der Installation wird Apache unter Windows NT/2000/XP automatisch als Service (Dienst) eingerichtet und sofort gestartet. Im rechten Teil der Taskleiste erscheint ein kleines Icon, das den Zustand von Apache anzeigt (ein grüner Pfeil, wenn das Programm läuft). Über dieses Icon können Sie Apache starten und stoppen.

Apache starten und stoppen

Sie können Apache mit den Menükommandos PROGRAMME|APACHE HTTP SERVER| CONTROL APACHE SERVER|START bzw. |STOP starten bzw. beenden. PROGRAMME| APACHE HTTP SERVER|CONFIGURE APACHE SERVER|EDIT HTTPD.CONF startet den Editor Notepad und zeigt darin die Apache-Konfigurationsdatei an.

Als ersten Test, ob Apache tatsächlich läuft, öffnen Sie in Ihrem Webbrowser die Seite http://localhost. Sie sollten die Defaultstartseite des Webbrowsers zu sehen bekommen. Abschließend noch zwei Verzeichnisangaben, die für eine Defaultinstallation gelten:

Konfigurationsdatei: C:\Programme\Apache Group\Apache2\conf\httpd.conf

Webdateien: C:\Programme\Apache Group\Apache2\htdocs

PHP 5.0 installieren

Die Installation umfasst die folgenden Schritte:

# Änderungen in C:\Programme\Apache Group\Apache2\conf\httpd.conf  ...  LoadModule php5_module "c:/php5/php5apache2.dll"  AddType application/x-httpd-php .php  PHPIniDir "C:/php5"  ...  

PHP ist nun als Erweiterung zu Apache installiert und wird zusammen mit Apache gestartet. Um zu testen, ob die Installation funktioniert hat, erstellen Sie im Verzeichnis C:\Programme\Apache Group\Apache2\htdocs mit einem Editor die Datei phptest.php mit dem folgenden Inhalt:

<?php phpinfo();  ?>  

Anschließend öffnen Sie mit einem Webbrowser die folgenden Seite http://localhost/phptest.php. Das Ergebnis sollte wie in Abbildung 1 aussehen.

MySQL 4.1 oder 5.0 installieren

MySQL 4.1 installieren

Nach der Installation des Servers in das Verzeichnis C:\Programme\MySQL\MySQL Server 4.1 fragt das Installationsprogramm, ob Sie einen Account bei mysql.com einrichten möchten. Diesen optionalen Schritt überspringen Sie im Regelfall einfach mit SKIP SIGN-UP.

MySQL-Konfigurationsassistent

Wesentlich wertvoller ist da schon der Konfigurationsassistent, der nach der Installation automatisch ausgeführt wird. Bei Bedarf können Sie diesen Assistenten später mit PROGRAMME|MYSQL|MYSQL SERVER 4.1|MYSQL SERVER INSTANCE CONFIG WIZARD neuerlich starten. Der Assistent erfüllt zwei Aufgaben: Er richtet die MySQL-Konfigurationsdatei ein und sichert den MySQL-Server durch ein Passwort ab. Die folgende Aufzählung beschreibt die wichtigsten Schritte des Assistenten, wenn Sie sich für die DETAILED CONFIGURATION entscheiden. (Sie machen nichts falsch, wenn Sie im Zweifelsfall immer den Defaultvorgaben des Assistenten folgen.)

Selbstverständlich können Sie später weitere Benutzer einrichten, die auf MySQL bzw. auf bestimmte MySQL-Datenbanken zugreifen dürfen.

MySQL 5.0 installieren

Vermutlich wird die Installation von MySQL 5.0 ab Version 5.0.2 genauso verlaufen wie eine 4.1-Installation. Die zuletzt verfügbare Version 5.0.1 verwendet allerdings noch ein älteres Installationsprogramm. Die wesentlichen Unterschiede:

Testen, ob MySQL läuft

Um zu testen, ob die MySQL-Installation erfolgreich war, starten Sie den MySQL-Kommandozeileninterpreter mysql.exe. Wenn Sie das neue Installationsprogramm eingesetzt haben (also ab MySQL 4.1.5/5.0.2), starten Sie mysql.exe einfach durch PROGRAMME|MYSQL|MYSQL SERVER 4.1|MYSQL COMMAND LINE CLIENT. Wenn Sie dagegen das alte Installationsprogramm verwendet haben (MySQL 4.0.n, MySQL 4.1.4 und früher, MySQL 5.0.1 und früher), öffnen Sie mit PROGRAMME|ZUBEHÖR| EINGABEAUFFORDERUNG ein Eingabeaufforderungsfenster (DOS-Fenster) und führen darin die folgenden Kommandos aus:

> CD "c:\Programme\MySQL\MySQL Server 4.1\bin"  > mysql -u root -p  Enter password: ********  

Dabei geben Sie dasselbe Passwort an, das Sie während der MySQL-Konfiguration verwendet haben. Wenn alles klappt, erscheint im Eingabefenster nun die MySQL-Eingabeaufforderung. Geben Sie als erstes Kommando status ein. Damit werden die wichtigsten Verbindungsparameter angezeigt. Mit exit beenden Sie den Kommandozeileninterpreter.

MySQL Administrator und Query Browser installieren

Grundsätzlich können Sie mit dem gerade vorgestellten Programm mysql.exe und einigen ähnlichen Werkzeugen (mysqldump.exe und mysqladmin.exe, um die beiden wichtigsten zu nennen) die gesamte Administration des MySQL-Servers durchführen. Besonders praktisch ist das aber nicht. Wesentlich mehr Komfort bieten die Programme MySQL Administrator und Query Browser.

Ein weiteres Administrationswerkzeug ist phpMyAdmin. Sein größter Vorteil gegenüber den hier erwähnten Programmen besteht darin, dass es über einen Webbrowser bedient wird und sich daher auch zur Administration des MySQL-Servers eignet, der auf einem anderen Rechner läuft (z.B. beim ISP).

MySQL-Extensions für PHP konfigurieren

Als letzter Schritt muss nun PHP so konfiguriert werden, dass es auf den MySQL-Server zugreifen kann. Hierfür ist es entscheidend, dass PHP zwei DLL-Dateien findet, die mit PHP mitgeliefert werden. Sie müssen die folgenden Schritte ausführen:

; Änderungen in c:\php5\php.ini  ...  extension_dir = "c:/php5/ext"  extension = php_mysql.dll  extension = php_mysqli.dll  

Um die richtigen Zeilen zu finden, verwenden Sie am besten BEARBEITEN|SUCHEN. Achten Sie darauf, dass als Trennzeichen für Verzeichnisse / verwendet wird, nicht wie unter Windows üblich \. Vor den Zeilen extension=... darf kein Strichpunkt stehen – andernfalls gilt die Zeile als Kommentar und wird ignoriert.

Sollten Probleme auftreten, laden Sie in Ihrem Webbrowser nochmals die PHP-Testseite. Kontrollieren Sie, ob der Dateiname in der Zeile Configuration Path (php.ini) File (fünfte Zeile) tatsächlich C:\php5\php.ini lautet. Wenn das nicht der Fall ist, kopieren Sie Ihre php.ini-Datei an den Ort, den die Testseite angibt. (Der wahrscheinlichste Konfigurationsfehler besteht erfahrungsgemäß darin, dass PHP die Konfigurationsdatei an einem anderen Ort erwartet als Sie. Das ist dann der Grund, weswegen Ihre Änderungen in php.ini einfach ignoriert werden …)

So lange man nur ein paar kleine Probescripts schreibt, fällt es schwer, sich vorzustellen, wie komplex – und mitunter ausufernd lang – ein Script sein kann. Wenn dann noch die nicht ungewöhnliche Situation auftritt, dass man sich Monate nach der ursprünglichen Programmierung erneut an den Programmcode setzt, um irgendetwas zu ändern oder zu verbessern, wäre man ohne erklärende Kommentare im Script mitunter verloren. Haben Sie hingegen mit aussagekräftigen Kommentaren gearbeitet, können Sie sehr viel einfacher rekonstruieren, was jeweils angewiesen wurde und warum Sie den Code so und nicht anders geschrieben haben.

Kommentare im PHP-Code werden nicht übertragen. Der Parser (der die Befehle ausführt) ignoriert die Zeile(n) einfach. Sie können Kommentare also auch getrost als „Notizzettel“, die nur für Sie selbst gedacht sind, nutzen. Es gibt zwei Methoden, eine Programmzeile im Script zu kommentieren. Entweder Sie schreiben // oder # an den Anfang der Zeile, die lediglich ein Kommentar sein soll. Das sieht beispielsweise so aus:

//Fehlermeldung zusammenbauen  If(!$name){$fehler="Bitte geben Sie einen Namen ein <br>";}  

Um eine Programmzeile direkt zu kommentieren, können Sie den Kommentar auch an das Ende der Zeile schreiben:

$name=""; // löscht den eingegebenen Wert  

Benutzen Sie am Anfang des Kommentarteils das Zeichen /* und am Ende */, wird der PHP-Prozessor alles ignorieren, was zwischen diesen Zeichen steht, ob nur eine Zeile oder auch mehrere:

<?php  /*  echo "<b>Heute ist Montag, willkommen auf unserer Seite</b>";  */  ?>  

Würden Sie diesen Code speichern und die Datei im Browser aufrufen, würden Sie eine leere Seite sehen, weil ja nichts ausgegeben wird. Deswegen ersparen wir Ihnen hier ein Bild! Info Manche Editoren verwenden für Kommentare eine andere Farbe als die, die sonst im Script benutzt wird: Dies ist, wie Sie sich denken können, vor allem bei langen Scripts sehr hilfreich.

Bevor wir uns genauer mit der PHP-Programmierung beschäftigen, ist es nötig, einen Blick in die zentrale Konfigurationsdatei von PHP, php.ini, zu werfen. Jeder PHP-Programmierer sollte zumindest mit den wichtigsten Einträgen in dieser Datei vertraut sein. Denn sie beeinflussen das Verhalten von PHP zum Teil erheblich.

Auf Linux-Systemen wird die php.ini gerne unter /etc abgelegt. Auf Windows-Systemen sollte sie sich im Windows-Verzeichnis befinden. Bearbeitet werden kann sie mit jedem Texteditor.

Die Optionen in der php.ini haben das Format Bezeichner = Wert. Alle Zeilen, die mit einem Semikolon beginnen, sind Kommentarzeilen. Wenn Sie die php.ini wie empfohlen durch Kopieren der php.ini-dist erzeugt haben, dann finden Sie eine reichhaltig auskommentierte Datei vor. Die Kommentare unterstützen beim Verständnis der einzelnen Konfigurationseinstellungen.

Änderungsberechtigungen

Es gibt Möglichkeiten, die Einträge in der php.ini zu überschreiben, und zwar:

Für jede Option in der php.ini ist daher in PHP festgelegt,

ob und wo der zugewiesene Wert überschreiben darf. Dafür gibt es folgende vier Berechtigungsklassen:

Wenn PHP im Apache Webserver als Modul installiert ist, können Sie PHP-Optionen in der Apache-Konfiguration mithilfe der Direktive php_value notieren. Beispiel:

php_value variables_order "EPGCS"  

Info Diese Direktive, in einer .htaccess-Datei notiert, bewirkt unter anderem, dass PHP in dem entsprechenden Verzeichnis die Daten über den POST-Kanal schneller ermittelt als die über den GET-Kanal.

Für die Praxis sind die Änderungsberechtigungen nur dann von Bedeutung, wenn Sie solche Feinjustierungen in der Apache-Konfiguration vornehmen möchten. PHP-Scripts dürfen alle Optionen überschreiben. PHP_INI_USER ist also die Mindestberechtigung aller Optionen.

Optionen für Sprachverhalten

Diese Optionen regeln allgemeine Einstellungen zu PHP. In der php.ini finden Sie die Einstellungen unterhalb von:

;;;;;;;;;;;;;;;;;;;; ; Language Options ; ;;;;;;;;;;;;;;;;;;;;Nachfolgende Tabelle beschreibt einige wichtige Optionen:

Option Default-Wert Bedeutung Berechtigung
engine On Nur bei On funktioniert die Einbindung von PHP unter Apache.
short_open_tag On Wenn On, dürfen Sie PHP-Code in <? … ?> einschließen. Wenn Off, müssen Sie <?php … ?> notieren. Letzteres ist auf jeden Fall vorzuziehen, wenn Sie mit PHP XML verarbeiten wollen. PERDIR, SYSTEM
asp_tags Off Wenn On, kann PHP so wie ASP innerhalb von <% … %> notiert werden. PERDIR, SYSTEM
precision 14 Anzahl der ausgegebenen Stellen bei Fließkommazahlen. ALL
y2k_compliance On Jahr-2000-Kompatibilität. ALL

Optionen für Ressourcenbelegung

Diese Optionen regeln, wie stark PHP das System, auf dem es läuft, belasten darf. In der php.ini finden Sie die Einstellungen unterhalb von:

;;;;;;;;;;;;;;;;;;; ; Resource Limits ; ;;;;;;;;;;;;;;;;;;;Nachfolgende Tabelle beschreibt diese Optionen:

Option Default-Wert Bedeutung Berechtigung
max_execution_time 30 Wenn ein Script zur Ausführung mehr Sekunden benötigt als angegeben, wird es vom Parser abgebrochen.
max_input_time 60 Wenn ein Script mehr Sekunden als angegeben keinen Input z.B. über GET oder POST erhält, wird es vom Parser abgebrochen.
memory_limits 8M Arbeitsspeicher, den ein Script maximal belegen darf. Integer-Wert mit M dahinter für Megabyte, oder -1 für „keine Begrenzung“. ALL

Optionen für Fehlerbehandlung

Diese Optionen regeln, wie PHP bei Syntaxfehlern oder problematischem Code reagieren soll. Gerade während der Entwicklungsphase von größeren Scripts ist es sinnvoll, die Fehleranzeige und das Fehler-Logging etwas großzügiger einzustellen. In Produktivumgebungen dagegen sollten Fehler überhaupt nicht angezeigt werden. In der php.ini finden Sie die Einstellungen unterhalb von:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Nachfolgende Tabelle beschreibt die wichtigsten dieser Optionen:

Option Default-Wert Bedeutung Berechtigung
error_reporting E_ALL & ~E_NOTICE Bestimmt, welche Vorkommnisse behandelt werden. Mögliche Werte und Beispiele siehe Kommentare in php.ini. ALL
display_errors On Gibt Fehler im Browser aus. Sollte bei Produktivumgebungen auf Off gestellt werden. ALL
display_startup_errors Off Gibt bei On Fehler beim Interpreter-Start aus. ALL
log_errors Off Protokolliert Fehler in der Logdatei statt sie im Browser auszugeben. Empfehlenswert für Produktivumgebungen. Die Logdatei kann bei error_log festgelegt werden. ALL
log_errors_max_len 1024 Länge für Fehlermeldungen begrenzen (Anzahl Byte). 0 bedeutet: keine Begrenzung. ALL
ignore_repeated_errors Off Wenn eine PHP-Datei eine Meldung mehrmals produziert, wird sie nur einmal protokolliert. ALL
ignore_repeated_source Off Wenn eine PHP-Datei eine Meldung mehrmals produziert, wird die betroffene Quelltextumgebung nur einmal protokolliert. ALL
report_memleaks On Fehler bei der Speicherfreigabe werden ausgegeben bzw. protokolliert. ALL
track_errors Off Die jeweils letzte Meldung steht dem Script selbst in einer Variablen namens $php_errormsg zur Verfügung. ALL
html_errors On HTML-Markup in Meldungsausgaben verwenden. ALL
error_log Absoluter Pfadname der gewünschten Logdatei. Der Webserver muss für diese Datei Schreibrechte haben. ALL

Optionen für Datenbehandlung

Diese Optionen regeln wichtige Voreinstellungen, wie PHP mit Daten umgeht. In der php.ini finden Sie die Einstellungen unterhalb von:

;;;;;;;;;;;;;;;;; ; Data Handling ; ;;;;;;;;;;;;;;;;;Nachfolgende Tabelle beschreibt die wichtigsten dieser Optionen:

Option Default-Wert Bedeutung Berechtigung
track-vars On Wenn On, stehen GET- und POST-Daten, Umgebungsvariablen, Servervariablen und Cookies in so genannten Superglobal-Arrays zur Verfügung. Sollte unbedingt auf On stehen und die Superglobals sollten heutzutage anstelle älterer Lösungen für entsprechende Datenzugriffe verwendet werden.
variables_order „EGPCS“ Reihenfolge der Variablen beim Parsen: E = Umgebungsvariablen, G = GET-Daten, P = POST-Daten, C = Cookie-Daten, S = Servervariablen. ALL
register_globals Off Wenn On, stehen GET- und POST-Daten, Umgebungsvariablen, Servervariablen und Cookies in bestimmten, normalen Variablen zur Verfügung. Sollte heutzutage nicht mehr verwendet werden, also auf Off stehen. PERDIR, SYSTEM
post_max_size 8M Integer-Wert mit M dahinter für Megabyte. Maximaler Umfang von Daten, die mittels POST-Methode an ein Script übermittelt werden können. Interessant vor allem, wenn Datei-Uploads verarbeitet werden müssen. PERDIR, SYSTEM

Diverse Optionen

Die php.ini enthält zahlreiche weitere Optionen, die zum Teil das Zusammenspiel zwischen PHP und bestimmten anderen Softwareprodukten regeln. Viele davon werden in der Praxis nur sehr selten angefasst. Es gibt jedoch auch Optionen, die Sie möglicherweise ändern oder zumindest kennen sollten.

Nachfolgend eine Liste solcher Optionen:

Option Default-Wert Bedeutung Berechtigung
doc_root Nur von Bedeutung, wenn PHP im CGI-Modus läuft. In diesem Fall das Stammverzeichnis für PHP-Scripts als absolute Pfadangabe. SYSTEM
file_uploads On Wenn On, werden Inhalte aus File-Upload-Feldern in HTML an PHP-Scripts übertragen, wenn Off, dann nicht. PERDIR, SYSTEM
upload_max_filesize 2M Integer-Wert mit M dahinter für Megabyte. Wenn file_uploads auf On steht, kann hier die maximale Größe notiert werden, die hochgeladene Dateien haben dürfen. PERDIR, SYSTEM
allow_url_fopen On Wenn On, dürfen Scripts die Methoden zum Öffnen von Dateien auch auf URIs anwenden. PHP stellt dann im Hintergrund eine Socketverbindung her und lädt die URI-Inhalte wie normale Dateien. SYSTEM
SMTP Bei lokalen PHP-Installationen unter Windows von Bedeutung. IP-Adresse des Servers für ausgehende Mails. Wird für Mailversand aus PHP benötigt. ALL
session.save_handler files Name einer Funktion, die Session-Dateien speichern kann. Sollte auf files stehen bleiben. ALL
session.save_path „/tmp“ Absoluter Pfadname des Verzeichnisses, unter dem Session-Dateien gespeichert werden. Es sollte sich um ein typisches Verzeichnis für temporäre Daten handeln. Muss unter MS Windows in der Regel geändert werden, da dort /tmp üblicherweise nicht existiert. ALL
session.name „PHPSESSID“ Default-Name für Session-Cookies. ALL
session.use_cookies On Wenn On, versucht PHP für Session-Daten ein Session-Cookie an den Browser zu senden. ALL
session.use_ only_cookies Off Wenn On, funktionieren Sessions nur, wenn der Anwender Session-Cookies akzeptiert. Scripts laufen dann möglicherweise nicht wie gewünscht, wenn der Anwender keine Cookies akzeptiert. Dafür wird aber dem Ausspähen von Session-Daten vorgebeugt, was auch im Interesse des Anwenders ist. ALL
session.cache_expire 180 Anzahl Minuten, die Session-Seiten im Cache-Speicher bleiben. ALL
session.use_trans_sid Off Die Session-ID wird an den GET-String angehängt, wodurch sich Sessions via URI ansprechen lassen. Sollte normalerweise auf Off stehen, da es das Ausspähen von Session-Daten erleichtert. ALL

Für einige der genannten Optionen, vor allem diejenigen zu Sessions, sind Kenntnisse in PHP-Sessions erforderlich. Wir werden dieses wichtige Thema im vorliegenden Kapitel behandeln.

Die Funktion phpinfo()

<?php phpinfo(); ?>  

Rufen Sie diese Datei via HTTP im Browser auf.

PHP ist eine Programmier- und Script-Sprache. Bis einschließlich Version 4.x gehört PHP zu den prozeduralen Programmiersprachen, in denen eine Anweisungsreihenfolge existiert, die abgearbeitet wird. Ab Version 5.0 kann PHP zusätzlich auch zu den objektorientierten Sprachen gezählt werden, die regulierbare Datenkapselung bei Objekten und Vererbung beherrscht. PHP bietet beide Formen der Programmierung an.

PHP-Code wird im üblichen Betrieb jedoch nicht in statische, für das Betriebssystem ausführbare Dateien übersetzt, sondern von einem Interpreter zur Laufzeit „on the fly“ kompiliert und ausgeführt. Insofern gehört es zur Familie der Script-Sprachen. Für den Programmierer entfällt also das zeitraubende Neukompilieren vor jedem neuen Austesten.

PHP entbindet den Programmierer wie alle Script-Sprachen auch davon,

sich um die Arbeitsspeicherverwaltung selbst zu kümmern. Die einzige Schnittstelle in dieser Hinsicht bietet PHP in seinen Konfigurationseinstellungen an. Dort lässt sich angeben, wie viel Speicher PHP für die Ausführung eines Scripts maximal reservieren darf.

PHP ist zwar mittlerweile auch ähnlich wie Perl als Script-Sprache auf Konsolenebene einsetzbar. Doch der eigentliche und konsequent verfolgte Einsatzzweck ist der als Script-Umgebung eines Webservers. PHP kann in HTML eingebettet werden und PHP-Dateien können wie HTML-Dateien in allen Webverzeichnissen abgelegt und ausgeführt werden (sofern es im Webserver als Modul eingebunden wird und nicht nur für CGI-Scripts erlaubt ist). PHP hat eine besonders enge Anbindung an die Gegebenheiten von HTML und HTTP. Viele vordefinierte Funktionen übernehmen Aufgaben, die speziell der Webprogrammierung entgegen kommen.

Geschichte und heutige Bedeutung von PHP

Die Geschichte von PHP reicht bis ins Jahr 1995 zurück. Der Aufstieg von einem kleinen Toolset zu einer ernst zu nehmenden Technologie begann 1998 auf Basis der Version 3.0. Damit wuchs allerdings auch der Druck auf Stabilität und Features der Script-Sprache.

Für die Sprachversion 4.0 wurde der Script-Interpreter von Grund auf neu entwickelt. Sein neuer Kern war die so genannte Zend Engine (ein Akronym aus den Vornamen der Chef-Entwickler Zeev Suraski und Andi Gutmans). Schon in der langen Beta-Phase zog der neue Script-Interpreter immer mehr interessierte Webprogrammierer in seinen Bann. Nachdem PHP 4.0 im Mai 2000 offiziell freigegeben wurde, sorgte eine für ein Open-Source-Projekt wirklich ungewöhnliche PR-Maschine dafür, dass PHP in Sachen Webprogrammierung alsbald zum Maß aller Dinge wurde und zuvor dominierende Sprachen wie Perl aus diesem Bereich weitgehend verdrängte.

Auch Microsoft hat PHP bis heute keine Konkurrenz machen können.

Das .NET-Framework, Nachfolger der Active Server Pages (ASP), ist den meisten Webprogrammierern zu komplex und zu proprietär. Andere Konkurrenten, wie etwa die Java Server Pages (JSP), haben sich bestimmte Nischen gesichert, vor allem bei Banken und Großunternehmen. Dort wird Java auch für andere Zwecke verwendet und es haben immer noch Entscheider das Sagen, die einem kommerziellem Anbieter (Java wird von Sun Microsystems vertrieben) mehr vertrauen als einem Open-Source-Projekt wie PHP.

Zeichenketten werden – wie Sie gesehen haben – in Anführungszeichen gesetzt. Das bringt offensichtlich Probleme mit sich, wenn ein Textstück tatsächlich in Anführungszeichen ausgegeben werden soll. Als Lösung können Sie zwei Methoden anwenden. Entweder Sie verwenden doppelte und einfache Anführungszeichen, beispielsweise doppelte Anführungszeichen für die Zeichenkette und einfache für das Textstück (oder vice versa):

"ich lerne 'PHP'"; oder 'ich lerne "PHP"';  

oder Sie benutzen als ein Maskierungszeichen den Backslash. Dieser wird vor die Anführungszeichen für das Textstück gesetzt. Damit sagen Sie PHP, dass die Anführungszeichen ausgegeben, aber nicht als Beginn oder Ende einer Anweisung interpretiert werden sollen (deswegen: Maskierung):

"ich lerne \"PHP\"";  

Wenn Sie mit HTML vertraut sind, wissen Sie, dass hier oft Anführungszeichen gesetzt werden/gesetzt werden sollen, denn Sie schreiben ja beispielsweise <font color="red">. Diese Anführungszeichen müssen natürlich auch maskiert werden, wenn sie Teil einer PHP-Anweisung sind. Das sieht dann so aus:

echo "<font color=\"red\">";  

Wenn nun tatsächlich ein Backslash angezeigt werden soll, brauchen Sie einen zweiten:

echo "c:\\programme";  

Auch für Zeilenumbrüche in PHP (nicht zu verwechseln mit dem <br>-HTML-Tag) gibt es ein Zeichen mit einem Backslash: \n.

Die folgende Tabelle gibt einen Überblick über die Zeichen, die Sie verwenden können, damit bestimmte Zeichen ausgegeben werden.

Zeichenfolge Effekt
\n neue Zeile
\r Wagenrücklauf
\t horizontaler Tabulator
\\ Backflash
\$ Dollarsymbol
\" doppelte Anführungszeichen

Mit PHP war ursprünglich einmal „Personal Homepage“ gemeint. Das war im Jahr 1994, als PHP von Rasmus Lerdorf kreiert wurde. Über die Folgejahre wuchsen die Fähigkeiten von PHP und damit änderte sich auch die Bedeutung dieses Kürzels – passenderweise ohne dass ein Buchstabe unbedingt geändert werden musste – in die klangvolle Bezeichnung Hypertext Preprocessor.

Darin steckt schon im Ansatz die „Arbeit“ von PHP: Es verarbeitet Daten, bevor sie vom Webserver – üblicherweise in HTML (HyperText Markup Language) ausgegeben – zum Browser des Clients wandern.

Spezielle Merkmale von PHP

  • Im Gegensatz zu diversen anderen Programmiersprachen, die clientseitig, also auf dem Rechner des Users ablaufen, führt PHP seine Anweisungen serverseitig, d.h. direkt auf dem Server aus. Die Ergebnisse der Verarbeitung werden – in der Regel – im Browser angezeigt. Wie PHP arbeitet, betrachten wir weiter hinten noch etwas genauer.
  • PHP ist plattformübergreifend. Dies heißt, es ist auf allen gängigen Linux- und Windows-Versionen lauffähig und funktioniert auch mit Macintosh und OS/2. Angenehm dabei ist, dass die Scripts im Gegensatz zu den meisten anderen Programmiersprachen ohne große Mühe an eine andere Plattform angepasst werden können, sollte dies notwendig sein. Die Plattformunabhängigkeit bedeutet beim Einsatz im Internet nicht, dass Sie die Scripts für die Clients, die die PHP-Webseiten im Browser anschauen, je nach Betriebssystem ändern müssen, sondern dass geringe Änderungen notwendig sind, wenn auf dem Webserver das Betriebssystem gewechselt wird.
  • Der PHP-Code kann direkt in HTML-Seiten integriert werden. Allein durch die Endung .php oder .php3, mit der die Dokumente gespeichert werden, kann der Server erkennen, dass es sich um ein PHP-Script handelt. Die offizielle PHP-Website spricht von PHP als einer „HTML embedded scripting language“. Der Begriff embedded, der ja mit eingebettet übersetzt wird, macht recht schön deutlich, wie die HTLM-Codes und PHP-Codes zusammengehen.
  • Eine weitere Besonderheit von PHP ist, dass es, wie eben schon zitiert, eine Scripting Language ist und keine Programmiersprache im eigentlichen Sinn. Dies bedeutet, PHP wird nur „aktiv“, wenn ein Ereignis eingetreten ist, z.B. wenn eine Webseite (ein URL) aufgerufen wird oder wenn auf einer Webseite ein Formular von einem User ausgefüllt und dieses Formular abgeschickt wurde. Andere Programmiersprachen funktionieren im Gegensatz dazu stand-alone (Compiler), d.h., sie agieren von sich aus, teilweise das Web involvierend, teilweise nicht. JavaScript hingegen ist ebenfalls eine Scripting Language und von daher in gewisser Weise mit PHP vergleichbar, obwohl es meist nicht serverseitig, sondern clientseitig eingesetzt wird.
  • Es gibt kaum Unterschiede zwischen PHP3 und PHP4, obwohl in PHP4 durchaus einige nützliche Funktionen hinzugekommen sind und PHP etwas leistungsstärker geworden ist.

Was kann und macht PHP?

Die erste Möglichkeit, die in den Sinn kommt, wenn man über die Erstellung von Webseiten nachdenkt, ist HTML. HTML ermöglicht jedoch keine Flexibilität und/oder Dynamik auf einer Seite. Besucher einer HTML-Seite kommen weder in den Genuss, spezifische Reaktionen hervorrufen zu können, noch kann die Seite in irgendeiner Form angepasst werden. Durch diese Beschränkung ist reines HTML keine Alternative zu PHP, mit dem Sie genau das machen können – wobei Sie auch beim Einsatz von PHP nicht auf HTML verzichten können, um Ausgaben an den Browser zu senden. Mit PHP erstellen Sie dynamische Webseiten, auf denen Sie alle möglichen speziellen Gegebenheiten und Interaktionen mit dem User einbauen, auf Eingaben reagieren können, für regelmäßige Updates sorgen etc. Außerdem – und dies ist ein besonders hervorzuhebendes Feature – arbeitet PHP sehr gut mit einer Vielzahl von Datenbanken, mit dem Datei- und Verzeichnissystem und E-Mails zusammen, sodass sich Informationen speichern und weiterverarbeiten lassen (was ein enorm wichtiger Aspekt bei der Verwendung von Webseiten – für welche Zwecke auch immer – ist, denn es geht ja vielfach um die Generierung von Daten und Informationen, z.B. Adressen, Bestelldaten etc).

Abgesehen von HTML, das auf Grund seiner Grenzen keine Konkurrenz zu PHP ist,

gibt es natürlich Alternativen, denn die Webadministratoren und Fachleute wissen ja schon längst, dass sich mit HTML keine dynamischen Seiten basteln lassen. Daher haben in den letzten Jahren auch andere serverseitige Technologien an Bedeutung gewonnen und sind mehr und mehr eingesetzt worden. Hier ist beispielsweise Perl zu erwähnen oder ASP (Active Server Pages) und (mit Einschränkungen) auch JavaScript.

Im Vergleich bietet PHP gegenüber den erwähnten Programmiersprachen einige deutliche Vorteile, die schnell auf den Punkt gebracht sind:

  • PHP ist schneller zu programmieren und schneller bei der Ausführung von Scripts.
  • Der zweite gewichtige Unterschied, der vor allem für den Einsteiger (in die Welt des Programmierens) von Interesse ist, ist der, dass es leichter zu lernen ist und keine formalen Programmiervorkenntnisse erfordert. Für ASP sind beispielsweise Erfahrungen mit VB-Scripts mehr oder minder zwingend, Perl und C erfordern relativ lange Einarbeitungszeiten. Dabei handelt es sich um komplexe Sprachen, mit denen man sich nicht mal eben vertraut machen kann! Dies gilt zwar auch für PHP, aber nicht allzu komplizierte Aufgaben lernt man relativ schnell per PHP zu bewältigen; dies werden Sie feststellen, sobald Sie mithilfe dieses Kapitels Ihre ersten Gehversuche unternommen haben. Eine kleine dynamische Seite, mit der Sie beispielsweise Webseitenbesucher je nach Tageszeit oder Wochentag mit wechselnden Texten begrüßen, ist schnell erstellt. Und das ist doch schon etwas!
  • Nicht zuletzt spielt es eine Rolle, dass im Prinzip nur PHP speziell für die Programmierung dynamischer Webseiten entwickelt wurde. Dies bedeutet, dass PHP bestimmte Aufgaben besonders gut und schneller lösen kann als die Alternativen, nicht alle, aber insbesondere die, für die es geschrieben wurde. Dies macht die Sprache nicht unbedingt besser als ASP oder Perl, aber in mancher Hinsicht einfach adäquater. Überdies kann man PHP mit anderen Sprachen kombinieren, was seine Funktionalität natürlich erhöht.

Funktionsweise von PHP

Wir wollen nicht in die Einzelheiten gehen! Es ist aber nicht verkehrt, eine gewisse Vorstellung davon zu haben, in welcher Form PHP arbeitet.

Voraussetzung ist das Zusammenspiel von Client, Server und PHP. PHP arbeitet – das wurde eingangs bereits erwähnt – serverseitig. Dies heißt vereinfacht: Alles, was PHP macht, geschieht auf dem Server. Wenn Sie eine PHP-Datei erstellen und auf den Server hochladen (bzw. selbst am Server arbeiten), erkennt der Server anhand der Endung, dass es sich um ein PHP-Dokument handelt und schickt es an den PHP-Interpreter. Der führt die Anweisungen aus und sendet das Ergebnis, also die passenden Informationen (HTML-Ausgaben des Scripts), zurück an den Webserver und dieser schickt es an den Browser.

Dies unterscheidet sich von HTML-generierten Seiten, wo der Client lediglich eine URL-Anfrage an den Server stellt und der Server die Anfrage direkt beantwortet, indem die Seite an den Browser des Clients geschickt wird. Bei diesem Prozess ist keinerlei serverseitige Interpretation involviert. Für den User, der eine Seite aufgerufen hat und sie im Browser betrachtet, ist im Prinzip nicht ersichtlich, ob ein PHP-Script ausgeführt wurde oder nicht.

Dieser Artikel geht auf die Frage ein, welchen Zeichensatz Sie für die Entwicklung Ihrer Webanwendungen verwenden sollen. Diese Frage ist relativ elementar, und sie sollte beantwortet werden, bevor Sie mit der Entwicklung eines größeren Projekts beginnen. (Eine nachträgliche Änderung des Zeichensatzes ist mühsam.)

Um das Thema hier nicht ausarten zu lassen, beschränkt sich der Abschnitt auf die beiden in unserem Sprachraum wichtigsten Zeichensätze, nämlich auf latin1 alias ISO-8859-1 und auf Unicode UTF-8 alias ISO-10646.

Vor den vielen Details als eine Art Kurzfassung des Abschnitts zwei Tipps:

  • Wenn für Ihre Anwendung der latin1-Zeichensatz ausreicht (und das wird für den Großteil der im deutschen Sprachraum entwickelten Anwendungen der Fall sein), verwenden Sie diesen Zeichensatz. Er verursacht bei weitem die geringsten Probleme.
  • Wenn Sie Unicode einsetzen (müssen), gehen Sie dabei möglichst konsequent vor. Nutzen Sie alle zur Verfügung stehenden Unicode-Möglichkeiten sämtlicher Glieder der Entwicklungskette, also Betriebssystem, Webserver, PHP, Datenbank, Editor bzw. Entwicklungswerkzeuge etc.

Theoretisch ist es auch möglich, in unterschiedlichen Ebenen der Entwicklung bzw. Datenverwaltung unterschiedliche Zeichensätze einzusetzen. Die Wahrscheinlichkeit, dass dann an irgendeinem Punkt beim Übergang von latin1 zu Unicode (oder umgekehrt) Probleme auftreten, beträgt aber nahezu 100 Prozent.

Zeichensatzgrundlagen

Zeichensätze bestimmen, welche Codes zur Darstellung von Zeichen verwendet werden. Bei den 128 US-ASCII-Zeichen sind sich die meisten Zeichensätze einig (z.B. Code 65 für den Buchstaben A). Problematischer ist die Darstellung internationaler Zeichen.

latin-Zeichensätze:

In der Vergangenheit wurden je nach Sprachraum verschiedene 1-Byte-Zeichensätze entwickelt, von denen die latin-Zeichensätze die größte Verbreitung gefunden haben: latin1 alias ISO-8859-1 enthält alle in Westeuropa üblichen Zeichen (äöüßáàå etc.)., latin2 alias ISO-8859-2 Zeichen aus Zentral- und Osteuropa etc. latin0 alias latin9 alias ISO-8859-15 entspricht latin1, enthält aber zusätzlich das Euro-Zeichen.

Die latin-Zeichensätze werden sowohl von Unix/Linux als auch von aktuellen Windows-Versionen unterstützt. Bei älteren Windows-Versionen kann ersatzweise die codepage 1252 verwendet werden (kurz CP 1252, manchmal auch ANSI-Zeichensatz genannt), die bis auf wenige Abweichungen dem latin1-Zeichensatz entspricht.

Das Problem bei diesen Zeichensätzen ist offensichtlich: Ihre Anwendung kommt nie mit allen Zeichen aus ganz Europa zurecht, weil jeder latin-Zeichensatz nur eine Teilmenge der Zeichen enthält.

Unicode-Varianten

Als Lösung wurde der 2-Byte-Zeichensatz Unicode entwickelt. Mit 65.535 möglichen Zeichen deckt er nicht nur alle Zeichen ganz Europas ab, sondern darüber hinaus auch noch die der meisten asiatischen Sprachen.

Unicode regelt allerdings nur, welcher Code welchem Zeichen zugeordnet ist, nicht, wie die Codes tatsächlich gespeichert werden. Hierfür bestehen wieder mehrere Varianten, von denen UCS-2 und UTF-8 die beiden wichtigsten sind. (UTF steht für Unicode Transfer Format, UCS für Universal Character Set.)

  • UCS-2 alias UTF-16: Die einfachste Lösung scheint auf den ersten Blick darin zu bestehen, jedes Zeichen einfach durch 2 Byte (also 16 Bit) darzustellen. Diese Formatierung wird UTF-16 oder UCS-2 genannt. Fast alle Betriebssystemfunktionen von Microsoft Windows verwenden diese Darstellung. Sie hat allerdings zwei Nachteile: Erstens verdoppelt sich der Speicherbedarf, und zwar auch in solchen Fällen, wo überwiegend europäische Zeichen oder gar nur US-ASCII-Zeichen gespeichert werden sollen. Zweitens tritt der Bytecode 0 an beliebigen Stellen in Unicode-Zeichenketten auf. Bei Texten mit US-ASCII-Zeichen ist sogar jedes 2. Byte 0. Viele C-Programme, E-Mail-Server etc. setzen aber voraus, dass das Byte 0 das Ende einer Zeichenkette markiert.
  • UTF-8: Die bei weitem populärste Alternative zu UTF-16 ist UTF-8. Dabei werden die US-ASCII-Zeichen (7 Bit) wie bisher durch ein Byte dargestellt, deren oberstes Bit 0 ist. Alle anderen Unicode-Zeichen werden durch zwei bis vier Byte lange Byte-Ketten dargestellt. Der offensichtliche Nachteil dieses Formats besteht darin, dass es keinen unmittelbaren Zusammenhang zwischen der Byteanzahl und der Anzahl der Zeichen eines Dokuments gibt. Wegen der größeren Kompatibilität zu existierenden Programmen und einer Reihe anderer Vorteile hat sich UTF-8 unter Unix/Linux und bei den meisten für die Webentwicklung wichtigen Komponenten als Standard etabliert. Wenn von Unicode die Rede ist, ist in Zukunft immer Unicode im UTF-8-Format gemeint.

Trotz der offensichtlichen Vorteile von Unicode – egal, in welcher Darstellung – gibt es zwei Gründe, die gegen den sofortigen Umstieg sprechen: Zum einen ist der Unicode-Zeichensatz inkompatibel mit den bekannten 1-Byte-Zeichensätzen; vorhandene Datenbestände und Codedateien müssen also konvertiert werden. Zum anderen ist die Unicode-Unterstützung der Komponenten, die bei der Webentwicklung zum Einsatz kommen, noch alles andere als perfekt.

Zeichensatzunterstützung in Apache, PHP und MySQL

Dieser Abschnitt behandelt im Folgenden die gesamte Kette der Werkzeuge und Programme, vom Apache-Server bis zum Webbrowser auf dem Client. Der Abschnitt geht auch auf das Format HTML und das Protokoll HTTP sowie auf die Zeichensatzkonfiguration unter Windows und Linux ein.

HTML

Laut HTML-Standard gilt für alle Dokumente ohne explizite Zeichensatzangabe der Zeichensatz iso-8859-1 (also latin1). Den verwendeten Zeichensatz können Sie am Beginn von HTML-Dokumenten angeben. Im HTML-Code sieht das so aus:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  <html>  <head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  ...  

Zulässige charset-Einstellungen sind utf-8, iso-8859-1, iso-8859-15 etc. Das Problem besteht allerdings darin, dass viele Webbrowser diese Information schlicht ignorieren und sich stattdessen auf die HTTP-Header-Daten verlassen (siehe unten).

HTML-Codes für Sonderzeichen

Wenn Sie möchten, dass internationale Sonderzeichen auch dann richtig dargestellt werden, wenn der Webbrowser die Zeichensatzinformation nicht oder falsch auswertet, können Sie für viele Sonderzeichen spezielle HTML-Codes einsetzen, beispielsweise &auml; statt a. Das macht Texte in HTML-Dokumenten allerdings schwer zu lesen. Bei Ausgaben, die Sie mit PHP durchführen, setzen Sie einfach die Funktion htmlentities ein. (htmlentities('aäb') liefert die Zeichenkette a&auml;b). Info Wenn Sie als Zeichensatz latin1 bzw. iso-8859-1 nutzen, können Sie zwar Sonderzeichen wie äöüß direkt im HTML-Code verwenden, nicht aber das Euro-Zeichen. Um das Euro-Zeichen dennoch fehlerfrei auszugeben, verwenden Sie einfach den HTML-Code &euro;!

Formulare

Normalerweise werden in ein HTML-Formular eingegebene Texte in dem Zeichensatz versendet, in dem die HTML-Seite dargestellt wird. Hat also beispielsweise die Erkennung des Unicodes-Zeichensatzes geklappt, dann sollten auch die eingegebenen Zeichen als Unicode versandt werden.

Was aber, wenn dies bei alten Browsern nicht zuverlässig funktioniert, wenn die Zeichensatzerkennung im Browser fehlgeschlagen ist oder im Browser ein bestimmter Zeichensatz fest eingestellt ist? Dann werden die Texteingaben aus dem Formular in einem anderen Zeichensatz zurückgeliefert und in der Folge falsch gespeichert.

Das folgende PDF-Dokument (die Zusammenfassung eines Vortrags über Internationalisierung und MySQL) schlägt vor, in Formulare ein hidden-Textfeld mit einer vorgegebenen Unicode-Zeichenkette einzubauen und vor der Formularverarbeitung zu kontrollieren, ob diese Zeichenkette korrekt übermittelt worden ist. Nach unseren Tests bleibt aber auch diese Kontrolle wirkungslos, wenn im Browser ein falscher Zeichensatz aktiv ist. In diesem Fall wird der Inhalt des hidden-Textfelds korrekt übertragen, die restlichen Eingabefelder enthalten aber dennoch falsch codierte Texte.

http://mysql.binarycompass.org/Downloads/Presentations/practical-i18n-oscon2004.pdf  

HTTP-Protokoll

Das HTTP-Protokoll definiert, wie der Webbrowser und der Webserver miteinander kommunizieren, wie HTML-Dokumente und Formulardaten übertragen werden etc. Dieses Protokoll bietet neben dem HTML-<meta>-Tag die zweite Möglichkeit, den Zeichensatz einzustellen. Es wurde bereits erwähnt, dass sich die meisten Webbrowser auf die Information des HTTP-Protokolls verlassen, die Zeichensatzangabe im <meta>-Tag aber ignorieren.

Entscheidend für den Zeichensatz ist die Header-Information über den Dokumenttyp. Der HTTP-Header wird vor dem eigentlichen HTML-Dokument übertragen und ist daher nicht Teil des HTML-Codes. Einige Webbrowser bieten aber ein Kommando zur Anzeige der Seiteninformationen oder -eigenschaften an, aus denen auch die Header-Informationen hervorgehen (siehe Abbildung 1).

Woher kommt nun die Header-Information über den Dokumenttyp samt Zeichensatz? Es bestehen folgende Möglichkeiten:

  • Apache-Defaulteinstellung (AddDefaultCharset utf-8 in httpd.conf)
  • Apache-Verzeichniskonfiguration (AddDefaultCharset in .htaccess)
  • PHP-Defaulteinstellung (default_charset="utf-8" in php.ini)
  • PHP-Code der betreffenden Seite:
<?php Header("Content-Type: text/html; charset=utf-8");  ?>  

Beachten Sie, dass die Header-Funktion am Beginn der PHP-Seite und vor jeder HTML-Ausgabe erfolgen muss!

Details zu den Konfigurationsmöglichkeiten von Apache und PHP folgen in den weiteren Abschnitten.

Webbrowser

Wie bereits erwähnt, ist es die Aufgabe des Webbrowsers, den Zeichensatz des Dokuments zu erkennen und das Dokument entsprechend korrekt anzuzeigen. Der Webbrowser kann dazu zwei Informationen auswerten: die HTTP-Header-Daten und das HTML-<meta>-Tag. Die meisten Browser berücksichtigen allerdings nur die Header-Informationen und ignorieren das <meta>-Tag.

Über die korrekte Erkennung des Zeichensatzes hinaus bestimmen noch zwei Faktoren die korrekte Darstellung der Seite:

  • Der Browser muss den Zeichensatz kennen. Das ist keineswegs immer der Fall. Sehr alte Browser haben beispielsweise keine Unicode-Unterstützung. Aber selbst moderne Browser können nicht alle Zeichensätze kennen – dafür gibt es viel zu viele. (latin1 alias iso-8859-1 und utf-8 bereitet natürlich keinem aktuellen Browser Probleme. Die meisten einigermaßen aktuellen Browser kommen auch mit latin9 alias iso-8859-15 zurecht.)
  • Der Browser muss Zugang zu Schriftarten haben, die die gewünschten Zeichen enthalten. Für die in Europa üblichen Zeichen ist das kein Problem. Es gibt aber nur sehr wenige Schriften, die alle in Unicode definierten Zeichen (also auch alle asiatischen Zeichen) enthalten.

Apache

Es bestehen mehrere Möglichkeiten, Apache mitzuteilen, welchen Zeichensatz er in der HTTP-Header-Information an den Browser übermitteln soll:

  • AddDefaultCharset Zeichensatz in httpd.conf: Apache übermittelt den hier angegebenen Zeichensatz (z.B. utf-8 oder iso-8859-1) für alle Seiten an den Browser. Die Einstellung gilt sowohl für .html- als auch für .php-Dateien. Das <meta>-Tag im HTML-Code wird ignoriert. Beachten Sie, dass einige Linux-Distributionen per Default AddDefaultCharset utf-8 verwenden. Wenn also Ihre latin1-Seiten fehlerhaft angezeigt werden, ist das der wahrscheinlichste Grund.
  • AddDefaultCharset off in httpd.conf: Apache wertet das <meta>-Tag aus und sendet den dort angegebenen Zeichensatz an den Browser. Es gilt also der Inhalt des <meta>-Tags.
  • AddCharset Zeichensatz .kennung in httpd.conf: Damit werden ein Zeichensatz für Dateien einer bestimmten Kennung eingestellt. AddCharset utf-8 .utf8 bewirkt also, dass für alle Dateien, deren Name auf .utf8 endet, als Zeichensatz utf-8 an den Browser gemeldet wird.
  • AddDefaultCharset und AddCharset in .htaccess: Die beiden Schlüsselwörter sind auch in .htaccess zulässig und ermöglichen so eine verzeichnisspezifische Konfiguration. Das ist praktisch, wenn Sie (z.B. bei einem ISP) keinen Einfluss auf httpd.conf haben. Beachten Sie aber, dass die Einstellungen in .htaccess nur berücksichtigt werden, wenn httpd.conf für das Webverzeichnis Veränderungen durch lokale Konfigurationsdateien zulässt (AllowOverride All oder FileInfo in der betreffenden <Directory>-Gruppe). Vorsicht: Die Defaulteinstellung für AllowOverride lautet oft None. Die .htaccess-Datei wird in diesem Fall vollständig ignoriert.

PHP

Falls Sie einen anderen Zeichensatz als Unicode einsetzen, ist PHP momentan das schwächste Glied der Kette. Zwar ist PHP prinzipiell in der Lage, HTML-Dokumente in beinahe jedem Zeichensatz zu erzeugen. Funktionen wie echo oder print geben Zeichenketten einfach unverändert aus und kümmern sich dabei nicht um den Zeichensatz.

Zeichenkettenfunktionen

Das eigentliche Problem besteht aber darin, dass alle PHP-Zeichenkettenfunktionen davon ausgehen, dass die Zeichenketten den latin1-Zeichensatz verwenden. Wenn die Zeichenketten dagegen UTF-8-codiert sind,

  • liefert strlen die Anzahl der Bytes statt der Anzahl der Zeichen,
  • können Sie sich nicht auf die Ergebnisse der Kleiner- und Größer-Operatoren verlassen,
  • können Sie Zeichenketten-Arrays nicht zuverlässig sortieren,
  • können Sie sich auf die Ergebnisse wichtiger Funktionen wie stripslashes oder htmlspecialchars nicht mehr verlassen etc. Ganz auswegslos ist die Situation zum Glück auch nicht: PHP stellt die Funktionen utf8_encode und utf8_decode zur Verfügung, um Zeichenketten zwischen UTF-8 und ISO-8859-1 zu konvertieren. Falls PHP mit der iconv-Erweiterung kompiliert ist bzw. wenn diese Erweiterung als Modul aktiviert wird (unter Windows extension=php_iconv.dll in php.ini), stehen neben den Standardzeichenkettenfunktionen auch iconv-Varianten zur Verfügung. iconv_strlen($s, "UTF-8") ermittelt dann die tatsächliche Zeichenanzahl einer UTF-8-Zeichenkette. Noch mehr Zusatzfunktionen stellt die mbstring-Erweiterung zur Verfügung. Damit können Sie auch reguläre Ausdrücke auf Multibyte-Zeichenketten anwenden, E-Mails versenden etc.

Zeichensatz der PHP-Dateien

Die PHP-Dateien müssen in einem Zeichensatz vorliegen, der es dem PHP-Interpreter ermöglicht, die PHP-Sprachelemente korrekt zu erkennen. Das setzt voraus, dass zumindest die US-ASCII-Zeichen wie bei latin1 codiert sind. Diese Voraussetzung ist für UTF-8-Dateien zum Glück erfüllt.

HTTP-Zeichensatz-Header in php.ini einstellen

Per Default gibt der PHP-Interpreter keine HTTP-Header-Informationen zum Zeichensatz an Apache weiter und überlässt es somit dem Webserver, ob und welche Header-Zeichensatzdaten dieser an den Browser sendet.

Durch die Einstellung default_charset="utf-8" in php.ini können Sie dieses Verhalten ändern. Der PHP-Interpreter gibt nun den angegebenen Zeichensatz an Apache weiter. Dieser verändert die Informationen nicht mehr. Das heißt, die von PHP stammenden Informationen haben Vorrang gegenüber denen aus der Apache-Konfiguration!

HTTP-Zeichensatz-Header dynamisch einstellen

Sie können den gewünschten Header-Zeichensatz auch durch die PHP-Funktion header erzeugen. Das ermöglicht eine dokumentenspezifische Zeichensatzeinstellung. Die header-Funktion muss am Beginn der PHP-Seite aufgerufen werden (vor jeder HTML-Ausgabe!).

<?php header("Content-Type: text/html; charset=utf-8");  ?>  

Die durch die header-Funktion gesendeten Daten haben Vorrang sowohl gegenüber der default_charset-Einstellung als auch gegenüber der Apache-Konfiguration.

MySQL

Geradezu vorbildlich ist die Zeichensatzunterstützung von MySQL – allerdings erst seit Version 4.1. Sie können nicht nur für den Server oder für eine Datenbank den gewünschten Zeichensatz einstellen, sondern auch für jede Tabelle und sogar für jede einzelne Spalte einer Tabelle. Es ist also möglich, in einer Tabelle eine Spalte mit latin1-Zeichenketten und eine zweite Spalte mit utf-8-Zeichenketten zu erzeugen.

MySQL unterstützt eine ganze Reihe verschiedener Zeichensätze, unter anderem ascii, binary, cp1250, greek, hebrew, latin1, latin2, latin5, latin7, ucs2 und utf8. In der Liste fehlt allerdings latin9. Das Euro-Zeichen können Sie daher nur in utf8- oder ucs2-Spalten speichern.

utf-8-Spalten weisen gegenüber latin1-Spalten momentan eine wesentliche Einschränkung auf: Die deutsche Sortierordnung wird nicht unterstützt. Die Sortierordnung utf8_general_ci ist zwar für viele deutschsprachige Anwendungen ebenfalls ausreichend, aber wenn Sie gemäß dem DIN-1- oder DIN-2-Standard sortieren möchten, müssen Sie bis auf weiteres bei latin1-Spalten bleiben.

Konfiguration des Defaultzeichensatzes

In der MySQL-Konfigurationsdatei my.cnf bzw. my.ini (Windows) können Sie mit default-character-set = latin1 den Defaultzeichensatz für den MySQL-Server einstellen (Optionsgruppe [mysqld]). Die Einstellung gilt für neue Datenbanken, Tabellen und Textspalten, soweit beim Erzeugen dieser Objekte nicht explizit ein anderer Zeichensatz angegeben wird.

Zeichensatztransformation durch die Client-Bibliotheken

Was nützt es, wenn der MySQL-Server selbst mit allen erdenklichen Zeichensätzen umgehen kann, der Client (also z.B. Ihr PHP-Programm oder der Kommandozeileninterpreter mysql) aber einen ganz bestimmten Zeichensatz voraussetzt? Die MySQL-Client-Bibliotheken wandeln daher automatisch alle Zeichenketten vom Zeichensatz des Clients in den Zeichensatz des Servers um (und umgekehrt).

Für welche Aspekte der Verbindung zwischen Client und Server welche Zeichensätze zum Einsatz kommen, wird durch mehrere MySQL-Systemvariablen gesteuert, die in der folgenden Tabelle zusammengefasst sind.

Variable Bedeutung
@@character_set_client Zeichensatz des Clients
@@character_set_server Defaultzeichensatz des Servers
@@character_set_connection Zeichensatz für die Verbindung zwischen Client und Server
@@character_set_results Gewünschter Zeichensatz für SELECT-Ergebnisse
@@character_set_database Defaultzeichensatz der Datenbank

Einige dieser Variablen können Sie in Ihrem Client-Programm verändern. Wenn Sie also beispielsweise PHP-Code schreiben, der UTF-8-Daten verarbeiten soll, sehen die entsprechenden Kommandos so aus (hier für die mysqli-Schnittstelle):

// mysql-Verbindung herstellen  $mysqli->query("SET @@session.character_set_client = 'utf8'");  $mysqli->query("SET @@session.character_set_results = 'utf8'");  $mysqli->query("SET @@character_set_connection = 'utf8'");  

Nach diesen Vorarbeiten können Sie SQL-Kommandos mit UTF-8-Zeichenketten ausführen und erhalten SELECT-Ergebnisse ebenfalls im UTF-8-Format.

MySQL-Kommandos (mysql, mysqldump)

Die Kommandos mysql und mysqldump versuchen selbst zu erkennen, in welchem Zeichensatz Ein- und Ausgaben erfolgen sollen. Das gelingt manchmal, aber nicht immer. Manchmal müssen Sie daher mit der Option --default-character-set=latin1 oder =utf8 nachhelfen.

MySQL-Administrationswerkzeuge (phpMyAdmin, Query Browser)

phpMyAdmin

Dieses Programm kommt verblüffend gut mit allen erdenklichen Zeichensätzen zurecht. Das Beispiel ist gewissermaßen der Beweis dafür, dass man mit PHP durchaus Unicode-taugliche Programme entwicklen kann. Zwei Tipps zur Bedienung:

  • Auf der Startseite von phpMyAdmin können Sie den Zeichensatz für die MySQL-Verbindung einstellen. Wählen Sie hier utf8_general_ci!
  • Wenn Sie mit phpMyAdmin *.sql-Dateien importieren bzw. ausführen, müssen Sie den Zeichensatz dieser Dateien angeben. (Per Default nimmt phpMyAdmin an, dass es sich um UTF-8-Dateien handelt.)

MySQL Query Browser

Erwartungsgemäß zeigt auch dieses Programm wenig Zeichensatzprobleme. Bei der aktuellen Linux-Funktion treten allerdings manchmal Schwierigkeiten bei der korrekten Interpretation von Eingaben auf. Beispielsweise hatte ich mehrfach Probleme, ein Euro-Zeichen in eine Tabelle mit Unicode-Spalten einzugeben.

Linux

Unter Linux steuern die Umgebungsvariablen LANG sowie LC_xxx, welcher Zeichensatz per Default gilt. Die meisten Programme und insbesondere alle Textkommandos halten sich an diese Einstellung. Einige aktuelle Distributionen verwenden mittlerweile UTF-8 als Defaultzeichensatz (z.B. Fedora, Red Hat, SUSE, nicht aber Mandrakelinux)!

Wo diese Variablen eingestellt werden, hängt von der Distribution ab (bei Red Hat und Fedora /etc/sysconfig/i18n, bei SUSE /etc/sysconfig/language). Manche Distributionen stellen auch komfortable Konfigurationswerkzeuge zur Verfügung, um den Zeichensatz zu verändern (bei SUSE das YaST-Modul SYSTEM|SPRACHE). Info Der Defaultzeichensatz gilt auch für den Editor, mit dem Sie Ihre PHP-Dateien erstellen. Wenn Sie, wie in diesem Artikel empfohlen, latin1 verwenden, sollten Sie auch den Linux-Defaultzeichensatz entsprechend einstellen. Andernfalls müssen Sie beim Aufruf des Editors immer darauf achten, dass alle *.php-Dateien im richtigen Zeichensatz gespeichert werden, was ebenso mühsam wie fehleranfällig ist.

Wenn Sie den Zeichensatz von Text- oder Codedateien nachträglich ändern möchten, helfen Ihnen dabei die Kommandos recode oder iconv. Die beiden folgenden Zeilen zeigen zwei alternative Möglichkeiten, um latin1-Dateien in utf8-Dateien umzuwandeln:

user$ recode latin1..u8 < latin1dat > utf8dat  user$ iconv -f latin1 -t utf-8 latin1dat >> utf8dat  

Microsoft Windows

Unter Windows gibt es keine zentrale Einstellung für den Defaultzeichensatz. Das hat damit zu tun, dass reine Textdateien unter Windows eine viel geringere Rolle spielen als unter Unix/Linux. Sie müssen aber bei Ihrem Editor darauf achten, dass dieser die *.php-Dateien im richtigen Zeichensatz speichert.

PHP-Scripting der Anfang

Bei der Behandlung von Fehlern kommt das Konzept des Exceptionhandlings (Ausnahmebehandlung) zum Einsatz. Es bietet die Möglichkeit, bestimmte Fehler abzufangen. Damit wird es einem Programm ermöglicht, weiterzulaufen, obwohl ein Fehler aufgetreten ist. Sie werden lediglich informiert, so dass Sie die Fehlerursache abstellen können.

Beim Exceptionhandling wird der Codebereich, in dem ein Fehler auftreten kann, in einen sogenannten try-Block eingeschlossen. Es wird „versucht“, den Code auszuführen. Falls ein definierter Fehler auftritt, wird ein Objekt der Klasse Exception durch die Anweisung throw erzeugt.

Anschließend wird statt des restlichen Codes im try-Block der Code im zugehörigen catch-Block ausgeführt; der Fehler wird somit „abgefangen“. Dies erläutere ich am Beispiel eines Programms, zu dem es zwei Versionen gibt: einmal ohne und einmal mit Ausnahmebehandlung.

Ohne Ausnahmebehandlung

Zunächst das Programm ohne Ausnahmebehandlung:

<html>  <body>  <?php  /* Für dieses Programm alle Fehler anzeigen */  ini_set("error_reporting", 32767);  /* Variable unbekannt */  $c = 2 * $a;  echo "<p>$c</p>";  /* Division durch 0 */  $x = 24;  for($y=4; $y>-3; $y--)  {  $z = $x / $y;  echo "$x / $y = $z<br />";  }  /* Zugriff auf Funktion */  fkt();  echo "Ende"  ?>  </body>  </html>  

Datei exception_ohne.php Info Zunächst wird mit Hilfe der Funktion ini_set() der PHP-Konfigurationsparameter error_reporting eingestellt. Dieser bestimmt, welche Fehler angezeigt werden und welche nicht. Der Wert 32767 führt dazu, dass der weitaus größte Teil der Fehler angezeigt wird.

Anschließend wird eine Variable innerhalb eines Ausdrucks verwendet, die bis zu diesem Zeitpunkt noch unbekannt ist. Darüber werden Sie mit Hilfe einer Notice informiert, siehe Abbildung 1.

Mit Ausnahmebehandlung

Es folgt das gleiche Programm, diesmal aber mit Ausnahmebehandlung:

<html>  <body>  <?php  /* Für dieses Programm alle Fehler anzeigen */  ini_set("error_reporting", 32767);  /* Variable unbekannt */  try  {  if(!isset($a))  throw new Exception("Variable unbekannt");  $c = 2 * $a;  echo "<p>$c</p>";  }  catch(Exception $e)  {  echo $e->getMessage() . "<br />";  }  /* Division durch 0 */  $x = 24;  for($y=4; $y>-3; $y--)  {  try {  if($y == 0)  throw new Exception("Division durch 0");  $z = $x / $y;  echo "$x / $y = $z<br />";  }  catch(Exception $e)  {  echo $e->getMessage() . "<br />";  }  }  /* Zugriff auf Funktion */  try  {  if(!function_exists("fkt"))  throw new Exception("Funktion unbekannt");  fkt();  }  catch(Exception $e)  {  echo $e->getMessage() . "<br />";  }  echo "Ende"  ?>  </body>  </html>  

Datei exception_mit.php Info Vor der Nutzung der unbekannten Variablen wird mit Hilfe der Funktion isset() geprüft, ob die Variable existiert. Das Ganze findet in einem try-Block statt. Es ist also ein „Versuch“, die Variable zu nutzen.

Falls festgestellt wird, dass die Variable nicht existiert, wird mit Hilfe von throw eine Exception (Ausnahme) erzeugt und „geworfen“, mit einem zugehörigen Text.

Diese Exception wird in dem catch-Block „gefangen“, der dem try-Block zugeordnet ist. Bei der Erzeugung der Exception wurde ihr eine Meldung mitgegeben. Diese Fehlermeldung wird mit Hilfe der Methode getMessage() ausgegeben, siehe Abbildung 2.

Bislang haben wir nur einzelne PHP-Scripts kennen gelernt, die auch zugleich via URI im Browser aufrufbar waren. Bei größeren Projekten wird der Code jedoch in aller Regel auf mehrere, manchmal sogar sehr viele Scripts verteilt. PHP-Scripts können andere PHP-Scripts einbinden. Dabei müssen nicht alle Scripts innerhalb der Document Root liegen. Nur diejenigen, die im Browser direkt aufrubar sein sollen, müssen sich in einem Webverzeichnis befinden.

Die einzelnen Scripts übernehmen dabei typischerweise abgegrenzte Aufgaben. Ein Script etwa übernimmt die Kommunikation mit der Datenbank, ein zweites erledigt das Verarbeiten von Formulardaten, ein drittes das Behandeln aufgetretener Fehler und ein viertes das Zusammenfügen von HTML-Templates. Ein Script fungiert meistens als „Ober-Script“, das alles koordiniert.

Ein kleines Beispiel soll demonstrieren,

Dazu kommen noch zwei Template-Dateien:

PHP-Script linkweb_vars.php: die Variablenzentrale

<?php  #-------------------------------------------  # Templates:  $templates = array();  $templates['standard'] = "linkweb.tpl";  $templates['error'] = "linkweb_errors.tpl";  #-------------------------------------------  # Inhaltsdateien:  $content_files = array();  $content_files['home'] = "0001.txt";  $content_files['impressum'] = "0007.txt";  $content_files['themen'] = "0003.txt";  $content_files['branchen'] = "0009.txt";  $content_files['auskunft'] = "0005.txt";  $content_files['geo'] = "0002.txt";  $content_files['wissen'] = "0004.txt";  $content_files['literatur'] = "0008.txt";  $content_files['lexika'] = "0006.txt";  $content_files['glossare'] = "0010.txt";  $content_files['wikis'] = "0014.txt";  $content_files['ftp'] = "0011.txt";  $content_files['wais'] = "0015.txt";  $content_files['newsgroups'] = "0012.txt";  $content_files['foren'] = "0013.txt";  #-------------------------------------------  # Fehler:  $errors = array();  $errors['no_page'] = "Fehler: Seitenname existiert nicht";  $errors['no_template_key'] = "Interner Fehler: Template-Schlüssel existiert  nicht";  $errors['no_error_key'] = "Interner Fehler: Fehler-Schlüssel existiert nicht";  #-------------------------------------------  # Diverses:  $standard_title = "untitled";  ?>  

Info Wie ersichtlich, werden in diesem Script ausschließlich Variablen definiert. Der Vorteil dieser Lösung ist, dass man bei mehreren Scripts nicht mehr überlegen muss, welche Variable in welchem Script eingeführt wird. Wenn beispielsweise neue Seiten hinzukommen, neue Templates, neue Fehlermeldungen usw., dann wird alles in dieser Script-Datei notiert.

PHP-Script linkweb_templates.php: Zusammenkleben von Templates und Daten

<?php  #-------------------------------------------  # Includes:  include_once("linkweb_vars.php");  include_once("linkweb_errors.php");  #-------------------------------------------  # Funktionen:  function template_use($template_key){  global $templates;  if(! array_key_exists($template_key, $templates))  error_show("no_template_key");  else  return(file_get_contents($templates[$template_key]));  }  function template_set_var($template_content, $name, $value){  $pattern = "/\[\%".$name."\%\]/";  return(preg_replace($pattern, $value, $template_content));  }  function template_get_title($template_content){  global $standard_title;  $pattern = "/<h1>(.*)<\/h1>/";  preg_match($pattern, $template_content, $matches);  if(isset($matches[1]))  return($matches[1]);  else  return($standard_title);  }  function template_show($template_content){  echo $template_content;  exit();  }  ?>  

Info In diesem Modul-Script begegnet uns erstmals die PHP-Funktion include_once(). Sie erlaubt das Einbinden anderer Dateien an der aktuellen Stelle im Code. Eingebunden werden die anderen Scripts linkweb_vars.php und linkweb_errors.php. Der Grund ist, dass das Script zum Template-Management auf Variablen zugreifen muss, die in linkweb_vars.php definiert wurden, und dass bei auftretenden Fehlern eine Funktion aus linkweb_errrors.php aufgerufen werden soll.

Neben include_once() gibt es auch die ältere Funktion include(). Wir ziehen include_once() jedoch vor, um Probleme mit Mehrfacheinbindungen zu vermeiden. Darauf kommen wir später noch zurück.

Es ist nicht zwingend nötig, die anderen Dateien

wie in unserem Beispiel am Beginn eines Scripts einzubinden. Wichtig ist nur, dass ein anderes PHP-Script eingebunden wird, bevor auf Variablen oder Funktionen dieses Scripts zugegriffen wird. Im Sinne eines übersichtlichen Codes ist es jedoch zweckmäßig, die Inkludierungen am Beginn zu notieren, damit die Abhängigkeiten des Scripts transparent werden.

Das Script linkweb_templates.php besteht außer den include_once-Anweisungen am Beginn nur aus einer Reihe von Funktionen. Auch das ist typisch für modular aufgebaute Scripts: Der Code in den Modulen wird nicht direkt beim Einbinden ausgeführt, sondern steht in Routinen, die bei Bedarf aufgerufen werden. Folgende Funktionen sind in linkweb_templates.php vorhanden:

Es fällt auf, dass alle Funktionsnamen mit template_ beginnen. Konventionen dieser Art sind nirgendwo vorgeschrieben. Sie sind einfach Teil einer ordentlichen Programmierung.

PHP-Script linkweb_errors.php: Fehlerausgaben

<?php  #-------------------------------------------  # Includes:  include_once("linkweb_vars.php");  include_once("linkweb_templates.php");  #-------------------------------------------  # Funktionen:  function error_show($error_key){  global $templates, $errors;  $page = template_use("error");  if(array_key_exists($error_key, $errors))  $page = template_set_var($page, "message",  $errors[$error_key]);  else  $page = template_set_var($page, "message",  $errors['no_error_key']);  template_show($page);  }  ?>  

Info In diesem Script werden die zentralen Variablen und die Template-Verwaltung eingebunden. Auch dieses Script enthält ansonsten nur funktionsgebundenen Code, und zwar in einer einzigen Funktion namens error_show(). Diese Funktion ruft die in linkweb_templates.php stehende Funktion template_use() mit dem Template-Namen error auf. Dadurch wird template_use() veranlasst, die Template-Datei zu laden, deren Pfadname sie über den in linkweb_vars-php notierten Array $templates mit dem Schlüsselnamen error ermittelt. Das alles mag etwas verwirrend sein am Anfang, doch es ist wichtig, diese Zusammenhänge zu verstehen und zurückzuverfolgen. Denn das Verständnis dieser Querbezüge zwischen den Script-Modulen ist der Schlüssel für eine eigene erfolgreiche Programmierung im Stil der Profis.

Der Rückgabewert von template_use() ist der komplette Inhalt einer Template-Datei.

Diese wird innerhalb von error_show() in der funktionslokalen Variablen $page gespeichert. Als nächstes muss die Variable [%message%] in der Template-Datei für Fehlermeldungen durch den gewünschten Fehlermeldungstext ersetzt werden. Dazu wird wieder eine Funktion aus linkweb_templates.php aufgerufen, nämlich template_set_var(). Die Funktion wird mit geeigneten Parametern versorgt. Da show_error() nicht sicher sein kann, ob der ihr übergebene Parameter, den sie als Schlüsselname einer Fehlermeldung im Array $errors interpretiert, dort tatsächlich vorkommt, wird eine if-Abfrage eingebaut, die das überprüft. Sollte keine Fehlermeldung zum übergebenen Wert existieren, wird eine Standardfehlermeldung ausgegeben.

Am Ende wird template_show() aufgerufen, wodurch die Fehlerseite an den Browser ausgegeben wird.

PHP-Script linkweb.php: das Hauptprogramm

<?php  #-------------------------------------------  # Includes:  include_once("linkweb_templates.php");  include_once("linkweb_errors.php");  #-------------------------------------------  # Inhalt seitenabhängig einlesen:  if(isset($_GET['page']))  $get_page = $_GET['page'];  else  error_show('no_page');  $page = template_use("standard");  $page = template_set_var($page, "content",  file_get_contents($content_files[$get_page]));  $title = template_get_title($page);  $page = template_set_var($page, "title", $title);  template_show($page);  ?>  

Info Dies ist das einzige Script, das im Browser aufgerufen wird. Auch hier werden zu Beginn die im weiteren Code benötigten Modul-Scripts eingebunden. Der Inhalt des „Hauptprogramms“ steht im Gegensatz zu denen der Modul-Scripts nicht in Funktionen, sondern wird direkt ausgeführt. Die Aufgabe des Hauptprogramms besteht darin, eine normale Seite aus Templates, Datendateien usw. zusammenzukleben und auszugeben. Dazu muss der übergebene GET-Parameter page ausgewertet werden. Wurde kein solcher GET-Parameter beim URI-Aufruf des Scripts übergeben, wird error_show() aufgerufen, um einen entsprechenden Fehler auszugeben. Ansonsten wird ähnlich wie innerhalb von error_show() ein Template-Inhalt in einer Variablen $page gespeichert. Der Seiteninhalt aus der passenden Datendatei wird durch Aufruf von template_set_var() gesetzt. Ersetzt wird die Zeichenfolge [%content%] im Template-Code. Der Wert, durch den sie ersetzt wird, wird durch Aufruf der PHP-Funktion file_get_contents() ermittelt. Der Rückgabewert dieser Funktion ist nämlich der komplette Inhalt der eingelesenen Datei. Welche Datei eingelesen werden soll, entscheidet das Script, indem es in dem in linkweb_vars.php gespeicherten Array $content_files den Schlüssel mit dem im GET-String übergebenen Namen verwendet.

Der Titel wird noch ermittelt und in das title-Element als Elementinhalt eingesetzt.

Am Ende wird die fertige Seite durch Aufruf von template_show() ausgegeben.

Einbindungen kreuz und quer

In unserem Beispiel ergeben sich folgende Abhängigkeiten:

Durch das muntere Einbinden entsteht die Situation, dass manche Dateien gleich mehrfach eingebunden werden, und sogar gegenseitige Einbindungen kommen vor. In vielen Programmiersprachen führt dies zu Problemen, da deren Compiler das Einbinden gnadenlos so durchziehen wie angewiesen. Dadurch kommen dann Funktionen oder Variablen aus mehrfach eingebundenen Modulen im Gesamtquelltext doppelt oder mehrfach vor, was den Compiler zu Fehlermeldungen veranlasst und dazu, das Kompilieren zu verweigern. Das Ergebnis sind die zahlreichen ifdefs etwa in C, um die sich der Programmierer selber kümmern muss.

In PHP sorgt die im Beispiel überall verwendete Funktion include_once() dafür, dieses Problem zu vermeiden. Wurde ein Modul bereits eingebunden, bindet es der Compiler nicht noch einmal ein.

Fazit: Modularisierung von Code lohnt sich

Vielleicht ist Ihnen aufgefallen, dass die PHP-Quelltexte in den modulartig verteilten Scripts dieses Abschnitts deutlich abstrakter wirken als diejenigen aus den ersten Beispielen. Das liegt daran, dass fast alle Aufgaben nicht mehr „selbst“ erledigt werden, sondern durch Aufruf entsprechender Funktionen. Das einheitliche Namenskonzept bei Variablennamen und Funktionsnamen tut ein übriges. Der Code wird dadurch andererseits eleganter und, was noch viel wichtiger ist, besser wartbar.

Mit diesem Modularisierungsbeispiel haben wir uns dem Programmierstil der professionellen PHP-Entwickler bereits angenähert. Was noch fehlt, ist die Verwendung von Objekten, Klassen und Instanzen. Darauf gehen wie später noch genauer ein.

Sie sind beim Surfen sicherlich schon oft darauf gestoßen: in irgendeiner Ecke der Webseite befindet sich ein kleiner Zähler, der anzeigt, der wievielte Besucher der Webseite Sie sind. Ist es die eigene Seite, ist es natürlich interessant zu wissen, wie groß die Heerschar der Besucher ist, die Ihre Seite aufruft.

Ein einfacher Counter ohne Cookie

Bei einem einfachen Counter geht es lediglich darum, eine Textdatei zu erzeugen, die den Counterwert speichert, den Wert beim Aufrufen der Seite auszulesen und anschließend den neuen Wert (+1 für den neuen Besucher) zu speichern und anzuzeigen.

Da Sie die Funktionen zum Öffnen einer Textdatei bereits kennen gelernt haben, haben Sie den Programm-Code schnell geschrieben. Ins Spiel kommt eigentlich nur eine neue Funktion des Dateiensystems, die Funktion rewind().

Diese Funktion setzt den Dateizeiger auf das erste Byte einer Datei zurück. Vor dem Einsatz von rewind muss zuvor eine Datei erfolgreich geöffnet worden sein, Sie müssen also bereits die Funktion fopen eingesetzt haben. Info int rewind(int file)

Die Funktion setzt die Position des Dateizeigers auf den Anfang der Datei zurück.

Das hier vorgestellte Beispiel kann nur einen Counterwert in der Textdatei speichern. Möchten Sie verschiedene Counter für mehrere Seiten erstellen, können Sie dies einfach bewerkstelligen, indem Sie den Namen der Textdatei in jeder Webseite variieren. Beachten Sie bitte, dass die Textdatei des Counters im gleichen Verzeichnis wie die Seite liegen oder aber die Pfandangabe angepasst werden muss. Sie können relative Pfadangaben, wie Sie sie von Hyperlinks kennen, verwenden.

Der folgende Code zeigt den Programm-Code für einen einfachen Counter. Er ist – wie Sie sehen – tatsächlich sehr kurz!

<?php  if(!file_exists("count.txt")){fopen("count.txt", "a" );}  $counter=fopen("count.txt","r+"); $aufruf=fgets($counter,100);  $aufruf=$aufruf+1;  rewind($counter);  fputs($counter,$aufruf);  echo $aufruf;  ?>  

Erläuterung des Scripts

Nach dem öffnenden PHP-Tag wird die Datei angelegt:

if(!file_exists("count.txt")){fopen("count.txt", "a" );}  

Sofern Sie die bisherigen Beispiele mitgespielt haben, kennen Sie den Befehl fopen bereits, da wir ihn im Zusammenhang mit dem Gästebuch eingesetzt haben, um die Einträge in einer Textdatei zu speichern. Dieser Befehl wird in diesem Fall „missbraucht“ und mit dem Attribut a verwendet, um die Datei anzulegen. Deshalb wird der Befehl in den Block der if-Bedingung geschrieben, um die Datei nur anzulegen, wenn sie noch nicht existiert. Dieser Schritt ist notwendig, da mit dem später verwendeten fopen("count.txt", r+) die Datei nicht angelegt, sondern nur zum Lesen und Schreiben geöffnet wird.

$counter=fopen("count.txt","r+");  

Nach dem Anlegen gibt es die Datei auf jeden Fall. Mit dem Attribut r+ zum Lesen und Schreiben wird sie nun geöffnet und der Dateihandle erzeugt. Der Dateizeiger (Cursor) steht am Anfang der Datei.

Nun setzen Sie die folgende Funktion ein: fgets(). Info Diese Funktion liest eine Zeile von der aktuellen Position des Dateizeigers, der durch fopen("count.txt", "r+") am Anfang steht, bis die angegebene Anzahl Zeichen (100) oder das Zeilenende erreicht wird. Zurückgegeben wird eine Zeichenkette, die wir der Variablen $aufruf zuweisen.

$aufruf=fgets($counter,100);  

Info sring fgets(int file, int length)

Die Funktion liest aus einer Datei eine Zeile mit der angegebenen Länge (length) aus. Der Dateihandle (file) muss ein gültiger Handle für eine geöffnete Datei sein.

Eine Länge von 100 Zeichen ist für einen Counter zweifellos ausreichend, es gibt aber Einsatzbereiche, bei denen die Länge deutlich größer sein muss, um die komplette Zeile der Datei einzulesen. In $aufruf ist nach dieser Zuweisung der aktuelle Zählerstand gespeichert. Die Tatsache, dass die Datei gerade erst angelegt wurde und folglich noch kein Zählerstand enthalten ist, führt nicht zu Problemen, da die nächste Zeile die gezählten Aufrufe (Zählerstand) um eins erhöht:

$aufruf=$aufruf+1;  

Auch wenn die TXT-Datei noch leer war, ist in $aufruf nun eine Zahl enthalten, nämlich 1, da PHP bei „Nichts“ bzw. „Null“ + 1 keine Probleme beim Rechnen hat.

Mit der Funktion rewind() bewirken Sie nun, dass der Cursor in der Textdatei, in der der Zählerstand gespeichert wird, wieder auf den Anfang gesetzt wird, sodass der neue Zählerstand den alten überschreibt.

rewind($counter);  

Nun schreiben Sie den neuen Zählerstand in die Datei.

fputs($counter,$aufruf);  

Zu guter Letzt heißt es

echo $aufruf;  

um den Zählerstand anzuzeigen.

Schließen Sie PHP, speichern Sie das Dokument als PHP-Datei und testen Sie das Script im Browser. Sie müssten eine Seite angezeigt bekommen, die in etwa dem Bild 1 entspricht.

Klicken Sie dann ein paar Mal auf AKTUALISIEREN. Wenn es geklappt hat, wird die angezeigte Zahl hochgezählt. Der Browser zeigt, wie in Bild 2 zu sehen, den Erfolg.

Ein Counter mit Cookie

Vor der „Erfindung“ von Cookies war das Surfen im Netz ein Reise ohne Geschichte und ist/wäre es auch heutzutage, wenn Cookies unbekannt wären oder wenn ihr Gebrauch im Browser deaktiviert würde. Durch Cookies kann der Server Informationen über den User speichern und sich an ihn „erinnern“. Diese Informationen werden in Form von kleinen Textdateien auf dem Rechner des Surfers gespeichert und vom Browser automatisch an den Server, der das Cookie gesetzt hat, gesendet. Info Um weitere Informationen über Cookies zu erhalten, lesen Sie bitte den Exkurs am Ende dieses Abschnitts. In diesem Exkurs gehen wir kurz auch auf die vermeintliche Gefährdung durch Cookies ein.

Ohne Cookies wären viele Sachen im Internet nicht möglich. Ein häufig zitiertes Beispiel dafür sind Online-Einkaufstouren, bei denen Sie ihre gekauften Waren in einen virtuellen Warenkorb legen. Es leuchtet ein, dass während einer solchen Tour Informationen gespeichert werden müssen.

PHP unterstützt Cookies; Sie können mit PHP Cookies setzen, Informationen zurückgeben lassen, und Cookies auch wieder löschen. Im Zusammenhang mit einem Zähler werden wir im nächsten Abschnitt also den Programm-Code schreiben, mit dem Sie einen Cookie setzen. Dieser Cookie wird dazu verwendet, eine Reloadsperre zu implementieren, sodass nicht jedes Aktualisieren der Webseite als neuer Aufruf gezählt wird.

Einen Cookie implementieren

Einen Cookie setzen Sie mit dem Befehl setcookie();

Als Argumente tragen Sie in die Klammer einen beliebigen Namen und einen Wert ein. Info int setcookie(string name, string value [, int expire [, string path [, string domain [, int secure]]]])

Alle Parameter sind optional außer dem Namen des Cookies (name) und dem Wert (value). Die Funktion sendet einen Cookie – übertragen im Header – an den Browser.

Um einen Wert von einem Cookie zurückgeben zu lassen, brauchen Sie nur Bezug nehmen auf den Namen des Cookies, also $cookiename. Werfen Sie einen Blick auf das abgebildete Script für den Counter mit Cookie. Sie finden hier die Zeile:

setcookie('willi',time());  

Damit wird das Cookie gesetzt.

<?php  if(!file_exists("count.txt")){fopen("count.txt", "a" );}  $counter=fopen("count.txt","r+");  if(!$willi OR $willi<time()-60)  {  setcookie("willi",time());  $aufruf=$aufruf+1;  rewind($counter);  fputs($counter,$aufruf);  }  $aufruf=(string) $aufruf;  for($i=0;$i<strlen($aufruf);$i++)  {  echo "<img src='c1_".$aufruf[$i].".gif'>";  }  ?>  

Erläuterung des Scripts

Das Script beginnt wie das vorherige einfache Script. Neu und ergänzt ist es ab der Zeile

if(!$willi OR $willi<time()-60)  {  

„Willi“ ist unser Name für das Cookie (das noch zu setzen ist). In der damit korrespondierenden Variablen $willi steht – sofern das Cookie „willi“ vorhanden ist – die Zeit des letzten Aufrufs der Seite. Damit das Cookie nur gesetzt wird, und der Zähler um eins hoch gezählt wird, wenn die Seite noch nicht besucht wurde, oder wenn seit dem letzten Aufruf länger als 60 Sekunden vergangen sind, wird mit einer if-Bedingung Folgendes getestet:

Ist die Seite zuvor schon besucht worden, wenn nicht, dann ist $willi naturgemäß nicht gesetzt, oder – wenn die Seite schon besucht wurde -, ist eine gewisse Zeit seit diesem Besuch (im Beispiel 60 Sekunden) verstrichen.

Deshalb wird die aktuelle Zeit des Aufrufs (time()) mit der Zeit des vorherigen Aufrufs, die im Cookie „willi“ steht, verglichen, wobei eine Zeitdifferenz von 60 Sekunden berücksichtigt wird. Hierzu ist anzumerken, dass die Funktion time() immer die aktuelle Zeit in Sekunden zurückgibt, die seit dem 01.01.1970 um 00:00:00 Uhr verstrichen sind. Info int time()

Die Funktion gibt die aktuelle Zeit zurück, die über den sogenannten UNIX-Zeitstempel ermittelt wird. Der zählt die Sekunden ab dem 01.01.1970 um 00:00:00 Uhr.

Ist eine der Bedingungen in der if-Anweisung erfüllt, soll das Cookie erstmalig gesetzt bzw. mit einem neuen Wert gesetzt (überschrieben) werden. Mit setcookie("willi", time()); wird das Cookie mit dem Namen „willi“ und der aktuellen Zeit des Servers gesetzt.

Danach legen wir fest, dass die gezählten Aufrufe (also der jeweilige Zählerstand) um eins erhöht werden. Dies ist ganz einfach gemacht. In der Variablen $aufruf ist der Wert gespeichert, den die Funktion fgets zurückgegeben hat. Also legen wir jetzt fest: $aufruf=$aufruf+1;.

Sodann muss in der Textdatei, in der der Zählerstand gespeichert wird, der Cursor wieder auf den Anfang gesetzt werden, damit der neue Zählerstand den alten überschreiben kann. Dazu benutzen wir wie gehabt die Funktion rewind($counter);.

Danach folgt die Zeile, um den neuen Zählerstand in die Datei zu schreiben. Dies ist die letzte Anweisung im if-Block, der dann mit der geschweiften Klammer geschlossen wird.

fputs($counter,$aufruf);  }  

In der if-Anweisung wird der Zähler also nur dann hochgezählt, wenn die Seite das erste Mal aufgerufen wird, bzw. die festgesetzte Zeitspanne (60) verstrichen ist, ansonsten wird der unveränderte Zählerstand, der vor der if-Anweisung ausgelesen wird, verwendet.

Darstellung der Ziffern

Danach, d.h. in den folgenden Zeilen des Scripts, geht es darum, wie der Zählerstand angezeigt wird. Für die weitere Darstellung des Counters muss sichergestellt werden, dass die Variable $aufruf als String vorliegt, damit sie entsprechend editiert werden kann (Wir erklären weiter unten noch genauer, warum $aufruf eine string-Variable sein muss). Dafür gibt es den einfachen Befehl string

$aufruf=(string) $aufruf;  

Info (string)

Die Funktion verwandelt eine Variable in den Datentyp string.

Der Zählerstand soll mithilfe von kleinen Bildern angezeigt werden. Für jede Ziffer des Zählerstandes wird eine GIF-Datei angezeigt, die die entsprechende Ziffer abbildet. (Für den Zählerstand 250 beispielsweise werden drei GIF-Dateien nebeneinander gesetzt.) Die Namen der GIF-Dateien sind alle identisch bis auf die letzte Ziffer, die entsprechend des angezeigten Wertes verändert wird. Sie brauchen also 10 kleine GIF-Dateien, die die Ziffern 0 bis 9 darstellen (diese Bildchen müssten Sie nun, wenn Sie unser Beispiel mitspielen, in einem entsprechenden Programm erstellen).

Dann setzen Sie eine for-Schleife ein, damit jeweils eine Stelle des Zählers angezeigt wird. Die for-Schleife muss also entsprechend der Anzahl der Ziffern durchlaufen werden. Dazu ist zunächst die Anzahl der Ziffern zu ermitteln. Auch dafür gibt es eine Funktion. Sie lautet strlen().

Diese Funktion gibt die Länge eines Strings zurück. Die Zeile heißt dann (achten Sie darauf, dass in for-Schleifen die Argumente mit Semikolon getrennt werden):

for($i=0;$i<strlen($aufruf);$i++)  {  

Info int strlen(string zeichenketteX)

Die Funktion gibt die Länge einer Zeichenkette (zeichenketteX) zurück.

Verwendung von GIF-Dateien

Um die Ziffern darzustellen, müssen wir auf die einzelnen Elemente (des Zählerstands) zugreifen können. Aus diesem Grund haben wir weiter oben $aufruf als String definiert, denn in PHP kann man auf die einzelnen Stellen einer String-Variablen wie auf ein Array zugreifen. Im nullten Element wird die erste Ziffer von links gespeichert.

Ein Beispiel: Bei einem Zählerstand von 250 ist $aufruf[0]=2, $aufruf[1]=5 und $aufruf[3]=0. Sodann brauchen wir den echo-Befehl, damit das Bild mit der richtigen Ziffer angezeigt wird. (Die Dateien heißen bei uns c1_0.gif etc.) Beim ersten Schleifendurchlauf ist die gesetzte Variable $i=0, somit wird $aufruf[0] verwendet und echo gibt aus: <img src='c1_2.gif'>. Das Bild c1_2 soll vereinbarungsgemäß eine 2 anzeigen. Beim nächsten Durchlauf wird der Wert für $aufruf[1] verwendet, somit wird die Datei c1_5.gif angezeigt. Als Programmzeile sieht das dann so aus:

echo "<img src='c1_".$aufruf[$i].".gif'>";  }  

Cookies, Cookies

Obwohl gern gegessen, haben Cookies im Internet einen eher schlechten Ruf. Bei Usern, die sich noch nicht näher mit dem Thema befasst haben, ist die Meinung weit verbreitet, dass über Cookies persönliche Daten aller Art weitergereicht werden. Dabei sind Cookies, auch die im Internet, zunächst einmal recht harmlos. Es handelt sich um Textinformationen, die auf Veranlassung eines Webservers z.B. mithilfe von PHP durch den Browser gespeichert werden. Wird die Webseite erneut aufgerufen, oder eine Seite der gleichen Domäne, sendet der Browser die Textinformation des Cookies an den Webserver. Von daher sind Cookies zunächst einmal völlig unproblematisch. Der Webserver kann nur Informationen in Cookies speichern, die ihm ohnehin schon bekannt sind und somit auch nur diese Informationen vom Browser wieder zurückgesandt bekommen.

Bei dieser Beschreibung sind Sicherheitslücken der Browser nicht berücksichtigt. So lassen der Internet-Explorer 5 und Internet-Explorer 6 auch einen Zugriff von anderen Domänen zu, sofern das entsprechende Sicherheitspatch nicht nachinstalliert wurde.

Cookies werden inzwischen bei fast allen dynamischen Webseiten eingesetzt und sie sind fast unvermeidbar auf Webseiten, bei denen Sie sich mit Benutzername und Passwort anmelden, da beim Wechsel von einer Webseite zur nächsten ihre Anmeldung nicht vergessen werden darf. Der Webserver ohne Cookies hätte keine vernünftige Möglichkeit, festzustellen, dass Sie vor fünf Minuten die Login-Seite besucht haben, und nun auf die Seite mit den neuesten Angeboten gewechselt sind. Auch die meisten Counter (Zähler) arbeiten mit Cookies, um eine sogenannte Reload-Sperre zu realisieren, also dafür zu sorgen, dass der Counter nicht jeden Klick auf den Aktualisieren-Button mitzählt. Fazit: Wenn sie sich dazu entschließen, Cookies zu deaktivieren, verzichten Sie auch auf eine Menge nützlicher Auswirkungen dieser kleinen Kekse.

Missbrauch von Cookies?

Aber es gibt mal wieder zwei Seiten der gleichen Medaille. Missbraucht werden Cookies mitunter dazu, das Surfverhalten von Internet-Usern auszuspionieren. Laut Festlegung können Cookies nur von der Domäne gelesen werden, von der sie auch gesetzt wurden, sodass zunächst nur Ihr Surfverhalten innerhalb einer Domäne verfolgt werden kann. Aber pfiffige Menschen haben eine Methode entwickelt, Cookies auch von außerhalb der besuchten Domäne auf Ihren Browser zu setzen. Hierbei handelt es sich um die Cookies von Drittanbietern.

Um ein Beispiel zu geben: Über den Aufruf eines Bildes innerhalb der besuchten Webseite, das auf der Domäne des Drittanbieters liegt, kann diese Domäne einen Cookie bei Ihnen setzen bzw. die gesetzten auslesen.

Was heißt das nun für Sie als Surfer? Kommerzielle Firmen machen Webseitenanbietern das Angebot, Daten über das Surfverhalten ihrer Besucher zu generieren. Alle Webseitenanbieter, die sich einer dieser Firmen, z. B. der Firma XY, anschließen, fügen eine kleine Grafik von der Domäne XY auf ihren Webseiten ein. Besuchen Sie eine solche Webseite, z.B. die von „Müller und Sohn“, das erste Mal, so wird ein Cookie der Domäne XY mit einer eindeutigen Nummer, z.B. 4711 bei Ihnen gesetzt. Die Firma XY speichert, dass Sie als 4711 die Webseite „Müller und Sohn“ besucht haben. Landen Sie beim Surfen bei einem weiteren Kunden der Firma XY, z.B. bei „Meier und Tochter“, so wird Ihre Nummer 4711 ausgelesen und die Firma XY speichert, dass Sie als 4711 auch bei „Meier und Tochter“ zu Besuch waren. So zieht sich das von Kunde zu Kunde der Firma XY. Schließlich lässt sich, wenn die Kunden der Firma XY ihre Daten abgleichen, ohne größeren Aufwand folgende Zuordnung vornehmen: Sie als 4711 haben bei „Müller und Sohn“ Zahnpasta und Rasierschaum bestellt und bei der Firma „Meier und Tochter“ die Seiten über Rasenmäher und Gartenmöbel angeschaut. Schon kann man mit einiger Plausibilität den Schluss ziehen, dass Sie männlich und Hausbesitzer sind, sodass bei weiteren Besuchen der Kundenseiten der Firma XY die Werbeeinblendungen oder Artikelpräsentation auf Sie zugeschnitten werden können!

Nachdem die bedenklichen Seiten der Cookies von Drittanbietern beschrieben wurde, an dieser Stelle auch Beispiele, wo diese Cookies sinnvoll sind und allseits nützlich eingesetzt werden.

Webmaster binden häufig Inhalte/Angebote von so genannten Content Providern in ihre Seiten ein. Mitunter ist für die Funktion dieses eingebundenen Contents das Akzeptieren von Cookies von Drittanbietern Voraussetzung. Beispiele für diese Art Content sind: Newsticker, Gästebücher, Foren, Chats, Counter, Umfragen etc.

Ein Counter mit fester Stellenanzahl

Das Script kann noch erweitert werden, wenn Sie wünschen, dass der Zählerstand mit einer festen Anzahl an Stellen angezeigt wird. Die ersten Stellen von links werden mit Nullen aufgefüllt. Um dies zu erreichen, wird nur der Teil des Scripts, der den Zählerstand ausgibt, verändert, der erste Teil, der den Zählerstand ermittelt, bleibt wie gehabt. Die Mühe hält sich also in Grenzen!

Das erweiterte Script

Das Script, das ein Cookie setzt, die Ziffern als GIFs anzeigt und für die feste Stellenanzahl sorgt, sehen Sie unten im Code.

<?php  if(!file_exists("count.txt")){fopen("count.txt", "a" );}  $counter=fopen("count.txt","r+");  if(!$willi OR $willi<time()-60)  {  setcookie("willi",time());  $aufruf=$aufruf+1;  rewind($counter);  fputs($counter,$aufruf);  }  $aufruf=(string) $aufruf;  $temp=0;  for($i=0;$i<6;$i++)  {  if(6-$i>strlen($aufruf))  {echo "<img src='c1_0.gif'>";}  else  {  echo "<img src='c1_".$aufruf[$temp].".gif'>"; $temp=$temp+1;  }  }//Ende for  ?>  

Erläuterung des Scripts

Der erste Teil ist – wie gesagt – unverändert. Sie können Ihre Aufmerksamkeit also gleich den Änderungen widmen, die mit der for-Schleife beginnen. Mit $temp wird ein Hilfszähler zunächst auf 0 gesetzt. Mit Hilfe dieser Variablen wird die tatsächliche Anzahl vorhandener Stellen des Zählerstandes durchlaufen, $temp=0;

Mittels der for-Schleife wird die gewünschte Anzahl der angezeigten Stellen des Zählers, in diesem Fall 6, durchlaufen.

for($i=0;$i<6;$i++)  {  

Sodann folgt wieder eine if-Bedingung. Mit dieser if-Anweisung wird die GIF-Datei mit der 0 für die führenden Stellen des Zählers ausgegeben. Wir müssen also anweisen: solange $i noch nicht in dem Bereich der Stellen, deren Werte aus dem Zählerstand ermittelt werden, angelangt ist, ist die Bedingung erfüllt (und die entsprechende GIF-Datei c1_0.gif wird angezeigt):

if(6-$i>strlen($aufruf))  {echo "<img src='c1_0.gif'>";}  else  {  echo "<img src='c1_".$aufruf[$temp].".gif'>";  $temp=$temp+1;  }  

Zählerstand 250: ein Beispiel

Das Ganze ist ein bisschen trickreich und deshalb nehmen wir zur Verdeutlichung mal ein konkretes Beispiel an: Angenommen, der Zählerstand ist 250, dann ist strlen($aufruf)= 3 (drei Ziffern). Eine sechsstellige Zahl soll angezeigt werden, die ersten drei Stellen von links müssen also jeweils eine Null aufweisen. Folglich muss die Prüfung der if-Bedingung true ergeben. Dies ist für $i=0 bis $i=2 der Fall, denn nach Adam Riese gilt: 6-0>3, 6-1>3, 6-2>3. Mit echo wird die entsprechende GIF-Datei ausgegeben. Anschließend ist die Bedingung false: 6-3 ist nicht >3.

Ab jetzt wird der else-Zweig ausgeführt. Hier wird der Hilfszähler $temp wichtig, da der Wert nicht wie oben in $aufruf[$i] zu finden ist, (da im Beispiel $i=3 ist) und $aufruf[3] somit eine 0 ausgeben würde (dritte Stelle von 250). Es wird also für die folgenden Schleifendurchläufe, wenn der else-Zweig ausgeführt wird, nacheinander $aufruf[0], [1], [2] benötigt. Dies erreichen wir durch den Hilfszähler, der jeweils um eins erhöht wird.

Die letzte geschweifte Klammer schließt die for-Schleife.

Erweiterung für „viel besuchte“ Webseiten

Im obigen Beispiel wurde die maximale Anzahl der Stellen des Zählers auf 6 gesetzt, diese Zahl können Sie natürlich im Script ändern. Aber was passiert, wenn ein ungeahnter Besucheransturm – Sie haben z.B. die neueste Version Ihres Moorhuhn-Ablegers gerade ins Netz gestellt – Ihren Zähler innerhalb von Minuten diese Grenze sprengen lassen. Im obigen Beispiel würde der Zähler die hinteren Stellen der Besucherzahl einfach abschneiden. Bild 6 zeigt den Zählerstand in der Textdatei und im Browser.

Für diesen Fall können Sie das Script so anpassen, das der Counter aus mindestens 6 Stellen besteht, sollte der Zählerstand höher sein, werden aber auch mehr Ziffern angezeigt. Code unten zeigt das modifizierte Scriptfragment:

$aufruf=(string) $aufruf;  $temp=0;  $anzstellen=max(6, strlen($aufruf));  for($i=0;$i<$anzstellen ;$i++)  {  if($anzstellen-$i>strlen($aufruf))  {echo "<img src='c1_0.gif'>";}  else  {  echo "<img src='c1_".$aufruf[$temp].".gif'>";  $temp=$temp+1;  }  }//Ende for  ?>  

Erläuterung des erweiterten Scriptteils

Das Script hat nur kleine Veränderungen zum Vorgänger. Anstatt die gewünschte Anzahl der Stellen festzusetzen, wird diese in einer Variablen namens $anzstellen gespeichert. Diese Variable findet in der for-Schleife und in der if-Bedingung Verwendung:

for($i=0;$i<$anzstellen ;$i++)  

sowie

if($anzstellen-$i>strlen($aufruf))  

Der Trick: wir weisen der Variablen zuvor die gewünschte Anzahl mindestens anzuzeigender Stellen oder, wenn die Länge des Zählerstands größer ist, die Länge des Zählerstandes zu.

$anzstellen=max(6, strlen($aufruf));  

Die Funktion max() gibt immer den höchsten Wert der angegebenen Argumente zurück, im Beispiel also 6 oder die Länge des Zählerstandes.

Wenn die Ergänzung geklappt hat, sind der Zählerstand der Textdatei und der angezeigte Zählerstand nun identisch, zu sehen in Bild 7. Info mixed max(mixed argument1, argument2,)

Die Funktion ermittelt den höchsten Wert der übergebenen Werte. Zwei Werte sind logischerweise zwingend erforderlich.

Ausblick – Identifikation über die IP-Adresse

Cookies genießen einen schlechten Ruf und aus diesem Grund haben viele Surfer Cookies im Browser deaktiviert. In diesem Fall wird die Reloadsperre nicht funktionieren, da kein Cookie gesetzt werden kann. Es wird beim User aber auch keine Fehlermeldung angezeigt, der Counter wird einfach bei jedem Aufruf hochgezählt, egal, ob 60 Sekunden seit dem letzten Aufruf vergangen waren oder nicht.

Anstelle von Cookies wird deswegen mitunter auch die IP-Adresse des Users verwendet, um festzustellen, ob der Surfer die Seite zuvor besucht hat oder nicht. Der Browser sendet bei jedem Aufruf die IP-Adresse, die der Surfer – meistens dynamisch – von seinem Provider zugewiesen bekommen hat, an den Server. Anhand der IP-Adresse kann man den Surfer an sich identifizieren, da die zugewiesene IP-Adresse einmalig ist.

Die Sache hat aber einen Haken, da alle Theorie grau ist: die Zuordnung ist leider nur angeblich eindeutig, in der Praxis werden die dynamischen IP-Adressen mitunter während des Surfens vom Provider geändert oder der User verwendet einen Proxyserver oder NAT-Server, sodass unter Umständen mehrere Besucher der Seite die gleiche IP-Adresse aufweisen.

Welche Lösung für einen Counter zu bevorzugen ist, kann man nicht pauschal sagen. Es ist abzuwarten, ob der Trend, Cookies auszuschalten, abflaut oder nicht. Im Allgemeinen ist es so, dass Sie über den Counter mit Cookies eher mehr Besucher zählen, da mitunter die Cookies deaktiviert sind, während Sie über die IP-Adresse eher weniger Besucher zählen, da Proxy- und NAT-Server unabhängig davon, wie viele Menschen über die Server auf Ihre Seite gleichzeitig zugreifen, nur als ein Surfer/Aufruf gewertet werden.

Die IP-Methode

Dieses Verfahren über die IP-Adresse wird hier aus Platzgründen nur prinzipiell erklärt, es folgt kein ausführliches Script.

Der Weg ist folgender:

Bei jedem Aufruf der Seite wird die IP-Adresse des Surfers in einer Datenbanktabelle gesucht. Ist noch kein Eintrag vorhanden, wird der Seitenaufruf gezählt und ein neuer Datensatz in die Tabelle mit der IP-Adresse des Users und der aktuellen Uhrzeit geschrieben. Ist bereits ein Datensatz mit der IP-Adresse vorhanden, wird getestet, ob der letzte Aufruf lange genug zurückliegt, um den Aufruf erneut zu zählen. Ist dies der Fall, wird der Zähler um eins erhöht und der Datensatz mit der IP-Adresse mit der neuen aktuellen Uhrzeit aktualisiert. Ist noch nicht genug Zeit verstrichen, passiert nichts, weder wird der Zähler verändert noch die Uhrzeit des Datensatzes.

Bei diesem Verfahren sammeln sich schnell viele Einträge in der Datenbank, denn jeder Besucher generiert einen Datensatz. Daher ist es sinnvoll, von Zeit zu Zeit aufzuräumen und alte Datensätze zu löschen. Am einfachsten geht dies, wenn Sie bei jedem Aufruf alle Datensätze löschen, deren Uhrzeit anzeigt, dass der Aufruf länger als 1 Stunde (oder Ähnliches) zurückliegt.

Für die Realisierung dieser Variante benötigen Sie die IP-Adresse des Users. Diese stellt Ihnen PHP in der Variablen $REMOTE_ADDR zur Verfügung.

Oft ist es erwünscht, eine Benutzereingabe nicht nur auf einer Internetseite anzuzeigen, sondern den Benutzer auch via E-Mail davon zu informieren. E-Mails sind leichter zu archivieren als Verweise auf dynamische Webseiten und werden deshalb oft als Bestätigung von Bestellungen oder Reservierungen zusätzlich eingesetzt. In der einfachsten Form reicht eine Zeile PHP-Code aus, um eine E-Mail zu verschicken:

mail("user@server.com", "Greetings", "Hello User");  

Mit dieser Anweisung wird die Nachricht mit dem Betreff „Greetings“ an den Benutzer user bei server.com zugestellt. Der Inhalt der Nachricht beschränkt sich auf die Zeile „Hello User“. Diese Form hat aber noch einige Nachteile: So wird als Absender der E-Mail der Benutzeraccount angegeben, unter dem der Webserver läuft (oft apache oder www-data). Je nachdem, ob und wie die Namesrückauflösung für den Server eingestellt ist, wird der Domain-Name ergänzt, was oft dazu führt, dass der Empfänger nicht auf diese Nachricht antworten kann.

Damit Sie weitere Details des Mail-Transports mit PHP verstehen, werden im Folgenden einige grundsätzliche Eigenschaften der E-Mail-Zustellung erläutert. Info Der E-Mail-Standard RFC822 verbietet es, dass Zeilen im Text von E-Mails einfach durch line feed (entspricht \n) getrennt werden. Stattdessen ist eine Trennung durch carriage return und line feed vorgeschrieben. Wenn Sie zum Versenden von E-Mails die PHP-Funktion mail verwenden, müssen Sie unbedingt darauf achten, statt \n die Kombination \r\n zu verwenden!

Probleme beim E-Mail-Versand

Das Versenden von E-Mails von lokalen Testrechnern bereitet oft Probleme: Unter Windows steht zumeist kein lokaler E-Mail-Server zum Versenden zur Verfügung. php.ini kann zwar so eingestellt werden, dass mail auf den SMTP-Server Ihres Internet-Providers zugreift, aufgrund verschiedener SPAM-Schutzmaßnahmen kann es aber dennoch passieren, dass der SMTP-Server die von PHP kommenden E-Mails einfach ignoriert.

Unter Linux läuft zwar oft sendmail oder postfix, aber diese Programme müssen häufig erst konfiguriert werden. Mit diesem Thema lassen sich eigene Bücher füllen, weswegen hier ein einziger Tipp ausreichen muss: Stellen Sie sicher, dass der Hostname des E-Mail-Servers richtig eingestellt ist und dass es sich dabei um einen im Internet gültigen Namen handelt (nicht nur im lokalen Netz)! Falls Sie postfix verwenden, ändern Sie in /etc/postfix/main.cf den Parameter myhostname und starten postfix anschließend neu.

Selbst wenn das Versenden an sich klappt, wird die E-Mail vom Empfänger möglicherweise abgelehnt,

wenn die IP-Adresse des Absenders (also die IP-Adresse, unter der Ihr Rechner aus dem Internet sichtbar ist) und der vom E-Mail-Server verwendete Hostname nicht übereinstimmen. Diese an sich vernünftige Maßnahme versucht das Versenden von SPAM zu unterbinden.

Bevor Sie eine Menge Zeit vergeuden, um das Versenden von E-Mails von Ihrem lokalen Rechner in den Griff zu bekommen, testen Sie die mail-Funktion Ihrer PHP-Projekte auf der PHP-Installation Ihres ISPs bzw. auf einem Root-Server: In der Regel treten dabei keine Probleme auf, sofern der Domainname in Ihrer Absenderadresse mit dem tatsächlichen Hostnamen des Rechners übereinstimmt.

PHP-Mail mit sendmail unter Unix/Linux

Die Arbeitsweise der mail()-Funktion unterscheidet sich grundlegend unter Windows- und UNIX-Betriebssystemen. So steht unter UNIX-Betriebssystemen ein Programm zur Verfügung, das sich um den Mail-Transport kümmert, ein Mail Transport Agent (MTA). Das macht sich PHP zunutze und delegiert die Arbeit des E-Mail-Verschickens an dieses Programm. In der PHP-Konfiguration können spezielle Optionen für den Programmaufruf angegeben werden, zum Beispiel der Pfad, sollte das Programm nicht standardmäßig installiert worden sein.

Der am weitesten verbreitete MTA ist sendmail. Bekannt durch seine nicht triviale Konfiguration und die Flexibilität, ist sendmail heute auf großen Mailservern von Universitäten und Firmen gleichermaßen wie auf dem Linux-Desktop im Einsatz. Inzwischen gibt es einige Programme, die die Funktionalität von sendmail nachahmen und dabei eine übersichtlichere Konfiguration versprechen. Qmail, postfix oder smail sind einige der prominenten Vertreter. Da alle diese Programme ein sendmail-Programm mitbringen, das noch dazu die wichtigsten Parameter des Originals unterstützt, ändert sich für die PHP-Konfiguration nichts.

Die weitere Zustellung hängt von der Konfiguration des MTA ab.

Dort kann eingestellt werden, ob die Nachricht direkt an den Mailserver des Empfängers übermittelt oder an einen anderen SMTP-Server weitergeleitet wird. Für PHP ist der Prozess abgeschlossen.

Um das in der Einleitung angesprochene Problem mit dem falschen Absender zu lösen, kann der MTA zusätzlich zum E-Mail-Text auch die Kopfzeilen der E-Mail verändern, um den richtigen Absender einzutragen. So reicht die Kopfzeile

From: anotherUser@server.com  

aus, um dem Empfänger die richtige Absenderadresse mitzuteilen. Der Aufruf im PHP-Code ändert sich damit auf:

mail("user@server.com", "Greetings", "Hello User", "From: anotherUser@server.com");  

Die Umsetzung weiterer Kopfzeilen (zum Beispiel From, Reply-To, User-Agent, …) erledigt der MTA.

PHP-Mail via SMTP unter Windows

Unter Windows-Betriebssystemen gehört das sendmail-Paket nicht zum Standardumfang. Hier braucht man eine andere Strategie, um eine E-Mail zu verschicken. PHP kontaktiert dazu einen externen Mailserver und gibt die Nachricht an diesen weiter. In der Konfigurationsdatei wird dieser Eintrag in der Sektion [mail functions] mit SMTP bezeichnet. Dieser SMTP-Server wird auch beim traditionellen E-Mail als Postausgangsserver oder eben SMTP-Server eingestellt.

Wegen der Problematik von unerwünschten E-Mail-Nachrichten (SPAM) nehmen richtig konfigurierte SMTP-Server nur Nachrichten an, für die sie auch zuständig sind. Ist man beispielsweise über ein Einwahlnetzwerk mit dem Internet verbunden, so stellt in der Regel der Provider einen SMTP-Server zur Verfügung.

Ist die Konfiguration von PHP richtig eingestellt, funktioniert das E-Mail-Verschicken auch unter Windows.

Einschränkungen gibt es allerdings für PHP-Versionen vor 4.3: Extra Kopfzeilen (zum Beispiel From:, Reply-To:, …) werden nicht umgesetzt. Einzig die Cc:-Kopfzeile funktioniert auch mit älteren PHP-Versionen unter Windows.

Sehr strikte Server-Administratoren verlangen auch für das E-Mail-Verschicken einen gültigen Benutzeraccount und ein Passwort am SMTP-Server. Das in RFC2554 festgeschriebene Verfahren ist noch eher jung (1999), und daher unterstützen es noch nicht alle Mail-Clients. Um mit PHP einen authentifizierten E-Mail-Versand zu erreichen, bedient man sich am besten der PEAR-Mail-Klassen, die seit PHP 4.1 zum Standardumfang von PHP gehören.

E-Mail mit PEAR

Die in PEAR enthaltene Klasse zum E-Mail-Versand (PEAR::Mail) geht über die Funktionalität des PHP-mail()-Aufrufs hinaus. So können mit PEAR::Mail auch Attachments verschickt oder HTML-E-Mails erzeugt werden. Die Art des Versands (smtp oder sendmail, bei PEAR als Treiber oder Backend bezeichnet) kann von Fall zu Fall ausgewählt werden.

Um PEAR::Mail zu verwenden, muss als Erstes eine Instanz der Hauptklasse Mail erzeugt werden:

$mail_inst =& Mail::factory('smtp', $params);  

Die Instanz wird von der Funktion factory angelegt, der als erster Parameter der Treiber (in diesem Fall smtp) und weiters ein Array mit Einstellungen übergeben wird. Erfordert der Postausgangsserver zum Beispiel eine Validierung, könnten die Parameter so aussehen:

$params['host'] = 'smtp.server.com';  $params['port'] = '25';  $params['auth'] = true;  $params['username'] = 'user';  $params['password'] = 'password';  

Plain-Text-E-Mail mit PEAR

send_text_mail.php verschickt eine kurze Textnachricht über den Server smtp.server.com. Die Kopfzeilen (Betreff, Absender und Empfänger) können dabei übersichtlich als Array definiert werden.

<?php // Datei mail/send_text_mail.php  require('Mail.php');  $headers['From'] = 'Another User <anotheruser@host.com>';  $headers['To'] = 'User <user@host.com>';  $headers['Subject'] = 'Testnachricht von PEAR::Mail';  $recipients = 'user@host.com';  $body = 'Hallo User!  Das ist eine kleine Testnachricht.';  $params['host'] = 'smtp.server.com';  $params['port'] = '25';  $mail_object =& Mail::factory('smtp', $params);  if ($mail_object->send($recipients,  $headers, $body)== TRUE) {  echo 'Ihre Nachricht wurde verschickt';  } else {  echo 'Fehler beim Senden Ihrer Nachricht.';  }  ?>  

Ohne die Kopfzeile für den Empfänger ($headers['To']) wird die E-Mail zwar trotzdem zugestellt (beim send-Aufruf wird $recipients verwendet), das E-Mail-Programm zeigt aber keine Adresse an.

MIME-E-Mail mit PEAR

Im nächsten Beispiel wird folgendes Szenario behandelt: Auf einer Internetseite befinden sich drei Bilder, aus denen der Benutzer eines auswählen kann und es via E-Mail an einen Freund verschickt. Zu dem Bild kann noch ein kurzer Text eingegeben werden. Info

Alternativ kann das Paket auch mit dem PEAR-Installer eingespielt werden:

root# pear install Mail_MIME  

Um eine gültige E-Mail mit einem binären Anhang zu erzeugen, muss die Nachricht in das MIME-Format umgewandelt werden. Diese Prozedur selbst zu programmieren, ist etwas aufwändig, aber zum Glück gibt es eine Klasse im PEAR-Paket, die diese Arbeit erledigt: Mail_mime. Diese Klasse erzeugt die Kopfzeilen und den Inhalt der E-Mail und wird dann mit der schon bekannten Funktion send aus der PEAR::Mail-Klasse verschickt.

$mime_message = new Mail_mime();  $mime_message->setTXTBody('Im Anhang das Bild');  $mime_message->addAttachment('/tmp/flower.jpg', 'image/jpeg');  $mime_body = $mime_message->get();  $mime_hdr = $mime_message->headers($headers);  

Nach diesen Aufrufen enthält $mime_body den gesamten Inhalt der E-Mail (inklusive des Bilds flower.jpg) und das Array $mime_hdr die Kopfzeilen. Um möglichst plattform-unabhängig zu sein, wird das Bild dabei in base64 kodiert, einem Format, das nur mit ASCII-Zeichen auskommt.

Die gesamte Anwendung kann in einer Datei implementiert werden:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  <html lang="de">  <head>  <title>E-Mail-Grußkarten</title>  <meta http-equiv="Content-type" content="text/html; charset=iso-8859-1">  </head>  <body>  <?php // Datei: mail/send_image.php  require 'Mail.php';  require 'Mail/mime.php';  if ($_POST['submit'] === 'Abschicken') {  if (strlen($_POST['message']) > 400) {  echo 'Fehler: Der Text Ihrer Nachricht ist zu lang';  echo '<br><a href="send_image.php">Neuer Versuch</a>';  echo '</body></html>';  exit();  }  

Nach der Ausgabe der HTML-Kopfzeilen werden die benötigten PEAR-Klassen geladen: Mail.php (zum Überprüfen der Empfängeradressen) und mime.php. Wurde das Formular abgeschickt ($_POST['submit'] === 'Abschicken'), so beginnt die Ausführung des PHP-Codes:

Zuerst wird die Länge der Nachricht überprüft. Sind es mehr als 400 Zeichen, wird das Programm beendet und der Anwender kann neu beginnen. Ist die Länge der Nachricht korrekt, werden der oder die Empfänger überprüft:

// überprüfen der eingegebenen Empfänger  $mail_instance = new Mail();  $recipients = $mail_instance->parseRecipients($_POST['email']);  if (count($recipients) <= 0) {  echo 'Es wurden keine gültigen Empfänger ermittelt';  echo '<br><a href="send_image.php">Neuer Versuch</a>';  echo '</body></html>';  exit();  }  

Dazu wird eine Instanz der Mail-Klasse benötigt, über die dann der Aufruf an parseRecipients gültige Adressen aus einer Zeichenkette extrahiert (intern wird dabei auf die Klasse RFC822 zurückgegriffen). Das erspart eine Menge Arbeit beim Überprüfen der Eingabe und ermöglicht eine flexible Angabe der E-Mail-Adressen. So kann parseRecipients aus den Strings

Username <user@server.com>, anotherUser@server.com  

und

user@server.com  

gültige Adressen herausfiltern. Der Rückgabewert der Funktion (das Array $recipients) enthält nur mehr gültige E-Mail-Adressen. Ist diese Variable leer, wird die Ausführung mit dem Hinweis beendet, dass keine Empfänger gefunden werden konnten.

$headers = array (  'Subject' => 'Eine Nachricht mit Bild',  'From' => 'Internetformular <webform@server.com>'  );  $mime_message = new Mail_mime();  $mime_message->setTXTBody($_POST['message']);  $mime_message->addAttachment($_POST['img'], 'image/jpeg');  $mime_body = $mime_message->get();  $mime_hdr = $mime_message->headers($headers);  $final_message =& Mail::factory('mail');  if ($final_message->send($recipients, $mime_hdr,  $mime_body) == TRUE) {  echo '<h1>Ihre Nachricht wurde verschickt</h1>';  } else {  echo '<p>Es ist ein Fehler aufgetreten, Ihre Nachricht konnte  nicht verschickt werden</p>';  }  }  ?>  

Im Anschluss werden die Kopfzeilen vorbereitet (in diesem Beispiel wird der Absender fix eingestellt), und die Nachricht wird, wie bereits beschrieben, in das MIME-Format konvertiert. Der Versand der Nachricht erfolgt über die Mail-Klasse von PEAR. In diesem Beispiel wird als Backend die von PHP zur Verfügung gestellte mail-Funktion verwendet.

<FORM action="send_image.php" method="POST">  <table summary="Bilder">  <caption>Bilder</caption>  <tbody>  <tr>  <td><IMG src="img1.jpg" alt="Bild 1" border="0"></td>  <td><IMG src="img2.jpg" alt="Bild 2" border="0"></td>  <td><IMG src="img3.jpg" alt="Bild 3" border="0"></td>  </tr>  <tr align="center">  <td>Bild 1<INPUT type="radio" checked name="img" value="img1.jpg"></td>  <td>Bild 2<INPUT type="radio" name="img" value="img2.jpg"></td>  <td>Bild 3<INPUT type="radio" name="img" value="img3.jpg"></td>  </tr>  </tbody>  </table>  <p>Nachricht (max. 400 Zeichen):</p>  <textarea name="message" cols="50" rows="10"></textarea>  <p>Empfänger:</p>  <INPUT type="text" name="email" size="25">  <INPUT type="submit" name="submit" value="Abschicken">  <INPUT type="reset" name="reset" value="Reset">  </FORM>  </body>  </html>  

Der restliche HTML-Code ist sehr schlicht und stellt die Auswahl der Bilder und die Textfelder für Nachricht und Empfänger zur Verfügung.

Das Problem tritt schon bei Betrieben mittlerer Größe auf: Ist das Besprechungszimmer heute um 15:00 Uhr frei, oder findet zu dieser Zeit bereits eine Sitzung statt? Wenn außerdem firmeneigene Geräte zum Ausleihen zur Verfügung stehen, ergibt sich bald die Notwendigkeit, ein Verwaltungssystem dafür zu installieren. Bei großen Unternehmen oder Universitäten können diese logistischen Probleme nicht ohne durchdachte Planung bewältigt werden.

Dem vorliegenden Beispiel fehlt eine wichtige Komponente, um es in der Praxis zu verwenden: die Multi-User-Fähigkeit. Es sieht nicht vor, dass mehrere Benutzer von unterschiedlichen Arbeitsplätzen aus die gleichen Daten bearbeiten können. Dieser Mangel kommt daher, dass die von Ihnen eingegebenen Daten lediglich in einer Session gespeichert werden. Um das System Multi-User-fähig zu machen, müssten die Daten in einer Datenbank abgelegt werden – aber das würde hier zu viel Wissen voraussetzen. Das Beispiel erfüllt daher ausschließlich den Zweck, Ihnen die objektorientierten Konzepte zu verdeutlichen.

Der Vorteil an der Session-Speicherung ist, dass die Methoden zum Speichern und Abfragen der Objekte sehr einfach sind. In der Session-Umgebung wird ein neues Array erzeugt, in dem alle Objekte abgelegt werden. Der Nachteil an diesem Ansatz ist, dass alle Daten im digitalen Nirwana verschwinden, sobald Sie Ihren Browser schließen. Info

Bedienung der Anwendung

Zu Beginn der Anwendung müssen Sie Ressourcen erzeugen, die zur Verwendung bereitstehen. Im vorliegenden Beispielprogramm stehen Ihnen fünf verschiedene Ressourcen zur Verfügung: ein Besprechungszimmer, ein Unterrichtsraum, ein Beamer, eine Digitalkamera und ein Flipchart. Alle diese Objekte haben unterschiedliche Eigenschaften, z.B. kann bei einem Besprechungszimmer die Anzahl der Sitzplätze eingegeben werden, während bei einem Beamer die Auflösung gespeichert wird.

Nachdem Sie eine oder mehrere Ressourcen hinzugefügt haben, können Sie beginnen,

Ihre Termine vorzumerken. Wählen Sie dazu die Verfügbarkeitsanzeige des gewünschten Geräts oder Raums, und geben Sie dort den Zeitraum ein. Kommt es beim Eintragen zu einer Überschneidung mit einem bereits gebuchten Termin, werden Sie darauf hingewiesen. Abbildung 1 zeigt die Verfügbarkeit eines Unterrichtsraums und den Versuch, einen bereits gebuchten Termin zu reservieren.

Sobald Sie Ihren Browser schließen, gehen die Session und mit ihr alle Ressourcendaten verloren. Sie können die Daten aber auch explizit löschen, in dem Sie den Link ALLE DATEN LÖSCHEN anklicken. Dabei werden alle relevanten Session-Variablen gelöscht und Sie werden auf die Startseite weitergeleitet.

Die Klassenstruktur

Die Basisklasse: Resource

Alle Klassen, die Sie als Ressourcen auswählen können, leiten sich von der Basisklasse Resource ab. Sie stellt diese Funktionen zur Verfügung, die alle Ressourcen gleich implementieren. Außerdem beinhaltet sie eine abstrakte Funktion: showCreate. Jede Subklasse muss diese Funktion überschreiben. Klassen, die eine oder mehrere abstrakte Funktionen beinhalten, werden als abstract class definiert. Abstrakte Klassen können Sie sich ein wenig wie Interfaces vorstellen. Von ihnen kann keine Instanz erzeugt werden, und sie geben bestimmte Funktionen vor, die überschrieben werden müssen. Der große Unterschied zu Interfaces ist aber, dass abstrakte Klassen sehr wohl Member-Variablen und nicht abstrakte Funktionen beinhalten können.

Zwei interessante Member-Variablen der Basisklasse sind als Arrays enthalten: In $reservations werden alle festgelegten Termine gespeichert, und $valid enthält eine Liste von gültigen Unterklassen.

abstract class Resource {  public $id;  public $name;  public $reservations = array();  // vorhandene Subklassen  public static $valid = array(  "MeetingRoom" => "Besprechungszimmer",  "LectureRoom" => "Unterrichtsraum",  "Beamer"      => "Beamer",  "DigiCam"     => "Digitalkamera",  "FlipChart"   => "Flipchart"  );  // zeigt ein HTML-Formular zum Erstellen einer neuen Klasse  // jede Unterklasse muss diese Funktion überschreiben  public abstract function showCreate();  

Auf diese Definitionen folgt die Funktion zum Buchen einer Zeiteinheit: book. Ihr wird eine Start- und eine Endzeit übergeben. Die Zeiten werden mit der strtotime-Funktion in einen UNIX-Timestamp umgewandelt und müssen daher den sehr flexiblen, aber leider englischsprachigen Zeit- und Datumsformatanweisungen entsprechen. Für einfache Datumsberechnungen sind Timestamps sehr praktisch, da es sich um Integerzahlen handelt, auf die die bekannten mathematischen Funktionen (>, <, ==, ...) angewendet werden können.

Ist eine der beiden Zeitangaben leer, wird ein Fehler, genauer gesagt eine Exception, ausgelöst.

Der Fehler ist vom Typ DateFormatException, das ist eine Subklasse der Exception-Klasse, die etwas später besprochen wird. Die Ausführung der Funktion wird nach dem Erzeugen der Exception abgebrochen.

Die Umwandlung der String-Variablen in Timestamps mit strtotime gibt -1 zurück, wenn das Format nicht als gültiges Datum erkannt wurde. In diesem Fall wird erneut eine DateFormatException erzeugt. Nur wenn zwei gültige Timestamps vorhanden sind, werden diese mit der IP-Adresse des Computers, von dem aus das Programm verwendet wird ($_SERVER["REMOTE_ADDR"]), einem neuen Objekt vom Typ Reserved übergeben. Diese Klasse überprüft im Konstruktor, ob es sich um eine gültige Zeitspanne handelt. Sollte die Zeitspanne nicht gültig sein, wird in der Reserved-Klasse eine Exception erzeugt, die an die aufrufende Funktion weitergeleitet wird und den Ablauf ebenso abbricht.

public function book($s, $e) {  if ($s == '' || $e == '')  throw new DateFormatException("Leeres Datum.");  $start = strtotime($s);  $end = strtotime($e);  $user = $_SERVER["REMOTE_ADDR"];  if ($start == -1 || $end == -1)  throw new DateFormatException("Zeitumwandlungsfehler");  $res = new Reserved($start, $end, $user);  if ($this->isBooked($start,$end))  throw new Exception("{$this->name} ist zu dieser Zeit".  "bereits belegt.");  $this->reservations[$start] = $res;  }  

Bevor das neue Reserved Objekt zu dem bestehenden Objekt hinzugefügt wird, muss natürlich noch überprüft werden, ob der Zeitraum bereits belegt ist. Dabei kommt die isBooked-Funktion zum Einsatz.

public function isBooked($start, $end=NULL) {  // wenn der Aufruf nur mit einer Zeitangebe erfolgt, wird  // die Endzeit auf eine Sekunde nach der Startzeit gesetzt  if ($end == NULL) {  $end = $start + 1;  }  if (count($this->reservations) > 0) {  foreach ($this->reservations as $r) {  if ($r->isBusy($start, $end)) {  return TRUE;  }  }  }  return FALSE; // keine gebuchten Ressourcen gefunden  }  

Diese Methode unterstützt das Überladen, d.h. in diesem Fall, dass sie mit einem oder zwei Parametern (einem Zeitpunkt oder einer Zeitspanne) aufgerufen werden kann. Die eigentliche Arbeit verrichtet eine Schleife über alle vorhandenen Reservierungen ($this->reservations). Für jedes Reservierungsobjekt wird geprüft, ob der Zeitraum bereits belegt ist ($r->isBusy). Sobald eine Überschneidung gefunden wird, gibt die Funktion TRUE zurück. Ist der Termin noch frei, retourniert die Funktion FALSE (die Reserved-Klasse wird gleich im Anschluss an diesen Abschnitt erklärt).

Neben einigen Hilfsfunktionen zur HTML-Ausgabe enthält die Resource-Klasse noch die Funktionen zum Speichern und zum Abrufen der Objekte. Wie bereits in der Einleitung erwähnt, werden die Objekte in der Session-Umgebung gespeichert. (Wenn Sie die Daten permanent speichern möchten, müssen Sie bei diesen Funktionen ansetzen. Anstatt in einer Session, könnten Sie die Daten auch in eine Datei oder eine Datenbank speichern. Der Dateizugriff ist meist problematisch, da Sie immer beachten müssen, dass es keine zeitgleichen Schreibzugriffe auf die Datei gibt. Datenbanken sind hier das Mittel der Wahl, da sie Ihnen diese Arbeit abnehmen.)

Die save-Funktion speichert das aktuelle Objekt ($this) in dem Session-Array $_SESSION['resource']. Um jedes Objekte später wiederzufinden, wird als Array-Index die ID des Objekts verwendet.

public function save() {  $_SESSION['resource'][$this->id] = $this;  return TRUE;  }  public static function clearAll() {  unset($_SESSION['resource']);  return TRUE;  }  public static function getAll() {  if (array_key_exists('resource', $_SESSION)) {  return $_SESSION['resource'];  } else  return FALSE;  }  // Session-Funktion: gibt genau ein Objekt zurück  public static function getById($id) {  if (array_key_exists($id, $_SESSION['resource'])) {  return $_SESSION['resource'][$id];  } else {  throw new Exception("Id not found");  }  }  

Die Funktionen clearAll, getAll und getById sind als statische Funktionen definiert. Das bedeutet, dass man keine Instanz der Klasse braucht, um sie aufzurufen, was bei diesen Funktionen sinnvoll ist. Die getById-Funktion überprüft, ob die gewünschte ID in der Session-Variable vorhanden ist. Sollte sie nicht vorhanden sein, wird eine Exception an die aufrufende Funktion weitergeleitet.

Die Klasse für die einzelnen Zeiteinträge: Reserved.php

Die Reserved-Klasse nimmt jeweils eine Startzeit, eine Endzeit und einen Benutzernamen im Konstruktor auf. Jedes Objekt repräsentiert einen gebuchten Eintrag.

class Reserved {  public $start;  public $end;  public $user;  // akzeptiert zwei Timestamps und einen String für den Benutzer  public function __construct($start, $end, $user) {  if ($start > $end) {  throw new Exception('Endzeit vor dem Beginn');  }  $this->start = $start;  $this->end = $end;  $this->user = $user;  if ($this->getDuration() > 3600*24*14) {  throw new Exception('Keine Buchungen über 14 Tage');  }  }  

Der Konstruktor überprüft zwei Dinge: Erstens, ob die Startzeit nach der Endzeit liegt (das ist in jedem Fall eine ungültige Zeitspanne), und zweitens, ob die Zeitspanne mehr als zwei Wochen lang ist (3600*24*14 Sekunden). Diese zweite Kontrolle soll verhindern, dass Ressourcen über einen zu langen Zeitraum reserviert werden können. In beiden Fällen wird eine Exception erzeugt und das Objekt wird nicht erstellt.

public function getDuration() {  return $this->end - $this->start;  }  // gibt TRUE zurück, wenn das Objekt zwischen $from und $to  // bereits gebucht ist  public function isBusy($from, $to) {  if (($this->start <= $from && $this->end >= $from) ||  ($this->start <= $to && $this->end >= $to) ||  ($from <= $this->start && $to >= $this->end)) {  return TRUE;  } else {  return FALSE;  }  }  

Die isBusy-Funktion wird ihrerseits mit zwei Timestamps aufgerufen und überprüft, ob die nachgefragten Zeiten mit der Zeit des Objekts kollidieren. Dazu wird überprüft, ob die Start- oder die Endzeit in den objekteigenen Zeitraum fallen. Die dritte Zeile in der if-Abfrage kontrolliert schließlich noch, ob der gefragte Zeitraum vor der Startzeit des Objekts beginnt und erst danach zu Ende ist.

Die Subklassen

Alle Subklassen von Resource müssen die showCreate-Methode überschreiben. In dieser Methode werden die Funktionalitäten einer Ressource eingegeben. In der Beamer-Klasse wird zum Beispiel die Auflösung des Geräts gespeichert und ob ein TV-Eingang vorhanden ist. Die Funktionen showTextFeature und showBoolFeature sind in der Basisklasse Resource implementiert und geben hauptsächlich HTML-Formatierungen aus. Der Konstruktor übergibt die ID an die Basisklasse, damit diese auch dort zur Verfügung steht.

class Beamer extends Resource {  public $resolution = '';  public $tv_in = true;  public function __construct($id) {  parent::__construct($id);  }  public function showCreate() {  echo "<table border='1'>\n";  $this->showTextFeature('Name', 'name');  $this->showTextFeature('Auflösung', 'resolution');  $this->showBoolFeature('TV-Verbindung', 'tv_in');  echo "</table>\n";  echo "<input type='hidden' name='object' ".  "value='Beamer'>\n";  }  }  

Die Klassen für Digitalkameras und Flipcharts funktionieren im Wesentlichen genauso wie die hier abgedruckte Beamer-Klasse. Einen Unterschied gibt es bei den Klassen zur Verwaltung von Räumen, also LectureRoom und MeetingRoom. Sie haben eine weitere gemeinsame Eigenschaft, nämlich die Anzahl von Sitzplätzen. Das Interface Room stellt sicher, dass alle Raum-Subklassen eine Sitzplatzfunktion bereitstellen. Alle Klassen, die das Room-Interface verwenden, müssen die Funktion getSeats überschreiben. Für die LectureRoom-Klasse sieht das zum Beispiel so aus:

class LectureRoom extends Resource implements Room {  public $microphone = FALSE;  public $seats = 0;  ...  public function getSeats() {  return $this->seats;  }  

Fehlerbehandlung: DateFormatException

Wenn ein Fehler bei der Zeiteingabe auftritt, wird eine Exception vom Typ DateFormatException erzeugt. Diese von der System-Klasse Exception abgeleitete Klasse formatiert die Fehlermeldung mit einer Stylesheet-Anweisung und kann außerdem den Link zur Dokumentation des Datumsformats ausgeben.

class DateFormatException extends Exception {  public function __construct($msg) {  parent::__construct($msg);  }  // zeigt den Link zur Datumsformat-Dokumentation  public function showHelp() {  echo "<p class='hint'><a ".  "href='http://www.gnu.org/software/tar/manual/".  "html_chapter/tar_7.html'>Zur Liste</a>  der ". "unterstützten Datumsformate.</p>\n";  }  public function __toString() {  return "<p class='error'>".$this->getMessage().  " (".__CLASS__.").</p>\n";  }  

Durch die __toString-Funktion wird es möglich, die formatierte Fehlermeldung einfach mit echo $e auszugeben.

Die Verwendung der Klassen

Der Ablauf der Anwendung wird von drei Scripts gesteuert: index.php, book.php und add.php, wobei add.php nur für das Hinzufügen neuer Ressourcen gebraucht wird. Alle Scripts inkludieren in der ersten Zeile die Hilfsdatei inc/main.php. Außer den Funktionen zum Erstellen eines HTML-Kopfteils (html_start) und dem Ende der HTML-Datei (html_end) wird hier die Session gestartet. Die PHP-5-Funktion __autoload stellt schließlich sicher, dass alle angeforderten Klassen mit require_-once geladen werden. Wenn Sie eine neue Klasse zum System hinzufügen und sie entsprechend benennen (Klassenname in Kleinbuchstaben), wird sie automatisch von __autoload gefunden.

<?php // Beispieldatei: booking/inc/main.php  session_start();  function __autoload($class_name) {  require_once(strtolower(dirname(__FILE__)."/$class_name.php"));  }  

Da die Dateien aller Klassen in Kleinbuchstaben gespeichert sind, wird bei dem require_once-Aufruf die Zeichenkette mit strtolower umgewandelt. Durch dir-name(__FILE__) wird sichergestellt, dass der korrekte Pfad zu den Klassen vorangestellt wird. Info Die strtolower-Funktion arbeitet abhängig von den verwendeten Locale-Einstellungen. Wenn Sie Umlaute in Ihren Dateinamen verwenden (generell eine schlechte Idee) und Locale nicht entsprechend eingestellt ist, wandelt strtolower die Umlaute nicht um.

Neue Ressourcen hinzufügen: add.php

Die Datei add.php arbeitet in drei Schritten: Schritt eins ist die Ausgabe einer Liste von verfügbaren Typen, also aller Subklassen von Resource. Nachdem ein Typ ausgewählt wurde, wird im zweiten Schritt das Formular zur Eingabe der Daten für ein neues Objekt angezeigt (die showCreate-Funktion der jeweiligen Klasse). Der dritte Schritt ist das Erzeugen des neuen Objekts.

<select name="resource">  <?  // Schritt 1: Anzeigen der verfügbaren Klassen  foreach (Resource::$valid as $k=>$v) {  echo "<option value='$k'>$v</option>\n";  }  ?>  

Als value der Auswahlliste resource wird im ersten Formular der Name der Klasse übergeben (das ist der Schlüssel aus dem $valid-Array in der Resource-Klasse, z.B. LectureRoom). Im zweiten Schritt wird aus diesem Klassennamen ein neues Objekt ($r) erzeugt. Durch diesen kleinen Trick kann ein Dummy-Objekt mit der ID 0 für den showCreate-Aufruf verwendet werden.

// Schritt 2: Eingabeformular für die neue Klasse  if ($send != '') { // überprüft, von welchem Formular die  // Anfrage kommt  $r = new $res(0);  html_start("Add a new resource");  echo "<h2>Hinzufügen einer Ressource: $res</h2>\n";  printf("<form method='GET' action='%s'>\n",$_SERVER["PHP_SELF"]);  $r->showCreate();  echo "<input type='submit' name='add' value='Hinzufügen'>\n";  html_end();  exit();  }  

Nach dem neuerlichen Abschicken der Seite wird das echte Objekt ($o) erzeugt. Als ID bekommt das Objekt den aktuellen UNIX-Timestamp zugewiesen (Sie können also in einer Sekunde nicht zwei Objekte erzeugen). Alle vom Formular übergebenen Werte ($_GET) werden in dem neuen Objekt als Member-Variablen gespeichert ($o->$k = $v).

// Schritt 3: neues Objekt erzeugen  if ($add != '') {  $o = new $object(time());  foreach($_GET as $k=>$v) {  $o->$k = $v;  }  

Ist der Name der neuen Ressource nicht eingegeben worden ($o->name == ''), so wird die ID-Nummer als Name verwendet. Da bei dem Speicher-Aufruf ($o->save) eine Exception ausgelöst werden kann, ist diese Anweisung in einem try-catch-Block geklammert. Im catch-Abschnitt wird bei Bedarf die entsprechende Fehlermeldung ausgegeben.

try {  if ($o->name == '')  $o->name = $o->id;  $o->save();  html_start("Neue Ressource hinzugefügt");  echo "<h2>Neue Ressource hinzugefügt: ID: ".  "{$o->id}</h2>\n";  echo "<a href='book.php?id={$o->id}'>Termine ".  "reservieren</a>.\n";  html_end();  exit();  } catch (Exception $e) {  html_start("Fehler beim Hinzufügen");  echo "<h2>Es ist ein Fehler aufgetreten: ".  $e->getMessage()."</h2>\n";  }  }  

Nach erfolgreicher Erstellung des neuen Objekts wird gleich der Link zum Buchen einer Zeiteinheit für diese Ressource angezeigt.

Die Startseite: index.php

index.php ist die Startseite des Beispiels (siehe Abbildung 3).

<?php // Beispieldatei: booking/index.php  require_once(dirname(__FILE__)."/inc/main.php");  $elements = Resource::getAll();  if ($elements == FALSE) {  header("Location: add.php");  exit();  }  html_start("Reservierungssystem");  

Wenn der Funktionsaufruf Resource::getAll() FALSE zurückgibt, sind keine Ressourcen vorhanden und man wird via header-Funktion auf die Eingabeseite add.php umgeleitet. Andernfalls wird in einer foreach-Schleife die Liste der verfügbaren Ressourcen angezeigt (vergleiche Abbildung 3).

<?php  foreach($elements as $r) {  echo "<option value='{$r->id}'>{$r->name}";  if ($r instanceof Room) {  printf(" (%s Plätze)", $r->getSeats());  }  echo "</option>\n";  }  ?>  

Interessant in dieser Schleife ist der instanceof-Operator. Implementiert eine Klasse das Room-Interface, ist sie automatisch eine Instanz von Room. Dementsprechend wird für alle Räume angezeigt, wie viele Sitzplätze vorhanden sind.

Ressourcen reservieren: book.php

Der für den Anwender interessanteste Teil ist das Reservieren einer Ressource. Das book.php-Script muss mit einer ID aufgerufen werden ($id), andernfalls wird man gleich auf die Startseite weitergeleitet.

if ($id == '' || $id == FALSE) {  header("Location: index.php");  }  try {  $r = Resource::getById($id);  } catch (Exception $e) {  html_start();  echo "<p class='error'>Die gewünschte Ressource konnte ".  "nicht gefunden werden\n";  html_end();  exit();  }  

Das gewünschte Objekt wird von der statischen Methode getById aus der Resource-Klasse geholt. Ist die ID nicht vorhanden, wird eine Exception erzeugt, auf die mit der entsprechenden Fehlermeldung reagiert wird. Anschließend wird eine Hilfsfunktion aus der Resource-Klasse aufgerufen, die die beiden Texteingabefelder für den Start- und den Endzeitpunkt anzeigt ($r->showAddReservation). Wurde das Formular zur Buchung abgeschickt ($reserve hat einen Wert), wird versucht, die gewünschte Zeitspanne für die Ressource zu buchen. Andernfalls wird der ganze if-Block übersprungen und die Funktion $r->showBooked gibt eine List der vorhandenen Reservierungen aus.

$r->showAddReservation();  if ($reserve != '') {  $from = array_item($_GET, 'from');  $to = array_item($_GET, 'to');  try {  $r->book($from, $to);  $r->save();  } catch (DateFormatException $e) {  echo $e;  $e->showHelp();  } catch (Exception $e) {  printf("<p class='error'>%s</p>\n", $e->getMessage());  }  }  $r->showBooked();  

Die eigentliche Buchung führt die Funktion $r->book aus der Resource-Klasse durch. Diese kann, wie oben erwähnt, unterschiedliche Exceptions erzeugen, je nachdem, was schief gegangen ist (ein Datumsproblem oder die Ressource ist bereits belegt). In book.php werden die Fehler für Datumskonvertierungen (DateFormatException) gesondert behandelt, denn bei ihnen wird zusätzlich der Link zu den korrekten Datumsformaten angezeigt ($e->showHelp). Alle anderen Exceptions werden in der zweiten Schleife abgefangen.

Zwei weitere kleine PHP-Scripts sind noch vorhanden: del.php und reset.php. Das reset-Script ist sehr einfach: Es ruft die statische clearAll-Funktion der Resource-Klasse auf und leitet den Anwender auf die Startseite zurück. del.php löscht einen Eintrag aus der Liste der gebuchten Zeiten. Auch dazu wird die entsprechende Funktion in der Resource-Klasse verwendet (deleteReservation). Info Das Beispiel war wirklich nur dazu gedacht, Ihnen einige Grundzüge von OOP näherzubringen. In dieser Form bietet es keine praktischen Einsatzmöglichkeiten. Daher wurde auch darauf verzichtet, die Eingaben streng zu prüfen, um etwaige Angreifer abzublocken. Der Umgang mit Textfeldern und GET-Variablen in einem öffentlichen Webformular ist sehr heikel und sollte gut abgesichert werden.

Ein wesentliches Anliegen einer Website ist die Möglichkeit, auf Daten und Informationen zugreifen und sie dauerhaft sichern zu können. Dabei kann es sich um Nutzerbewegungen handeln, die in Dateien protokolliert werden sollen oder um Daten, die in ein Formular eingegeben wurden.

Prinzipiell können Sie für diesen Zweck zwei Methoden einsetzen: Sie sammeln die Daten in Textdateien oder Sie verwenden Datenbanken. Der Vorteil bei der Verwendung von Textdateien liegt unmittelbar auf der Hand: Sie brauchen keine Kenntnisse über Datenbanken, ein Thema, das bekanntlich nicht ganz einfach ist oder zumindest den Ruf hat, nicht ganz einfach zu sein! Bevor wir also daran gehen, beispielhaft auch mit einer Datenbank zu arbeiten, wollen wir in diesem Kapitel klären, wie Sie mit PHP eine Textdatei anlegen, Daten in die Datei schreiben und diese Daten auslesen.

Obwohl Datenbanken zweifellos leistungsstärker als Textdateien sind, werden Sie sehen,

dass sich auch mit Dateien eine Menge machen lässt. Elementar sind zunächst die zwei Prozesse: in eine Datei zu schreiben und Daten auszulesen. Wir wollen diese Prozesse nicht nur abstrakt erläutern, sondern nach der Erklärung einiger Grundlagen an einem Beispiel durchspielen. Dazu wird eine Seite kreiert, die ein Gästebuch anbietet, dem User also die Möglichkeit gibt, einen Kommentar einzugeben. Diese Informationen werden dann zum einen in einer TXT-Datei gesammelt und sind zum anderen für andere Besucher einsehbar.

Daten in Textdateien sichern

Die Aktion, Daten in eine Datei zu schreiben, besteht aus drei Schritten:

PHP bietet Funktionen, die diese Aufgabe leisten. Sie sind relativ einfach nachzuvollziehen.

Zum Anlegen einer Datei benutzen Sie die Funktion fopen().

Die Klammer nimmt als Argumente den Namen der Datei bzw. den Dateipfad auf und – und dies ist sehr wichtig – den Modus. Mit dem Modus bestimmen Sie, wie die Datei geöffnet werden soll und an welcher Stelle in der Datei der Dateizeiger (quasi der Cursor) steht, oder – anders ausgedrückt – wo der Startpunkt für das Lesen bzw. Schreiben in der Datei ist. Dabei können Sie zwischen diversen Varianten wählen. Die folgende Tabelle gibt einen kurzen Überblick über die verschiedenen Möglichkeiten:

Modus Bedeutung
r erlaubt das Auslesen einer Datei
w schreibt in eine Datei und legt sie an, sofern sie noch nicht existiert
a hängt neue Daten an das Ende einer Datei und legt eine Datei an, sofern sie nicht existiert
r+ schreibt Daten in eine Datei und liest die Daten aus
w+ schreibt Daten in eine Datei und liest die Daten aus, kreiert eine Datei, wenn sie nicht existiert, aber wirft vor dem Schreiben Daten raus, sofern die Datei existiert. Daten werden also überschrieben.
a+ schreibt Daten in eine Datei und liest die Daten aus, kreiert eine Datei, wenn sie nicht existiert. Neue Daten werden an das Ende der Datei gehängt.

Der Modus, der eigentlich alles zulässt, ist a+. Bei r oder r+ muss die Datei, in die geschrieben oder aus der gelesen werden soll, bereits existieren. Mit allen anderen Modi wird sie gegebenenfalls neu angelegt. Vorsicht ist geboten bei w+: der Inhalt der Datei wird ohne Nachfrage überschrieben. Info

int fopen(string dateiname, string modus [, int use_include_path])  

Diese Funktion öffnet eine Datei oder URL. Sofern sie noch nicht existiert, wird die Datei bei einigen Modi auch erzeugt. Der Modus gibt an, auf welche Weise die Datei geöffnet wird.

Wenn Sie mit fopen() eine Datei anlegen, erzeugen Sie einen sogenannten Dateihandle. Dieser Handle wird von PHP als Verweis auf die Textdatei verwendet. Für alle weiteren Operationen arbeiten Sie dann mit diesem Handle. Dies ist wesentlich praktikabler als immer wieder den Pfad einer Datei angeben zu müssen. Die Funktion, mit der Sie Daten an die Position des Dateizeigers schreiben, lautet: fputs().

Die Zeilen zum Öffnen (bzw. Anlegen) einer Datei und zum Schreiben von Daten in diese Datei sehen dann generell so aus (statt Modus eine der Abkürzungen aus der obigen Tabelle, also beispielsweise a):

$handle=fopen(Dateiname.txt, Modus);  fputs($handle, die zu schreibenden Daten);  fclose($handle);  

Info

int fputs(int filehandler, string daten [, int length])  

Mit dieser Funktion werden Daten (daten) an die aktuelle Position des Dateizeigers in eine Datei geschrieben. Gibt es keine Angabe zur Länge (length) des Strings, schreibt PHP den kompletten String in die Datei.

Diesen Prozess wollen wir anhand der Aufgabe, ein Gästebuch zu erstellen, im nächsten Abschnitt konkretisieren. Die Einträge der Gäste sollen zunächst in eine separate Textdatei geschrieben werden. Danach sorgen Sie durch die Erweiterung des Scripts dafür, dass die Besucher der Seite die Einträge des Gästebuchs einsehen können.

Vorüberlegungen zum Script

Wir wollen das Script zunächst soweit entwickeln, dass die Textdatei erzeugt wird, die die aus dem Formular übergebenen Daten sichert.

Im nächsten Schritt, d.h. im erweiterten Script, werden wir dann zu der Realisierung der Aufgabe kommen, die Daten auszulesen, sodass sie für den User einsehbar sind.

Für die erste Aufgabe brauchen Sie die oben vorgestellten Funktionen und eine Reihe weiterer Funktionen,

die vor allem dazu dienen, die Einträge, die übernommen werden, zu bearbeiten, sodass die Textdatei keine störenden Elemente enthalten wird.

Zeichen und/oder Text ersetzen

Kommen wir zunächst zu der Funktion str_replace().

Hiermit lassen Sie einen Text bzw. eine Zeichenkette nach dem Vorkommen einer anderen Zeichenkette durchsuchen und durch einen anderen String ersetzen. Als Argumente werden in die Klammer gesetzt:

str_replace('zu ersetzende Zeichenkette', 'Ersatzzeichenkette', 'zu durchsuchende Zeichenkette');  

Diese Aktion klingt sicherlich irritierend, aber der Sinn der Sache wird verständlich werden, wenn wir das Script erläutern. In unserem Beispiel hängt der Einsatz dieser Funktion damit zusammen, dass bestimmte Zeichen nicht in den Daten, die durch das Formular übergeben werden, enthalten sein dürfen und sie deswegen ersetzt werden, sofern sie vorkommen. Wir können an dieser Stelle auch verallgemeinert festhalten, dass insbesondere bei Text, der aus irgendeiner Quelle übernommen wird, auf störende (Sonder)zeichen geachtet und der Text in der Regel manipuliert werden muss. Info

string str_replace(string zeichen, string ersatzzeichenkette, string inhalt)  

Die Funktion ersetzt alle Vorkommen eines Strings durch einen anderen (von zeichen durch ersatzzeichenkette in inhalt)

Suchen und Ersetzen

Eine Spielart der Funktion replace() ist übrigens ereg_replace(). Damit wird nach dem zu ersetzenden Text/Zeichen gesucht und dann durch den/das gewünschte ersetzt. Ein kleines Beispiel, in dem ein Text und ein Zeichen ersetzt werden. Es gibt beispielsweise die folgenden Textzeilen

In PHP muss man darauf achten, wie texte übernommen werden.

In PHP sieht dieser Text so aus:

In PHP muss man darauf achten, \n wie texte übernommen werden.  

Wir hätten nun gern zwei Dinge verbessert. Zum einen sollen die PHP-Zeichen für Zeilenumbrüche ersetzt werden durch „richtige“ Zeilenumbrüche, die in HTML zu sehen sind, sodass die Textformatierung erhalten bleibt, und zum anderen soll aus texte Texte werden.

Wir speichern den obigen Text in einer Variablen

$testtext = In PHP muss man darauf achten,\n wie texte übernommen werden.  

und setzen dann zunächst die Funktion ereg_replace() ein:

$testtext=ereg_replace(texte, Texte,$testtext);  echo $testtext;  

Schauen Sie sich die Datei in Ihrem Browser an:

Durch das ereg_replace wird texte durch Texte ersetzt. Im nächsten Schritt kümmern Sie sich um den Austausch der Zeilenumbruchzeichen. Dazu setzen Sie die Funktion nl2br() ein, und schreiben

$testtext=nl2br($testtext);  echo $testtext;  

Info string ereg_replace(string suchennach, string ersatz, string inhalt)

Die Funktion sucht in einer Zeichenkette (inhalt) nach einer anderen Zeichenkette (suchennach) und ersetzt diese (ersatz).

Betrachten Sie die Datei erneut im Browser. Der Text müsste nun das gewünschte Aussehen haben. Das Bild 1 zeigt das kleine Script und die Ausgabe im Browser.

Info string nl2br (string string)

Die Funktion wandelt aus einem String sämtliche Zeilenumbrüche in die HTML-Entsprechung (<br>) um. Der Befehl funktioniert bei Installation von PHP auf Windows-Rechnern nicht immer einwandfrei!

Leerstellen und HTML-Zeichen entfernen

Außerdem kommt in dem Script für das Gästebuch wieder die Funktion trim() zum Einsatz, die wir auch schon im Zusammenhang mit dem Kontaktformular verwendet haben. Sie sorgt dafür, dass überflüssige Leerzeichen aus einer Zeichenkette entfernt werden. Des Weiteren werden wir die Funktion strip_tags() verwenden. Damit lassen sich HTML-Tags und PHP-Zeichen aus Zeichenketten entfernen.

Für die Darstellung des Formulars benutzen Sie die üblichen HTML-Tags.

Da die Besucher der Website in der Lage sein sollen, einen Kommentar einzutippen (und nicht nur einen Namen oder Ähnliches), brauchen Sie in dem Fall auch den Typ Textarea für mehrzeilige Eingabefelder. Die Seite könnte – schlicht und ohne weiteres Layout – aussehen wie das Bild 2.

Sie können das Script mit dem PHP-Teil beginnen und dann das HTML-Formular anlegen. Zunächst setzen Sie die eben kurz erläuterten Funktionen zur Manipulation des in die Felder eingegebenen Textes ein, dann bauen Sie die Fehlermeldungen zusammen für den Fall, dass das Formular nicht korrekt abgeschickt wird, danach folgen die Befehle für das Anlegen der Datei und das Schreiben der Daten in diese Datei. Als Letztes schreiben Sie den HTML-Teil für das Formular.

Das Script für das Gästebuch und die Textdatei

Der Code zeigt das Script für das Gästebuch. Es enthält Fehlermeldungen, Befehle zum Bearbeiten der Texteingaben und für das Anlegen der Textdatei sowie das Schreiben der Daten in die Datei und die HTML-Elemente für das Formular. Gestalterische Elemente beim Formular haben wir wieder weitgehend ausgespart, da wir uns auf die PHP-Codes konzentrieren.

<?php  if($sent==1)  {  $t1=chr(10);  $t2=chr(13);  $name=str_replace('~','',$name);  $betreff=str_replace('~','',$betreff);  $message=str_replace('~','',$message);  $name=trim($name);  $betreff=trim($betreff);  $message=trim($message);  $name=strip_tags($name);  $betreff=strip_tags($betreff);  $message=strip_tags($message);  If(!$name){$fehler="Bitte geben Sie einen Namen ein  <br>";}  If(!$betreff){$fehler=$fehler."Bitte geben Sie den Betreff an  <br>";}  If(!$message){$fehler=$fehler."Bitte geben Sie eine Nachricht ein  <br>";}  if($fehler){$fehler="<font color=red>  <h4>".$fehler."</h4></font>";}  }  if($name AND $betreff AND $message)  //Formular wurde ausgefüllt  {  $message=str_replace($t1,'<br>',$message);  $message=str_replace($t2,'<br>',$message);  IF(file_exists('gast.txt')){$ausgabe="\n";}  $comment=fopen('gast.txt','a');  $ausgabe=$ausgabe.$name."~".$betreff."~".$message;  fputs($comment,$ausgabe);  fclose($comment);  $name="";  $betreff="";  $message="";  }  ?>  <html>  <head>  <title>Gästebuch</title>  </head>  <body>  <h3>Unser Gästebuch</h3>  <?php echo $fehler; ?>  <form action='<?php echo $PHP_SELF; ?>' method='post'>  <input type='hidden' name='sent' value=1>  <p>Ihr Name:  <br>  <input type='text' name='name' size='30' value='<?php echo $name;  ?>'>  <br>  Betreff:<br>  <input type='text' name='betreff' size='30' value='<?php echo $betreff;  ?>'>  <br>  Ihre Nachricht:  <br>  <textarea name='message' rows='10' cols='30' wrap=virtual>  <?php echo $message; ?>  </textarea>  <br>  <input type=submit value=abschicken>  </form>  </body>  </html>  

Erläuterung des Scripts

Wie auch schon bei bisherigen Scripts setzen Sie mit

if($sent==1)  {  

am Anfang des Scripts eine if-Bedingung ein, die prüft, ob das Formular abgeschickt wurde. Die Anweisungen in dem Block werden nur ausgeführt, wenn die Prüfung true ergibt.

Zum Verständnis der Definition der nächsten beiden Zeilen, in denen die Funktion chr() verwendet wird, müssen Sie sich klar machen, wie der Text in den Textbereich (<textarea>) des Formulars eingegeben wird. Hier erfolgen Zeilenumbrüche, die in der Darstellung des Textes in der Textdatei nicht enthalten sein dürfen, da dadurch die Datensätze verfälscht werden würden, denn mit einem Absatz beginnt ja eine neue Zeile mit dem nächsten Datensatz.

Diesem Problem wollen wir mit der Funktion chr() zu Leibe rücken, mit der man sich das ASCII-Zeichen (8-Bit-Zeichensatz, bei dem jeder Zahl von 0-255 ein Zeichen zugewiesen ist) zu einer angegebenen Nummer ausgeben lassen kann. So gibt chr(10) das Zeichen für den ASCII-Wert 10 aus und der ASCII-Wert 10 (bzw. 13) ist der Zeilenumbruch. Diese Werte werden zunächst in den beiden Variablen $t1 und $t2 gespeichert:

$t1=chr(10);  $t2=chr(13);  

Info string chr(int ascii)

Die Funktion gibt das ASCII-Zeichen zu einer angegebenen Nummer aus.

Weiter unten im Script werden wir die Werte dieser Variablen ersetzen lassen, um damit die unerwünschten Zeilenumbrüche loszuwerden. Info Seit kurzer Zeit klimpert der Euro in den Kassen. Aus aktuellem Anlass deswegen der folgende Tipp: Schreiben Sie doch einfach einmal echo chr(128); in ein Script. Was zeigt der Browser? Voilà, das Eurozeichen!

Als Nächstes verwenden wir die Funktion str_replace, und zwar dafür, eventuelle Tilden (~) aus dem eingegebenen Text zu entfernen. Warum wir dies machen, erklärt sich im Prinzip erst weiter unten im Script. Dort sehen Sie, dass wir die Tilde als Trennzeichen beim Schreiben der Daten in die Textdatei benutzen. Mit anderen Worten: zwischen Name, Betreff und Message steht jeweils eine Tilde. Daher darf dieses Zeichen nicht in eingegebenen Formulardaten vorkommen und wir lassen es ersetzen, falls es doch vorkommt. Es wird durch „Nichts“ ersetzt, d. h. durch einen Leerstring. Diese Funktion müssen wir auf alle drei Texteingaben anwenden. Die Klammer enthält als Argumente das zu ersetzende Zeichen, den Ersatzstring und den zu durchsuchenden String. Dass die Tilde durch „Nichts“ ersetzt wird, erreichen wir mit zwei direkt aufeinanderfolgenden Anführungszeichen:

$name=str_replace('~','',$name);  $betreff=str_replace('~','',$betreff);  $message=str_replace('~','',$message);  

Danach werden mit der Funktion trim() eventuelle Leerzeichen aus den Texteingaben entfernt und zwar am Anfang und am Ende. Die Variablen $name, $betreff und $message erhalten also neue Werte.

$name=trim($name);  $betreff=trim($betreff);  $message=trim($message);  

Zu guter Letzt entfernen Sie eventuelle HTML-tags aus den Eingaben. Dies empfiehlt sich, da Sie so verhindern, dass ein User JavaScripte zum Umleiten der Seite eingibt.

$name=strip_tags($name);  $betreff=strip_tags($betreff);  $message=strip_tags($message);  

Info string strip_tags(string zeichenkette [,string erlaubte_tags])

Die Funktion entfernt aus zeichenkette alle HTML– und PHP-Tags, außer die in erlaubte_tags aufgelisteten.

Die Fehlermeldung

Damit auf alle denkbaren Fälle fehlender Eingaben reagiert wird, aber dennoch nicht zu viele if-Anweisungen geschrieben werden müssen, haben wir versucht, die Fehlermeldung durch Erweiterung der Variablen möglichst kompakt zusammenzubauen.

If(!$name){$fehler="Bitte geben Sie einen Namen ein <br>";}  If(!$betreff){$fehler=$fehler."Bitte geben Sie den Betreff an <br>";}  If(!$message){$fehler=$fehler."Bitte geben Sie eine Nachricht ein<br>";}  If($fehler){$fehler="<font color=red><h4>".$fehler."</h4></font>";}  

Zunächst benutzen Sie in der if-Bedingung wieder das Ausrufezeichen, um die Eingabe zu negieren, sodass als Prüfergebnis der Bedingung true ausgegeben und die Anweisung ausgeführt wird, wenn keine Eingabe in dem Feld vorgenommen wurde. Im ersten Anweisungs-Block wird der auszugebende Text in der Variablen $fehler gespeichert. In der nächsten if-Anweisung weisen Sie mit $fehler=$fehler. den Wert der Ursprungsvariablen erneut zu und erweitern diesen Wert durch das Setzen des Punktes mit dem neuen Wert. Ergibt die Prüfung der ersten und zweiten Bedingung true, werden also die beiden Meldungen als Wert in der Variablen $fehler gespeichert. Analog geben Sie dann die if-Anweisung für die letzte Fehlermeldung ein, Sie erweitern also erneut.

Zu guter Letzt kümmern Sie sich um die Formatierung der Fehlermeldung.

In der Anweisung schreiben Sie, dass der in der Variablen enthaltene Wert die Farbe rot annimmt und als Überschrift der Ebene 4 formatiert wird.

Danach folgt die geschweifte Klammer, die die erste if-Anweisung (die Prüfung, ob das Formular abgeschickt wurde) schließt.

Dann verzweigt das Script in die nächste if-Bedingung, die prüft, ob alle Felder ausgefüllt wurden. Dazu benutzen Sie AND-Verknüpfungen:

if($name AND $betreff AND $message)  {  

Wird als Prüfergebnis true ausgegeben, sollen als Erstes die Zeilenumbrüche aus der Eingabe im Textbereich entfernt und ersetzt werden. Wir sprachen das Problem bereits weiter oben an: Der Browser schickt die in der <textarea> befindlichen Zeilenumbrüche mit. Da die Texte in einer Textdatei gespeichert werden sollen, und zwar jeder Datensatz in einer Zeile, dürfen keine Zeilenumbrüche (außer den bewussten am Ende eines Datensatzes) in die Textdatei geschrieben werden, da der Datensatz sonst nicht komplett mit einer Zeile ausgelesen werden kann. In der Variablen $t1 bzw. $t2 sind die Zeichen für den Zeilenumbruch gespeichert (weiter oben zugewiesen über die Funktion chr(10) bzw. chr(13)). Nun ersetzen wir die Zeilenumbrüche durch <br>, (dieses Zeichen stört nicht, sondern sorgt nur dafür, dass die Zeile im Browser umgebrochen wird), indem wir die Variable $message mit dem neuen Wert überschreiben. Die beiden Zeilen für die beschriebene Aufgabe lauten:

$message=str_replace($t1,'<br>',$message);  $message=str_replace($t2,'<br>',$message);  

Erzeugen der Textdatei

In den nächsten fünf Zeilen geht es nun um die Textdatei zur Sicherung der Formulardaten. Hier müssen wir etwas ausholen, denn trotz der Erklärung weiter oben ist manches vermutlich nicht unmittelbar einleuchtend. Verständlich dürfte die zweite Zeile sein:

$comment=fopen('gast.txt','a');  

Hier wird mit fopen eine Datei erzeugt, diese Datei erhält den Namen gast.txt und als Modus wurde a festlegt, d.h. es wird eine Datei kreiert, sofern sie noch nicht existiert und die Daten werden an das Ende angehängt. Dies wird in dem Dateihandle ($comment) gespeichert. Die Zeile darunter definiert eine Variable für die Daten, die in die Datei geschrieben werden sollen. Wir möchten aus dem Formular den Nachnamen, den Betreff und die Nachricht übergeben lassen (die Variablen korrespondieren mit den Namen, die im HTML-Teil in den Formularfeldern vergeben wurden bzw. noch vergeben werden):

$ausgabe=$ausgabe.$lastname."~".$betreff."~".$message;  

Nun sind noch die verwendeten Tilden erklärungsbedürftig. Um sie zu verstehen stellen Sie sich am besten vor, in welcher Form die Daten in die Datei geschrieben werden sollen. Sicherlich nicht ohne Punkt und Komma, sondern jeweils mit einem Trennzeichen zwischen den einzelnen Elementen. Sie brauchen also ein Trennzeichen. Da fast alle Zeichen in den Texteingaben der User vorkommen können und für den Text wichtig sind, muss man ein Zeichen finden, das sozusagen noch „frei“ ist. Deswegen haben wir hier die Tilde – als String in Anführungszeichen – eingesetzt. Sie trennt die Elemente. Danach folgt die Funktion fputs. Die Klammer nimmt den Dateihandle auf und die Variable, deren Wert die Daten bilden, die in die Datei geschrieben werden sollen:

fputs($comment,$ausgabe);  

Zu guter Letzt wird der Prozess mit fclose geschlossen.

Nun bleibt noch die erste Zeile dieses Blocks, also die if-Bedingung über fopen zu erklären.

IF(file_exists('gast.txt')){$ausgabe="\n";}  

Stellen Sie sich dazu die Textdatei vor. Wenn der erste Datensatz in eine Zeile geschrieben ist, soll der nächste Datensatz in einer neuen Zeile stehen. Deshalb braucht man am Anfang ab dem zweiten Datensatz ein \n für einen Zeilenvorschub. Dadurch wird dieser Datensatz in die nächste Zeile geschrieben.

Wir stoßen hier in philosophische Gefilde, denn Sie müssen bedenken: der erste Datensatz erzeugt automatisch die Datei, folglich gibt es bereits einen Datensatz, wenn es die Datei gibt. Daher die if-Bedingung mit der Funktion file_exists(): Wenn es die Datei bereits gibt (die Prüfung TRUE ergibt), soll vor dem Datensatz ein \n gesetzt werden. Ansonsten (also der allererste Datensatz) wird \n nicht gebraucht.

Der nächste Dreizeiler ist einfach aber wirkungsvoll:

$name="";  $betreff="";  $message="";  

Sie löschen mit der Zuweisung von „Leerstrings“ die in den Feldern eingegebenen Werte (sie sind bereits in der Textdatei gespeichert), damit das Formular wieder leer angezeigt wird.

Jetzt schließen Sie den Block der if-Bedingung für das ausgefüllte Formular und zu guter Letzt setzen Sie das schließenden PHP-Zeichen.

Das Formular

Dann beginnt der HTML-Teil mit dem üblichen Header. Bevor Sie den <form>-Tag für das Formular öffnen, sorgen Sie dafür, dass eine Meldung erscheint, sofern eines oder mehrere der Felder nicht ausgefüllt wurden. Dies ist ein einfacher Einzeiler, da die entsprechende Werte in der Variablen $fehler gespeichert wurden:

<?php echo $fehler; ?>  

Mit dem Attribut action verweisen Sie entweder auf die aktuelle Datei oder Sie verwenden die PHP-Variable $PHP_SELF. Denken Sie aber daran, für die Variable PHP zu öffnen und zu schließen. Mit den Formularelementen erzeugen Sie die üblichen Textfelder und einen mehrzeiligen Textbereich. Dies ist die Zeile:

<textarea name="message" rows='10' cols='30'></textarea>  

Den Formularelementen weisen Sie am besten wieder Werte zu, nämlich die Ausgabe der Variablen, in der die Namen gespeichert sind. Dies bewirkt dann, dass die Eingaben in den Feldern stehen bleiben, wenn ein User das Formular unvollständig abgeschickt hat.

value='<?php echo $name; ?>  

Info Haben Sie sich beim Surfen schon häufig darüber geärgert, dass Sie bei Formulareingaben immer wieder von vorn anfangen müssen, sofern Sie das Formular mit einem Fehler abgeschickt haben? Genau diese Ärgernis umgehen Sie mit der Ausgabe der Werte der Variablen in den Value-Attributen. Wenn Sie diesen Schritt beherzigen, machen Sie es auch als Einsteiger besser als viele alte Hasen unter den Programmierern!

Nach dem schließenden </form>-Tag schließen Sie den <body> und HTML.

Speichern Sie das Dokument als PHP-Datei und rufen Sie es im Browser auf. Geben Sie ein paar Daten ein, und schicken Sie das Formular ab. Da wir keine Programmzeilen in das Script eingebaut haben, die die Inhalte der Felder im Browser ausgeben, erhalten Sie nach dem Abschicken des vollständig ausgefüllten Formulars einfach wieder ein Leerformular.

Die Textdatei überprüfen

Testen Sie nun, ob es geklappt hat, dass Daten in eine Datei geschrieben werden. Schicken Sie das ausgefüllte Formular also etliche Male hintereinander ab, damit Sie ein paar Daten generieren. Öffnen Sie dann den Explorer mit dem Ordner, in dem auch das Script gespeichert wurde und rufen Sie die Datei auf, die Sie mit fopen angelegt haben. (Im Beispiel war es gast.txt) Liegen Ihre Dateien bereits auf Ihrem Speicherplatz bei Ihrem Provider, verwenden Sie Ihr FTP-Programm zum Anzeigen der TXT-Datei. Die eben abgeschickten Daten müssten nun in der Textdatei untereinander aufgelistet werden. Das sieht so aus wie in Bild 6.

Auslesen der Gästebucheinträge

Wie auch im „wirklichen Leben“ ist ein elektronisches Gästebuch erst richtig interessant, wenn man nachschauen kann, was andere Gäste als Kommentar geschrieben haben. Die eingegebenen Informationen müssen also im Browser einsehbar sein. Diese Aufgabe lässt sich lösen mit dem so genannten Auslesen von Dateien. Dazu lernen Sie eine bisher nicht verwendete Funktion kennen:

readfile(); oder nur file();  

Die Funktion readfile() liest eine Datei und schickt den Inhalt an die Standardausgabe, also im Regelfall an den Browser. In der Klammer steht der Dateiname. Auch file() liest eine Datei, aber die Daten werden zeilenweise in einem Array abgelegt. Jede Zeile bildet ein Element des Arrays. Da dies für die Gästebucheinträge Sinn macht, schreiben Sie also in das Script

$eintrag=file('gast.txt');  

Info int readfile(string filename [int use_include_path])

Die Funktion liest eine Datei aus und gibt sie im Browser aus.

Dann setzen Sie eine Variable mit dem Wert <br>. Mit der Ausgabe der Variablen sorgen Sie dafür, dass zwischen den einzelnen Datensätzen jeweils ein Umbruch erfolgt:

$ausgabe="<br>";  

Die Variable $temp ist eine Hilfsvariable, mit der Sie die for-Schleife einfach definieren können. Mit der Funktion count($eintrag)-1; zählen Sie die Elemente des Arrays, und ziehen eins ab, weil $eintrag[0] ja das erste Element ist. Entsprechend ist das letzte Element: Anzahl -1.

Danach beginnen Sie die for-Schleife zu schreiben. Der Variablen $i wird der Wert der Variablen $temp zugewiesen (gezählte Elemente), dann wird die Bedingung geprüft, ob $i größer/gleich 0 ist. Ergibt die Prüfung true, fährt die Schleife fort mit der Ausführung des abschließenden Ausdrucks.

Der neueste Eintrag steht jeweils am Ende der Datei und deswegen im letzten Element des Arrays.

Wenn der neueste Eintrag oben angezeigt werden soll, muss man das Array von hinten nach vorn durchlaufen. Deswegen wird $i nicht inkrementiert, sondern dekrementiert.

for($i=$temp;$i>=0;$i--)  

In der Anweisung verwenden wir die Funktion explode().

Mit dieser Funktion lassen sich Zeichenketten anhand von Trennzeichen in Teile zerlegen. Sie sehen, dass in der Klammer das Trennzeichen angegeben wird und das Array Eintrag[$i], also das Array mit allen gezählten Elementen. Folglich zerlegt explode einen Datensatz mit Hilfe der Tilde in die einzelnen Feldinhalte. Die einzelnen Feldinhalte werden als Elemente des neuen Arrays abgelegt ($element).

Die einzelnen Elemente sollen in Form einer Tabelle untereinander angezeigt werden. Deswegen deklarieren Sie eine neue Variable, der über die Variablen-Erweiterung mit Hilfe des Punktes der Wert von $element zugewiesen wird sowie der HTML-Tag zum Erstellen einer Tabelle.

$ausgabe.="<table>";  

In der nächsten Zeile wird der Variablen die Zeile und Spalte zugewiesen sowie der Wert des ersten Elementes des Arrays $eintrag. Wir nehmen hier $eintrag[1], damit der Betreff als Erstes angezeigt wird. ($eintrag[0] ist der Name). Analog werden dann die nächsten Zeilen aufgebaut. Beachten Sie, dass auch das schließende </table>-Tag an die Variable $ausgabe angehängt wird.

Danach ist das Ende des Ausdrucks der for-Schleife erreicht, denken Sie also an die schließende geschweifte Klammer. Info array explode(string trennzeichen, string daten, [,int limit])

Die Funktion zerlegt eine Zeichenkette (daten) durch ein vorher festgelegtes trennzeichen. Alle Elemente werden in einem Array zurückgegeben.

Zu guter Letzt müssen Sie noch für die Ausgabe der in der Variablen $ausgabe festgelegten Werte sorgen. Vor dem Schließen des <body>-Tags ergänzen Sie das Script deswegen um die Zeile

<?php echo $ausgabe; ?>  

Im Code sehen Sie noch einmal den kompletten Teil, um den Sie das Script ergänzt haben:

$eintrag=file('gast.txt');  $ausgabe="<br>";  $temp=count($eintrag)-1;  for($i=$temp;$i>=0;$i--)  {  $element=explode('~',$eintrag[$i]);  $ausgabe.="<table>";  $ausgabe.="<tr><td><b>".$element[1]."</b></td></tr>";  $ausgabe.="<tr><td>".$element[0]."</td></tr>";  $ausgabe.="<tr><td>".$element[2]."</td></tr>";  $ausgabe.="</table>";  }  

Speichern Sie das Script ab und testen Sie es. Füllen Sie alle Felder des Formulars aus, und schicken Sie es ab. Diese Eingaben müssten nun oben in der Liste zu sehen sein. Die älteren Eingaben vom ersten Test werden darunter aufgelistet (es sei denn, Sie haben sie in der Textdatei gelöscht). In dem Bild 7 sehen Sie, wie die Seite in etwa aussehen müsste.

Info Es ist gut möglich, dass im Browser bestimmte Einträge zweimal oder mehrfach angezeigt werden, weil Sie die Daten beim Probieren auch mehrfach abgeschickt oder die Seite aktualisiert haben. Dieses Phänomen zu verhindern, ist nicht ganz einfach. Auch bei einigen professionellen Webseiten wird mit dem Hinweis auf der Seite: „Bitte das Formular nur einmal abschicken“ gegen dieses Problem angekämpft. Die Lösung werden wir nicht vorstellen können, aber alle benötigten Techniken werden in anderen Zusammenhängen erklärt. Der Weg ist folgender:

Beim ersten Aufruf des Formulars generieren Sie eine eindeutige Nummer. Diese Nummer wird in ein verstecktes Feld in das Formular geschrieben. Die gleiche Nummer speichern Sie in einer Datenbanktabelle, in der alle Nummern der noch nicht empfangenen Formulare gespeichert werden.

Nachdem das Formular abgeschickt wurde, testen Sie zunächst alle Angaben des Formulars. Sind alle diese Tests bestanden, suchen Sie die Nummer des Formulars in der Tabelle. Entdecken Sie sie, können Sie die Daten speichern. Anschließend löschen Sie die Nummer des Formulars aus der Tabelle, da die Daten dieses Formulars bereits gespeichert wurden.

Wird das gleiche Formular nun ein zweites Mal abgeschickt, steht in dem versteckten Formularfeld immer noch die gleiche Nummer. Die Suche nach dieser Nummer in der Tabelle wird aber ergebnislos verlaufen, da sie nach dem ersten Abschicken gelöscht wurde. In diesem Fall können Sie eine Fehlermeldung ausgeben und das wiederholte Speichern der Daten unterbinden.

PHP basiert letztlich auch auf C und C ist die Sprache der Funktionen. Das klassische C-Ideal besteht in einer Programmierung, die vorwiegend aus Funktionen besteht, von denen jede eine bestimmte, generische Aufgabe erfüllt und daher wiederverwendbar ist. Funktionen erwarten meistens einen oder mehrere Parameter als Input und liefern einen Return-Wert (Rückgabewert) als Output. Im Prinzip also soll eine typische Funktion ein kleines EVA-Programm innerhalb des Gesamtprogramms sein (EVA = Eingabe, Verarbeitung, Ausgabe – das Paradigma der klassischen Datenverarbeitung aus den Zeiten bildschirmloser, magnetbandgefütterter Rechner).

Nun stellt PHP schon sehr viele Funktionen bereit.

Dennoch bieten sich bei der Programmierung genügend Gelegenheiten, eigene Funktionen zu definieren. Es genügt schon, wenn ein Set aus Anweisungen an mehreren Stellen im Script-Code wiederholt werden muss. In diesem Fall bietet es sich an, das Anweisungs-Set in eine Funktion zu schreiben und diese bei Bedarf aufzurufen. Die Funktion hat dann die Aufgabe eines Unterprogramms, einer Sub-Routine.

Schema

Eine typische Funktion hat folgenden Aufbau:

function Funktionsname($Parameter) {  # Anweisungen;  return($Wert)  }  

Eingeleitet wird eine Funktionsdefinition durch das Schlüsselwort function. Dahinter folgt, durch Leerzeichen getrennt, ein frei wählbarer Funktionsname. Der Name kann wie ein Variablenname aus Buchstaben, Ziffern und Unterstrich bestehen, wobei der Name mit einem Buchstaben oder Unterstrich beginnen muss. Groß- und Kleinschreibung von Buchstaben werden nicht unterschieden, d.h., es ist egal, ob Sie eine Funktion mit test() oder TEST() aufrufen. Funktionsnamen müssen script-weit eindeutig sein. Wenn Sie beispielsweise andere Scripts über include_once() oder ähnliche Funktionen einbinden, dann dürfen in den anderen Scripts keine gleichnamigen Funktionen vorkommen. Nur Methoden verschiedener Objekte dürfen gleichnamig sein.

Der gesamte Funktionskörper muss in geschweifte Klammern eingeschlossen werden,

auch dann, wenn die Funktion aus nur einer Anweisung besteht.

Falls die Funktion an die aufrufende Anweisung einen Wert zurückgeben will, kann sie dazu die PHP-Funktion return() verwenden. Als Parameter wird dieser Funktion übergeben, was zurückgegeben werden soll. Meistens ist das eine Variable, in der ein ermittelter Wert steht. Es kann jedoch auch eine literale Zeichenkette oder eine aus literalen Teilen und Variablen zusammengesetzte Zeichenkette sein. Wenn mehrere Werte zurückgegeben werden sollen, können diese in einem Array gesammelt werden. Zurückgegeben wird dann die Array-Variable. Durch return() wird die Funktion sofort verlassen.

Parameter und lokale Variablen

Variablen, die innerhalb eines Funktionskörpers definiert werden, gelten nur innerhalb dieser Funktion und sind außerhalb davon nicht bekannt. Innerhalb einer Funktion dürfen also durchaus Variablennamen vorkommen, die auch außerhalb davon vorkommen. Die Variablen außerhalb der Funktion werden durch diejenigen innerhalb der Funktion nicht berührt.

$i = 0;  function set_i() {  $i = 23; }  set_i();  echo $i;  

Das Beispiel gibt 0 aus, weil die Funktion lost set_i(), die in ihrem Funktionskörper einer Variablen $i einen Wert zuweist, keinen Einfluss auf die Variable $i hat, die zuvor global mit 0 initialisiert wurde.

Auch Parameternamen, die bei einer Funktion definiert werden, sind lokale Variablen der Funktion.

Funktionsaufruf und Rückgabewert

Nachfolgendes Beispiel zeigt einen Komplettzusammenhang zwischen Funktionsaufruf, Funktion, Parametern und Rückgabewert (Return-Wert).

$mylink = set_link("http://www.example.org/", "Beispiel-Link");  echo $mylink;  function set_link($uri, $text) {  $str = "<a href=\"$uri\">$text</a>";  return($str);  }  

Das Beispiel zeigt zunächst, dass ein Funktionsaufruf durchaus vor der Funktion selbst notiert werden darf. Der Grund dafür ist, dass PHP ein Script vor der Ausführung ohnehin erst einmal on the fly kompiliert. Dabei werden intern auch alle Funktionen und Variablen registriert.

Im Beispiel wird eine Funktion namens set_link() aufgerufen. Ihr werden in den runden Klammern hinter dem Funktionsnamen zwei Zeichenkettenparameter übergeben. Mehrere Parameter werden durch Kommata getrennt. Auch wenn keine Parameter übergeben werden, müssen die runden Klammern notiert werden.

Da die Funktion set_link() einen Rückgabewert erzeugt, kann sie wie im Beispiel einer Variablen zugewiesen werden. In der Variablen $mylink steht im Beispiel am Ende das, was die Funktion set_link() zurückgibt. Funktionen, die einen Wert zurückgeben, können aber auch direkt in Prozessanweisungen oder sogar als Parameter an andere Funktionen übergeben werden. So wäre es im obigen Beispiel sogar kürzer, gleich zu notieren:

echo set_link("http://www.html-info.eu/", "Beispiel-Link");  

Angenommen, es gibt eine zweite Funktion namens set_link_style(), die als ersten Parameter einen kompletten HTML-Hyperlink erwartet und als zweiten Parameter den Namen einer CSS-Klasse, die dem Link zugewiesen werden soll. Dann könnten Sie im Beispiel notieren:

$mylink = set_link_style(set_link("http://www.html-info.eu/", "Beispiel-Link"), "extern_link");  

Als erster Parameter wird beim Aufruf von set_link_style() ein Aufruf von set_link() übergeben. Der PHP-Interpreter weiß, dass er in diesem Fall zuerst set_link() ausführen muss und den davon zurückgegebenen Wert in den Aufruf von set_link_style() einsetzen muss.

Default-Werte bei Parametern

Eine PHP-spezifische Besonderheit muss erwähnt werden. Bei Parameterdefinitionen im Funktionskopf können auch Defaultwerte angegeben werden.

function line_numbered_file($file, $color="red") {  $lines = file($file);  $chars = strlen((string) count($lines));  for($i = 1; $i <= count($lines); $i++) {  $line = str_replace("%", "%%", $lines[$i]);  $format = "<span style=\"color:".  $color."\">%".$chars."d</span> ".$line;  $numbered_lines[] = sprintf($format, $i);  }  return($numbered_lines);  }  $file_lines = line_numbered_file("/etc/php.ini");  echo "<pre>";  foreach($file_lines as $line)  echo $line;  echo "</pre>";  

Die im Beispiel definierte Funktion line_numbered_file() versieht die Zeilen einer Textdatei mit führenden Zeilennummern. Dabei werden die Zeilennummern in HTML andersfarbig formatiert. Welche Textfarbe die Zeilennummern erhalten sollen, wird mit dem zweiten Parameter $color übergeben. Sollte dieser Parameter beim Funktionsaufruf fehlen, verwendet PHP den Default-Wert red. Grund dafür ist die Notation $color="red" in der Parameterdefinitionsliste in den runden Klammern der Funktion.

Normalerweise gibt PHP eine Warnung aus,

wenn ein Funktionsaufruf eine Funktion nicht mit all den Parametern versorgt, die sie erwartet. Wenn jedoch bei der Funktionsdefinition mit der beschriebenen Syntax Defaultwerte für Parameter definiert werden, dann dürfen diese Parameter beim Aufruf auch weggelassen werden.

Auf den Inhalt der Funktion line_numbered_file() gehen wir an dieser Stelle nicht näher ein, weil uns das zu weit vom Thema abbringen würde.

Variable Anzahl von Parametern

Eine weitere Möglichkeit besteht darin, bei der Funktionsdefinition überhaupt keine Parameter zu definieren. Dennoch können der Funktion beim Aufruf Parameter übergeben werden, und zwar beliebig viele. PHP stellt einige Funktionen bereit, mit deren Hilfe eine Funktion auf ihr übergebene Parameter zugreifen kann.

$a = 17;  $b = 64;  $c = 23;  function sum() {  $x = 0;  $params = func_get_args();  foreach($params as $param)  $x += $param;  return($x);  }  echo sum($a, $b, $c);  

Die Funktion sum() im Beispiel ist in der Lage, die Summe einer beliebigen Anzahl übergebener Zahlenwerte zu ermitteln und zurückzugeben. Dabei erwartet sie gar keine Parameter. Falls sie ohne Parameter aufgerufen würde, würde sie einfach 0 zurückgeben.

Mit der PHP-Funktion func_get_args() kann eine Funktion alle ihr übergebenen Parameter als Array ermitteln. In unserer Beispielfunktion wird der Array in $params gespeichert. In einer foreach-Schleife arbeitet die Funktion den Array $params ab und erhöht $x um den jeweiligen Wert, in der Annahme, dass lauter numerische Werte übergeben wurden.

Die echo-Ausgabe am Ende des Beispiels gibt 104 aus, weil der Funktion sum() beim Aufruf die drei zu Beginn initialisierten Variablen $a, $b und $c übergeben werden.

Zeiger (Referenzen) statt Werte übergeben

In Script-Sprachen wie PHP wird nicht so ausufernd mit so genannten Zeigern (Pointern) gearbeitet wie etwa in C. Dennoch bietet auch PHP die Möglichkeit an, einer Funktion anstelle eines Werts nur die Speicheradresse zu übergeben, an der der Wert beginnt. Sinnvoll ist dies beispielsweise, wenn als Parameter eine 1 Megabyte große Zeichenkette übergeben wird. Anstelle eines Megabyte werden nur ein paar Byte übergeben, eben die Arbeitsspeicheradresse der Variablen mit dem großen Inhalt. Damit PHP weiß, dass ein übergebener Wert nicht wirklich ein Wert ist, sondern nur eine Arbeitsspeicheradresse, muss er kenntlich gemacht werden.

$satz = "Ein langer Satz mit vielen Wörtern";  function count_words(&$str) {  return(count(explode(" ",$str)));  }  echo count_words($satz);  

Die Zeigertechnik wird bei der Parameterdefinition der Funktionsdefinition angewendet. Im Beispiel bewirkt &$str, dass die Funktion beim Aufruf keine Kopie des übergebenen Werts erhält, sondern dessen Arbeitsspeicheradresse. Das kaufmännische Und (&) vor dem Parameternamen genügt, um den Parameter als Referenz bzw. Zeiger zu deklarieren.

Beim Aufruf der Funktion muss (im Gegensatz etwa zu C) nichts beachtet werden. Im Beispiel wird der Funktion einfach ein Variablenname übergeben.

Wichtig zu wissen ist jedoch, dass Funktionen, die eine als Referenz übergebene Variable ändern, nicht die Kopie ändern, sondern das Original. In manchen Fällen kann dies sogar explizit gewünscht sein, in anderen dagegen nicht.

In PHP 4.x ist die Möglichkeit objektorientierter Programmierung zwar im Prinzip angelegt, doch fehlen noch entscheidende Features wie Regelungen zur Sichtbarkeit von Variablen. Dennoch lohnt es sich bei größeren Projekten, gleich mit objektorientierter Programmierung zu beginnen.

Grundbegriffe

Eine Klasse ist in der objektorientierten Programmierung das, was in der Textverarbeitung eine Dokumentvorlage ist. So, wie in einer Dokumentvorlage Seitenformate, Absatz- und Zeichenformate sowie weitere Vorgaben definiert werden, werden in einer Klasse Datenstrukturen und Funktionen definiert.

Ein Objekt ist dann ein konkretes Dokument. So, wie ein Dokument aus einer Dokumentvorlage erstellt werden kann, kann ein Objekt aus einer Klasse erzeugt werden. Andere Bezeichnungen für „Objekt“ sind Instanz und Exemplar.

Eine Objekteigenschaft oder einfach Eigenschaft ist eine Variable, die in der Klasse definiert wurde, aus der das Objekt erzeugt wurde.

Eine Objektmethode oder einfach Methode ist eine Funktion, die in der Klasse definiert wurde, aus der das Objekt erzeugt wurde.

Klassen und Objekte anlegen

Beginnen wir mit einem ganz einfachen Beispiel, um das Prinzip zu demonstrieren:

class MiniClass {  var $test = 23;  }  $mini_object = new MiniClass;  echo $mini_object->test;  

In diesem Beispiel wird eine Klasse mit dem Namen MiniClass definiert. Eine Klassendefinition beginnt mit dem Schlüsselwort class. Dahinter folgt, durch Leerzeichen getrennt, der gewünschte Klassenname. Bei der Wahl des Klassennamens sind Sie frei. Klassennamen müssen jedoch innerhalb eines gesamten Script-Kompilats eindeutig sein. Ansonsten gelten die gleichen Regeln wie bei Variablen und Funktionsnamen. Es hat sich jedoch eingebürgert, Klassennamen mit Großbuchstaben beginnen zu lassen.

Der gesamte Inhalt einer Klasse muss in geschweifte Klammern eingeschlossen werden,

genauso wie bei einer Funktion. Innerhalb einer Klasse darf nichts anderes notiert sein als Variablendefinitionen oder Funktionen. In unserem Minimalbeispiel ist lediglich eine einzige Variablendefinition notiert. Variablendefinitionen innerhalb von Klassen werden durch das Schlüsselwort var eingeleitet. Ansonsten gelten die gleichen Regeln wie bei normalen Variablen. Variablen können mit einem Wert initialisiert werden wie im Beispiel. Zwingend erforderlich ist das jedoch nicht. Wenn eine Variable zunächst keinen Wert haben soll, notieren Sie einfach var $name; – also direkt hinter dem Variablennamen das Semikolon zum Abschließen der Anweisung.

Wenn Sie Klassenvariablen mit einem Wert initialisieren wollen wie im Beispiel, darf dies nur ein konstanter Wert sein, also etwa eine Zahl oder eine Zeichenkette, nicht aber ein ermittelter Wert, wie er z.B. durch Aufruf von PHP-Funktionen oder anderen, eigenen Funktionen zustande käme. Falls Sie den Initialisierungswert der Variablen doch ermitteln lassen wollen, benötigen Sie eine Konstruktorfunktion. Dazu weiter unten mehr.

In unserem Minimalbeispiel wird unterhalb der Klasse eine Variable namens $mini_object definiert. Als Wert wird ihr new MiniClass zugewiesen. Mit dem Schlüsselwort new signalisieren Sie, dass Sie aus dieser Variable eine Objektvariable machen möchten. Dahinter geben Sie den Klassennamen an, aus dem ein Objekt erzeugt werden soll. In unserem Beispiel wird $mini_object zu einer Objektvariable für die Klasse MiniClass.

Die Variablendefinition

$mini_object = new MiniClass ist also nichts anderes als das Erzeugen eines Objekts aus der Klasse MiniClass. Das Objekt ist in der Variablen $mini_object gespeichert. Über diese Objektvariable ist der Zugriff auf die Eigenschaften (Variablen) und Methoden (Funktionen) der Klasse möglich.

Der Zugriff erfolgt über den „Zeige-Operator“ lost -> (Minuszeichen und Größer-als-Zeichen direkt hintereinander notiert). In unserem Minibeispiel wurde in der Klasse MiniClass eine Variable $test definiert. Aus Sicht der Objektvariablen $mini_object ist das eine Objekteigenschaft. Durch die Syntax $mini_object->test können Sie auf den Wert der Eigenschaft zugreifen. Beachten Sie dabei, dass vor dem Namen der Eigenschaft, hier also test, kein $-Zeichen notiert werden darf!

Das Beispiel gibt 23 aus, weil es einfach $mini_object->test ausgibt und darin die in der Klassendefinition festgelegte Vorbelegung gespeichert ist. Die Objekteigenschaft kann jedoch wie jede andere Variable auch geändert werden.

Klassen mit Konstruktortfunktion

Eine Klasse darf wie schon erwähnt nichts anderes enthalten als Variablendefinitionen mit var und Funktionen. Wenn innerhalb der Klasse eine Funktion definiert wird, die exakt den gleichen Namen hat wie die Klasse selbst, dann gilt diese Funktion als so genannte Konstruktorfunktion. Das Besondere an der Konstruktorfunktion ist, dass sie beim Anlegen eines neuen Objekts mit $variable = new $Klassenname automatisch mit ausgeführt wird.

class MiniClass {  var $test;  function MiniClass() {  $this->test = ini_get("max_execution_time");  } }  $mini_object = new MiniClass;  echo $mini_object->test;  

Die Klasse MiniClass enthält nach wie vor eine Variablendefinition, wobei die Variable $test jedoch jetzt nicht mehr initialisiert wird. Sie soll zwar initialisiert werden, aber mit einem ermittelten Wert, was bei der Definition mit var innerhalb der Klasse nicht erlaubt ist. Die Initialisierung holen wir deshalb in einer Konstruktorfunktion nach. Diese ist im Beispiel die Funktion MiniClass(), weil sie exakt genauso heißt wie die Klasse, innerhalb deren sie definiert wird.

Innerhalb der Konstruktorfunktion wird die Variable

$test initialisiert, und zwar zum Spaß mit einem Aufruf der PHP-Funktion ini_set(). Diese holt Werte aus der php.ini. In unserem Fall ermittelt sie, wie lange das Script maximal laufen darf, bevor es gewaltsam beendet wird.

Wichtig zu kennen ist die Syntax: Wenn innerhalb einer Klasse auf Klassenvariablen zugegriffen werden soll, also auf Variablen, die außerhalb von Funktionen mit var definiert wurden, dann muss das mit $this->variablenname geschehen.

Das Beispiel-Script gibt am Ende den Wert aus der php.ini aus, der dort bei der Direktive max_execution_time zugewiesen ist.

Konstruktorfunktion mit Parametern

Eine Konstruktorfunktion, so haben wir schon gelernt, ist nicht zwingend erforderlich. Sie ist beispielsweise dann nötig, wenn Initialisierungswerte von Klassenvariablen durch Funktionsaufrufe oder Ähnliches ermittelt werden sollen. Ein anderer wichtiger Grund, eine Konstruktorfunktion zu verwenden, wäre der Wunsch, Parameter an eine Klasse zu übergeben.

class RGBColor {  var $R = 0;  var $G = 0;  var $B = 0;  function RGBColor($r, $g, $b) {  if($r >= 0 and $r <= 255)  $this->R = $r;  if($g >= 0 and $g <= 255)  $this->G = $g;  if($b >= 0 and $b <= 255)  $this->B = $b;  }  }  $my_blue = new RGBColor(40,80,220);  echo $my_blue->B;  

In diesem Beispiel wird eine Klasse namens RGBColor definiert, in der ein RGB-Wert gespeichert werden kann. Sie enthält neben den drei Klassenvariablen $R, $G und $B für jeden Farbanteil eine gleichnamige Funktion RGBColor, die dadurch ihre Konstruktorfunktion ist. Diese Funktion erwartet drei Parameter (für die drei Farbanteile).

Beim Erzeugen des Objekts wird die Klasse dann wie eine Funktion aufgerufen. Nur durch das Schlüsselwort new davor ist noch erkennbar, dass eine Klasse und keine Funktion aufgerufen wird.

Die Konstruktorfunktion bekommt die beim Klassenaufruf übergebenen Parameter übergeben. In unserem Beispiel prüft sie, ob sich die Werte im erlaubten Bereich zwischen 0 und 255 befinden. Wenn ja, weist sie den Klassenvariablen die übergebenen Werte zu.

Abgeleitete und erweiternde Klassen

Angenommen, Sie haben folgende Klasse:

class Person {  var $given_name;  var $family_name;  var $birthday_dd;  var $birthday_mm;  var $birthday_yyyy;  }  

Sie haben möchten nun außerdem eine Klasse für Kontaktdaten anlegen. Dann bietet Ihnen das Konzept der objektorientierten Programmierung an, die existierende Klasse Person zu kopieren und zu erweitern.

class Contact extends Person {  var $street;  var $zip_code;  var $location;  var $phone_number;  var $mobile_number;  var $mail_address;  }  

Bei der Definition einer Klasse können Sie hinter dem Klassennamen das Schlüsselwort extends und dahinter den Namen einer anderen existierenden Klasse angeben. Dadurch binden Sie alle dort definierten Klassenvariablen (Eigenschaften) und Funktionen (Methoden) in die neu anzulegende Klasse mit ein. Die neue Klasse kann dann zusätzliche Variablen und Funktionen definieren.

Im Beispiel wird eine Klasse Contact definiert, welche die Klasse Person erweitert. Innerhalb von Contact kann nun z.B. innerhalb einer Methode auf $this->given_name zugegriffen werden, da alle Definitionen aus $Person in $Contact übernommen wurden. Ebenso verhält es sich bei Objekten, die aus Contact erzeugt werden.

$julia = new Contact;  $julia->given_name = "Julia";  echo $julia->given_name;  

Wichtig zu wissen ist noch, wie es sich bei abgeleiteten Klassen mit Konstruktorfunktionen verhält. Hat die abgeleitete Klasse eine eigene Konstruktorfunktion, so wird diese beim Erzeugen eines Objekts aufgerufen, die der Elternklasse dagegen nicht. Gibt es in der abgeleiteten Klasse dagegen keine Konstruktorfunktion, so wird, falls die Elternklasse eine Konstruktorfunktion besitzt, diese aufgerufen. Vor allem Letzteres müssen Sie als Programmierer wissen, denn wenn die Konstruktorfunktion der Elternklasse Parameter erwartet, müssen Sie die abgeleitete Klasse beim Erzeugen eines Objekts mit den Parametern aufrufen, die von der Konstruktorfunktion der Elternklasse erwartet werden.

Direktaufruf von Methoden

Mithilfe des Operators :: ist es möglich, Methoden aus Klassen direkt aufzurufen, ohne ein Objekt der Klasse zu erzeugen.

class Member {  var $name = "Max Mustermann";  function show_name() {  echo "bla"; }  }  Member::show_name();  

Mit der Syntax Klassenname::Methodenname() kann eine Methode aus einer Klasse direkt ausgeführt werden. Allerdings mit einer Einschränkung: Die Methode darf nicht mit $this->Variablenname auf Klassenvariablen Bezug nehmen, da die Klassenvariablen ohne Objektinstanz nicht existieren. Das obige Beispiel gibt bla aus. Würde in show_name() jedoch stehen: echo $this->name, dann würde beim Aufruf über Member::show_name() gar nichts ausgegeben, da in diesem Aufrufkontext keine Klassenvariable $name existiert.

Praxisbeispiel: Template-Klasse

Das Beispiel besteht aus folgenden PHP-Scripts:

Die übrigen Dateien für Templates, Inhaltsdateien und CSS-Datei bleiben wie gehabt.

Zunächst der Quelltext des zentralen Scripts:

<?php  include_once("class.template.php");  include_once("class.page.php");  $main_template = new Template;  $main_page = new Page;  $content_files = array();  $content_files['home'] = "0001.txt";  $content_files['impressum'] = "0007.txt";  $content_files['themen'] = "0003.txt";  $content_files['branchen'] = "0009.txt";  $content_files['auskunft'] = "0005.txt";  $content_files['geo'] = "0002.txt";  $content_files['wissen'] = "0004.txt";  $content_files['literatur'] = "0008.txt";  $content_files['lexika'] = "0006.txt";  $content_files['glossare'] = "0010.txt";  $content_files['wikis'] = "0014.txt";  $content_files['ftp'] = "0011.txt";  $content_files['wais'] = "0015.txt";  $content_files['newsgroups'] = "0012.txt";  $content_files['foren'] = "0013.txt";  if(isset($_GET['page']))  $get_page = $_GET['page'];  else  $get_page = 'home';  $main_template->template_file = "linkweb.tpl";  $main_page->page_content = $main_template->get_template();  $main_page->set_var('content',  file_get_contents($content_files[$get_page]));  $main_page->set_var('title', $main_page->get_title());  $main_page->show_page();  exit();  ?>  

Dieses Script enthält selber keine Klasse, sondern regelt nur den Gesamtablauf. Die Definition des Arrays $content_files haben wir der Einfachheit halber mit in das zentrale Script übernommen – bei größeren Projekten empfiehlt sich aber, wie früher erwähnt, ein eigenes PHP-Script für globale Variablendefinitionen.

Die eigentliche Arbeit des Gesamt-Scripts wird jedoch von Script-Dateien erledigt,

die im Wesentlichen aus Klassen bestehen. Im Zentralscript werden oben die Scripts class.template.php und class.page.php eingebunden. Die Dateinamen sind natürlich frei wählbar. Es ist jedoch guter Stil, PHP-Scripts, die eigentlich nur eine Klasse enthalten, im Dateinamen entsprechend zu bezeichnen.

Unterhalb der include-Anweisungen sind denn auch gleich die Definitionen der benötigten Objektvariablen notiert. In $main_template wird ein Objekt der Klasse Template gespeichert und in $main_page ein Objekt der Klasse Page. Der Script-Ablauf im Zentral-Script besteht wie schon im früheren Beispiel darin, zunächst den übergebenen GET-Parameter page abzufragen, um zu ermitteln, welche Seite angezeigt werden soll.

Dann wird mit den beiden erzeugten Objekten gearbeitet. Zunächst wird der Eigenschaft template_file des Template-Objekts die gewünschte Template-Datei zugewiesen. Über die Methode get_template() wird die Template-Datei eingelesen. Der Rückgabewert der Methode ist die eingelesene Datei. Dieser wird gleich der Eigenschaft page_content des Page-Objekts zugewiesen. So lesen wir in einer einzigen Anweisung das HTML-Template ein und kopieren es in den zu erstellenden Seiteninhalt.

Nun müssen die aus dem Template kopierten Platzhalter des Typs

[%name%] durch seitenabhängige Inhalte ersetzt werden. Dies besorgt die Methode set_var() des Page-Objekts. Ihr werden zwei Parameter übergeben: der Platzhaltername, bei [%name%] also 'name', und der gewünschte Wert, durch den der Platzhalter ersetzt werden soll. Das Zentral-Script muss die Platzhalter [%content%] und [%title%] ersetzen. Bei [%content%] setzt es seitenabhängig die passende Datei aus dem Array $content_files ein und bei [%title%] kann es den Inhalt durch Aufruf der Methode get_title des Page-Objekts ermitteln. Die Methode get_title sucht im bislang vorhandenen HTML-Code der Seite nach einer h1-Überschrift, ermittelt deren Inhalt und gibt ihn zurück.

Am Ende muss das Zentral-Script nur noch die Methode show_page() des Page-Objekts aufrufen, um seine Arbeit abzuschließen und die gewünschte Seite auszugeben.

Das gesamte Script-Ensemble wird natürlich erst verständlich durch die Quelltexte der Dateien mit den Klassen. Zunächst die Template-Klasse:

<?php  #-------------------------------------------  # Includes:  include_once("class.error.php");  $template_error = new Error;  #-------------------------------------------  # Klasse Template:  class Template {  var $template_file;  var $template_content;  function get_template() {  global $template_error;  if(empty($this->template_file))  $template_error->handle_error  ('Keine Template-Datei angegegeben!');  if(! file_exists($this->template_file))  $template_error->handle_error  ('Template-Datei nicht gefunden!');  $this->template_content =  file_get_contents($this->template_file);  return($this->template_content);  } }  ?>  

Die Script-Datei zeigt den typischen Aufbau einer Script-Datei, die lediglich eine Klasse enthält. Im globalen Bereich vor der Klassendefinition werden andere Scripts eingebunden, die mehrfach benötigt werden. In unserem Fall wird class.error.php benötigt. Da dies auch eine Klasse ist, muss natürlich auch ein Objekt daraus erzeugt werden, das in der Objektvariablen $template_error gespeichert wird.

Umgekehrt werden wir in class.error.php sehen, dass dort ein Template zur Ausgabe der Fehlerseite benötigt wird und dass dazu ein Template-Objekt in der Variablen $error_template erzeugt wird. Eine solche Stringenz sowohl bei Dateinamen als auch bei Variablennamen ist bei größeren Projekten dringend zu empfehlen, da ansonsten leicht Probleme mit doppelt vorkommenden Variablen vorkommen können und die Gesamtübersicht einfach leichter verloren geht.

Die Klasse Template wird durch class Template {} definiert. Der Inhalt besteht aus zwei Klassenvariablen (Objekteigenschaften) namens $template_file und $template_content. In der einen muss ein Template-Objekt den Pfadnamen der gewünschten Template-Datei ablegen und in der anderen wird nach Aufruf der einzigen Klassenfunktion (Objektmethode) get_template() der Inhalt der Template-Datei gespeichert.

Eine Konstruktorfunktion wird in dieser Klasse nicht benötigt. Als Nächstes der Quelltext der Error-Klasse:

<?php  #-------------------------------------------  # Includes:  include_once("class.template.php");  include_once("class.page.php");  $error_template = new Template;  $error_page = new Page;  #-------------------------------------------  # Klasse Error:  class Error {  var $error_template = "error.tpl";  function handle_error($message) {  global $error_template;  $error_template->template_file = $this->error_template;  $error_page->page_content =  $error_template->get_template();  $error_page->set_var('message', $message);  $error_page->show_page();  exit();  } }  ?>  

Das Script mit der Error-Klasse benötigt die Klassen Template und Page, bindet dazu die nötigen Script-Dateien ein und erzeugt die erforderlichen Objekte in den Objektvariablen $error_template und $error_page.

Die Error-Klasse selbst besteht aus einer Klassenvariable für den Pfadnamen der Error-Template-Datei sowie aus der Funktion handle_error(). Deren Aufgabe ist eine ähnliche wie die des Zentral-Scripts. Sie muss die Error-Template-Datei einlesen und in die Objekteigenschaft page_content des erzeugten Page-Objekts kopieren. Dann muss sie noch mit der set_var-Methode des Page-Objekts den Platzhalter [%message%] durch die Fehlermeldung ersetzen, die beim Aufruf von handle_error() übergeben werden muss, und zuletzt kann sie die Fehlerseite ausgeben und den Scriptlauf beenden.

Nun noch der Quelltext der Page-Klasse:

<?php  #-------------------------------------------  # Klasse Page:  class Page {  var $page_content;  var $standard_title = "untitled";  function set_var($name, $value) {  if(empty($this->page_content))  return;  else {  $this->page_content =  str_replace("[%$name%]",  $value, $this->page_content);  return($this->page_content);  } }  function show_page() {  if(empty($this->page_content))  return; else  echo $this->page_content;  }  function get_title() {  if(empty($this->page_content))  return;  else {  $pattern = "/<h1>(.*)<\/h1>/";  preg_match($pattern, $this->page_content, $matches);  if(isset($matches[1]))  return($matches[1]);  else  return($this->standard_title);  }  }  }  ?>  

Die Page-Klasse benötigt keine anderen Scripts. Die Klasse selbst besteht aus den Klassenvariablen $page_content und $standard_title. In $page_content wird der komplette HTML-Code einer auszugebenden Seite gespeichert. In diese Variable muss zunächst der durch Einlesen des Templates gewonnene Inhalt kopiert werden, bevor die Methoden des Page-Objekts sinnvoll anwendbar sind. Die Initialisierung von $standard_title soll nur einen Defaultwert für den Titel vorgeben, falls die Funktion get_title() keine h1-Überschrift ermitteln kann, aus der sie den Titel gewinnen kann.

Die Funktionen der Page-Klasse sind bereits aus den Aufrufen als Objektmethoden bekannt: set_var() erlaubt es, Platzhalter des Schemas [%name%] durch Inhalte zu ersetzen. Die Methode aktualisiert dabei die Klassenvariable $page_content, gibt deren Wert aber zusätzlich auch an eine aufrufende Anweisung zurück. show_page() gibt eine fertig erzeugte Seite aus und get_title() sucht nach einer h1-Überschrift und gibt deren Inhalt zurück.

Ohne die Anwendung von Kontrollstrukturen kommen Sie beim Programmieren nicht aus. Hinter der Bezeichnung Kontrollstrukturen verbergen sich Bedingungen und Schleifen. Mit einer Bedingung ist gemeint, dass man PHP anweist, etwas zu vergleichen und dann in Abhängigkeit von dem Resultat des Vergleichs bzw. der Prüfung die eine oder andere Aktion durchzuführen. Erst mit dem Einsatz dieser Möglichkeit kreieren Sie wirklich dynamische Seiten, denn es erfolgen unterschiedliche Reaktionen je nach vorausgegangenen „Ereignissen“.

PHP kennt zwei Bedingungen: die if-Anweisung und die switch-Anweisung.

Die if-Anweisung

Diese Anweisung benutzen Sie, wenn Sie im Klartext sagen möchten: Wenn eine Bedingung, die mit Hilfe eines Ausdrucks formuliert wird, erfüllt ist, also true (wahr) ergibt, dann soll der angegebene Befehlsblock ausgeführt werden. Ist der Ausdruck false (falsch), wird dieser Programmblock ignoriert und die Programmausführung mit dem nachfolgenden Befehl fortgesetzt. Eine if-Anweisung wird folgendermaßen geschrieben:

if(Bedingung) {was soll geschehen;}  

In der runden Klammer steht der logische Ausdruck, in der geschweiften Klammer, auch als Block bezeichnet, der Dann-Teil der Anweisung bzw. Anweisungen. Jede Anweisung muss mit Semikolon abgeschlossen werden, aber Sie können im Prinzip beliebig viele Anweisungen in den Dann-Teil schreiben. Am Ende folgt die geschlossene geschweifte Klammer. Das folgende Listing zeigt einen Ausschnitt eines Scripts mit einer if-Anweisung.

<?php  if($kontakt=="e-mail")  {  echo "geben Sie eine e-mail-Adresse ein";  }  ?>  

Hier wurde (z.B. durch eine Option eines Auswahlfeldes in einem Formular) eine Kontaktoption (e-mail) übergeben, die in der Variablen $kontakt gespeichert ist.

Grundsätzlich kann die if-Bedingung weiter verschachtelt werden, d.h. jeder Block kann erneut einen if-Befehl enthalten. Dies führt aber – wie Sie sich denken können – zu ziemlich unübersichtlichen Codes und ist deshalb nicht unbedingt empfehlenswert. In der Regel gibt es Alternativen.

Vergleichsoperatoren und logische Operatoren

Für den richtigen Gebrauch der if-Anweisung müssen Sie noch einiges wissen. Wenn Sie als Bedingung einfach einen zuvor definierten Variablennamen verwenden, sagen Sie damit quasi so etwas wie: Wenn die Variable existiert und wenn sie einen Wert hat, der nicht Null ist, ergibt die Prüfung true. Anders bei einem Vergleich. Wenn Sie eine if-Anweisung dazu einsetzen, um zu prüfen, ob sie mit einem spezifischen Wert übereinstimmt, dann brauchen Sie einen Vergleichsoperator. In diesem Fall ist dies nicht einfach das Gleichheitszeichen (mit dem Sie einer Variablen einen Wert zuweisen), sondern es sind zwei Gleichheitszeichen hintereinander ==. Es ist wichtig, sich daran zu erinnern:

if($anrede == "Frau")  

müsste es also heißen, wenn eine Anweisung ausgeführt werden soll, sofern als Anrede Frau übergeben und der Wert in der Variablen $anrede gespeichert ist. Ist dies der Fall, ergibt die Prüfung der Bedingung true (wahr).

Doppelachtung! Wenn Sie in einer if-Bedingung die doppelten Gleichheitszeichen vergessen, gibt PHP keine Fehlermeldung aus, sondern nimmt die mit dem einfachen Gleichheitszeichen angewiesene Zuweisung vor. Bei der Zuweisung ändert sich der Wert der Variablen.

Nach if($anrede = "Frau") steht in $anrede der Wert „Frau“.

Diese Fehler sind schwer zu finden und zeigen keine typischen Auswirkungen auf das Script, sodass man nicht sagen kann: Immer, wenn das und das passiert, haben Sie ein doppeltes Gleichheitszeichen in der if-Bedingung vergessen. Der einzige Hinweis, den wir Ihnen geben können, ist der folgende:

Gibt das Script keinen Fehler aus und scheint der Code eigentlich richtig zu sein, aber die Ausgabe des Scripts ergibt partout nicht das, was Sie sich gedacht haben, dann kontrollieren Sie die doppelten Gleichheitszeichen.

Andere Vergleichsoperatoren sind Ihnen wahrscheinlich bekannt. So können Sie in einer if-Anweisung die mathematischen Zeichen für Größer als und Kleiner als benutzen, also > und < bzw. >= und <=, beispielsweise so:

if($kosten < 50) {echo "Zahlen Sie bitte per Scheck";}  

Einen Überblick über die Operatoren bietet die Tabelle.

Symbol Bedeutung
== Gleichheit
!= Ungleichheit
> Größer als
< Kleiner als
<= Kleiner als oder gleich
>= Größer als oder gleich

Vermutlich kennen Sie noch aus der Schule auch die speziellen (logischen) Operatoren, mit denen Ausdrücke verknüpft werden. Dies sind:

Sie können diese Operatoren bei if-Bedingungen einsetzen. Wenn Sie beispielsweise $variable1 AND $variable2 schreiben, bedeutet dies, dass nur wahr zurückgegeben wird, wenn beide Variablen wahr sind. Bei OR reicht es, wenn eine der Variablen wahr ist, um als Ergebnis wahr auszugeben. Mit XOR ergibt die Prüfung falsch, wenn beide Variablen gleich sind.

Verwendung von if-else

Neben der reinen if-Anweisung kommt sehr häufig die nächste logische Bedingung ins Spiel: die if-Anweisung, erweitert durch else (sonst). Mit der else-Klausel wird ein Code ausgeführt, wenn die Prüfung der Bedingung false ergeben hat. Die Syntax ist folgende:

if (Bedingung) {Anweisung1;}  else {Anweisung2;}  

Das würde dann in einem Script beispielsweise so aussehen:

if($kosten < 50) {echo "Zahlen Sie bitte per Scheck";}  else {echo"geben Sie Ihr Konto an";}  

Neben der else-Klausel kennt PHP auch die elseif-Klausel. Damit können mehrere Ausdrücke (Bedingungen) geprüft werden, indem in dem else-Zweig noch ein weiterer if-Befehl eingebaut wird. Sie verwenden die if-Anweisung mit elseif-Klausel folgendermaßen:

if (Bedingung1) {Anweisung1;}  elseif (Bedingung2) {Anweisung2;}  else {Anweisung3;}  

Den else-Teil können Sie auch weglassen, wenn es keine Anweisung für den Fall gibt, dass die Prüfung der Bedingungen nicht true zurückgegeben hat.

$tag=date("W");  if($tag==0 OR $tag==7)  
{echo "endlich Wochenende";  }  
elseif ($tag==5)  {echo "fast geschafft";  }  
else {echo "Arbeiten";  }  ?>  
<?php  $tag=date("w");  if($tag==0 OR $tag==7)  {echo "endlich Wochenende";  } elseif ($tag==5)  {echo "fast geschafft";  }  else {echo "Arbeiten!";  }  ?>  

Die switch-Anweisung

In vielen Fällen erweist es sich als sinnvoll, statt der if-Anweisung die Alternative switch zu benutzen. Entgegen der if-elseif-Auwertung prüft die switch-Bedingung immer nur eine Bedingung bzw. einen Ausdruck (meistens den Wert einer Variablen) und führt je nach Resultat des Vergleichs die angegebenen Befehle aus. Dabei wird nicht entweder true oder false zurückgegeben, sondern mit mehreren Fallunterscheidungen gearbeitet:

ergibt der Vergleich den Fall 1, dann Anweisung 1;

ergibt der Vergleich den Fall 2, dann Anweisung 2;

ergibt der Vergleich den Fall 3, dann Anweisung 3;

Sie verwenden die switch-Anweisung im Script folgendermaßen:

switch ($Variable)  {  case "wert1:  Anweisung1;  break;  case "wert2:  Anweisung2;  break;  case "wert3":  Anweisung3;  break;  default:  Anweisung4;  break;  }  

PHP beginnt die Auswertung am Anfang; sobald PHP den Fall (case) findet, der mit dem Wert korrespondiert, wird die Anweisung ausgeführt, und zwar tapfer so lange, bis das Ende der switch-Anweisung erreicht ist oder bis es auf break stößt. Deswegen also das break. Aus Gründen der Konsistenz schreiben wir es auch nach der default-Anweisung, wobei die default-Anweisung allerdings optional ist.

In PHP stehen folgende Kontrollstrukturen zur Verfügung:

Bedingte Ausführung und Verzweigung mit if, elseif und else

Eine oder mehrere Anweisungen können abhängig davon ausgeführt werden, ob eine Bedingung erfüllt ist.

if($_GET['page'] == 'home') {  $page = file_get_contents('home.dat');  replace_variables('home', $page);  echo $page;  }  

Der Block bedingt auszuführender Anweisungen wird durch das Schlüsselwort if eingeleitet. In Klammern wird dahinter eine Bedingung formuliert, die wahr oder falsch sein kann. Typisch dafür ist die Verwendung von Vergleichsoperatoren, da diese ein Ergebnis vom Typ „wahr“ oder „falsch“ liefern. Auch die Verknüpfung von Ausdrücken mit logischen Operatoren kommt häufig vor, da diese ebenfalls Wahr-Falsch-Werte liefern.

Daneben liefern aber auch viele PHP-Funktionen true oder false als Ergebnis zurück.

$name = "Rainer Zufall";  if(isset($name))  echo $name;  

Die PHP-Funktion isset() ermittelt, ob eine Variable existiert oder nicht. Wenn ja, wird true zurückgegeben, wenn nein, dann false. Innerhalb der Klammern von if() als Bedingung notiert, bewirkt der Funktionsaufruf, dass die bedingt auszuführenden Anweisungen dann ausgeführt werden, wenn isset() den Wert true zurückliefert.

Wenn mehrere Bedingungen abhängig von if ausgeführt werden sollen, müssen diese als Block in geschweifte Klammern eingeschlossen werden, wie im ersten der obigen Beispiele. Wenn nur eine Anweisung bedingt ausgeführt werden soll, können die geschweiften Klammern entfallen.

Mit else kann ein Alternativzweig für die Fälle definiert werden, in denen die if-Bedingung nicht erfüllt ist.

if(isset($name))  echo $name;  else  echo "Hier könnte Ihr Name stehen!";  

Auch bei else gilt: Sollen mehr als eine Anweisung abhängig davon ausgeführt werden, müssen diese in geschweifte Klammern eingeschlossen werden.

Für einfache Fallunterscheidungen können Sie auch noch ein oder mehrere Zweige des Typs elseif zwischen if und else setzen.

if($_GET['page'] == 'home') {  $page = file_get_contents('home.dat');  replace_variables('home', $page);  }  elseif($_GET['page'] == 'after_login') {  check_login();  $page = file_get_contents('logged_in.dat');  replace_variables('logged_in', $page);  } else  $page = file_get_contents('login.dat');  echo $page;  

Bei elseif muss ebenso wie bei if eine Bedingung formuliert werden, und zwar eine andere als bei if. Wenn allerdings wie im Beispiel sowohl bei der if-Bedingung als auch bei einer oder mehreren elseif-Bedingungen immer wieder abgefragt wird, ob eine bestimmte Variable einen bestimmten Wert hat, dann bieten sich auch Fallunterscheidungen an, wie nachfolgend beschrieben.

Fallunterscheidung mit switch/case

Nicht selten besteht das „Herzstück“ einer größeren PHP-Anwendung letztlich aus einer mehr oder weniger umfangreichen switch/case-Konstruktion. Denn wenn ein Script beispielsweise als Container dient, um via GET-Parameter bestimmte Seiten auszugeben, aber bei jeder Seite andere Aufgaben erfüllen muss, dann ist das ein typischer Fall für eine Fallunterscheidung. Um also beim Beispiel zu if-elseif-else zu bleiben – dies könnte auch so programmiert werden:

switch($_GET['page']) {  case 'home':  $page = file_get_contents('home.dat');  replace_variables('home', $page);  break;  case 'after_login':  check_login();  $page = file_get_contents('logged_in.dat');  replace_variables('logged_in', $page);  break;  default:  $page = file_get_contents('login.dat');  break;  }  echo $page;  

Das Konstrukt wird eingeleitet mit switch($Variablenname). Für jeden Wert, der unterschieden werden soll, wird ein case Wert: notiert. Zeichenkettenwerte werden dabei wie üblich in einfache oder doppelte hohe Anführungszeichen gesetzt, Zahlenwerte nicht. Im Anschluss daran können beliebig viele Anweisungen notiert werden, die ausgeführt werden, wenn die Fallunterscheidungsvariable diesen Wert hat. Dabei sind keine geschweiften Klammern nötig. Jedoch muss der gesamte switch-Block in geschweifte Klammern gesetzt werden.

Am Ende sollte noch an Stelle eines case ...: ein default: notiert werden. Dies entspricht einem else-Zweig und ermöglicht es, alle Fälle zu behandeln, die durch die explizit unterschiedenen Fälle nicht abgedeckt werden.

Von großer Bedeutung ist bei Fallunterscheidungen mit switch/case auch die Anweisung break;. Wird sie nicht notiert, dann werden alle nachfolgenden Fälle ebenfalls ausgeführt. In manchen Fällen kann das sogar gewünscht sein, meistens jedoch nicht. Wenn also von dem gesamten switch-Konstrukt maximal ein Fall ausgeführt wird, dann muss am Ende jedes Falls ein break; notiert werden.

Abarbeitungsschleifen mit for und foreach

Den typischen Aufbau einer for-Schleife kennen Sie bereits aus JavaSript. Das nachfolgende Beispiel zeigt, wie for-Schleifen zur Erstellung einer HTML-Tabelle genutzt werden können:

$html = '<table border="1">';  for($i = 1; $i <= 10; $i++) {  $html .= '<tr>';  for($j = 1; $j <= 10; $j++)  $html .= '<td>'.($i * $j).'</td>';  $html .= '</tr>';  }  $html .= '</table>';  echo $html;  

Das Beispiel erzeugt mittels einer verschachtelten for-Schleife eine HTML-Tabelle mit dem kleinen Einmaleins. Dabei wird die Variable $html in einer äußeren und dann noch in einer inneren for-Schleife sukzessive um entsprechende Inhalte erweitert. Am Ende genügt es, die Variable auszugeben.

Eine for-Schleife beginnt mit dem Schlüsselwort for. In den Klammern dahinter stehen typischerweise drei Anweisungen. Die erste initialisiert eine Variable mit einem Wert, die dritte verändert den Wert der Variablen und die mittlere formuliert eine Schleifendurchlaufbedingung. Die erste und die dritte Anweisung müssen so geartet sein, dass durch das Verändern der Variablen mit der dritten Anweisung irgendwann ein Wert erreicht ist, der nicht mehr die Durchlaufbedingung erfüllt. Dann ist die for-Schleife beendet.

Es hat sich unter vielen Programmierern eingebürgert,

die Zählvariable einer for-Schleife $i zu benennen. Sollte wie im Beispiel eine innere for-Schleife benötigt werden, die eine andere Variable benutzen muss, kann der nächste Buchstabe im Alphabet verwendet werden.

Im Beispiel ist for($i = 1; $i <= 10; $i++) also so zu lesen:

$i wird auf 1 gesetzt. Ist $i kleiner oder gleich 10? Ja! Also führe den Schleifenkörper aus. Zähle $i außerdem hoch. $i wird dadurch 2. Ist $i kleiner oder gleich 10? Ja! Also führe den Schleifenkörper aus. Zähle außerdem hoch usw. Irgendwann hat $i den Wert 11 und die Schleifendurchlaufbedingung ist nicht mehr erfüllt.

Den Schleifenkörper bilden diejenigen Anweisungen, die bei jedem Schleifendurchgang ausgeführt werden sollen. Bei mehr als einer Anweisung müssen die Anweisungen in geschweifte Klammern eingeschlossen werden.

Neben der klassischen for-Schleife bietet PHP auch eine foreach-Schleife an. Sie bietet sich speziell an, um alle Elemente eines Arrays zu durchlaufen (Traversion) und zwar dann, wenn man innerhalb des Schleifenkörpers keinen Zugriff auf eine Zählervariable wie $i benötigt. Nachfolgendes Beispiel zeigt einen Ausschnitt aus einer HTML-Datei:

<h1>Städte</h1>  <h2>Aachen</h2>  <p>bla</p>  <p>bla</p>  <p>bla</p>  <p>bla</p>  <h2>Augsburg</h2>  <p>bla</p>  <p>bla</p>  <p>bla</p>  <h2>Bamberg</h2>  <p>bla</p>  <p>bla</p>  <h2>Berlin</h2>  <p>bla</p>  

Wenn die Aufgabe gestellt ist, aus diesen Zeilen die in h2-Überschriften notierten Städtenamen zu extrahieren und in einer Aufzählungsliste auszugeben, so bietet sich eine foreach-Schleife an:

$lines = file("staedte.html");  echo "<ul>\n";  foreach($lines as $line) {  if(preg_match("/<h2>(.*)<\/h2>/", $line, $matches))  echo "<li>{$matches[1]}</li>\n";  }  echo "</ul>";  

Zunächst wird mit der PHP-Funktion file() die HTML-Datei eingelesen – in unserem Beispiel heißt sie staedte.html. Gespeichert wird das Ergebnis in $lines. Da file() eine Datei zeilenweise einliest, ist $lines am Ende ein Array, wobei in jedem Element eine Dateizeile steht.

In der foreach-Schleife werden alle Zeilen der Reihe nach abgearbeitet. Bei jeder Zeile wird gefragt, ob auf sie das Muster „<h2>Irgendwas</h2>“ passt. Wenn ja, steht hinterher in der mit übergebenen Variablen $matches, aus der die Suchfunktion preg_match() einen Array macht, im zweiten Element (also in $matches[1]) der im regulären Ausdruck geklammerte Inhalt, also das, was zwischen <h2> und </h2> steht. Passt eine Zeile auf das Suchmuster, dann wird ein Listenpunkt der Aufzählung ausgegeben.

Der foreach-Schleifenkopf hat folgenden typischen Aufbau:

foreach($Arrayvariable as $Elementvariable)  

Damit wird $Arrayvariable so oft durchlaufen, wie es Elemente im Array gibt. Innerhalb des Schleifenkörpers kann auf das jeweils aktuelle Element mit $Elementvariable zugegriffen werden.

Bei assoziativen Arrays ist auch folgende Variante möglich:

foreach($Arrayvariable as $Name => $Wert)  

Damit kann dann innerhalb des Schleifenkörpers sowohl auf den Schlüsselnamen als auch auf den zugehörigen Wert eines Array-Elements zugegriffen werden.

foreach($_POST as $name => $value)  echo "<b>$name:</b> $value</b><br>";  

Das Beispiel gibt alle im superglobalen Array $_POST gespeicherten Elemente mit Feldnamen und Wert aus.

Kopfschleifen mit while und Fußschleifen mit do … while

While-Schleifen benötigen im Schleifenkopf einen Ausdruck, der wahr oder falsch sein kann, genauso wie if-Bedingungen. Die Schleife wird dann so oft durchlaufen, wie der Ausdruck im Schleifenkopf wahr ist. Innerhalb des Schleifenkörpers muss dafür gesorgt werden, dass sich Zustände ändern, damit die Durchlaufbedingung im Schleifenkopf irgendwann nicht mehr erfüllt ist und die Schleife verlassen wird. Ansonsten entsteht eine Endlosschleife.

Schleifen mit while eignen sich dann, wenn nicht ermittelbar ist, wie oft die Schleife durchlaufen wird.

$now = time();  $then = $now + 5;  $count = 0;  while($now < $then) {  $file = fopen ("http://www.google.com/", "r");  $count += 1;  $now = time();  }  echo "$count mal Google geladen innerhalb 5 Sekunden";  

Mit der PHP-Funktion time() wird ein Unix-Zeitstempel erzeugt, und zwar die aktuell vergangenen Sekunden seit dem 1. Januar 1970. Der Wert wird in $now gespeichert. In $then wird ein um 5 höherer Wert gespeichert, in dem zu $now 5 hinzuaddiert wird.

Unsere while-Schleife lassen wir nun 5 Sekunden lang laufen. Erreicht wird das, indem die Schleifendurchlaufbedingung mit $now < $then formuliert wird, also „solange der Wert von $now kleiner ist als der von $then„. Innerhalb des Schleifenkörpers wird in der letzten Anweisung jedes Mal wieder $now = time() ausgeführt, d.h., jedes Mal wird $now wieder mit einem neuen Unix-Zeitstempel aktualisiert. Dadurch ist $now natürlich nach irgendeiner Anzahl von Schleifendurchläufen gleich oder größer $then, was zum Abbruch der Schleife führt.

Innerhalb der Schleife wird die Startseite von

google.com geladen. Auch hier begegnet uns übrigens wieder die „Leichtigkeit“ von PHP: mit der gleichen Funktion (fopen()), die zum Öffnen lokaler Dateien dient, lassen sich auch beliebige Webseiten einlesen. Der Programmierer muss keine umständlichen Socketverbindungen erstellen, HTTP-GET-Kommandos erzeugen oder Sonstiges. Das alles erledigt PHP für ihn.

Da das Laden übers Internet natürlich einen Moment lang dauert, wird unsere while-Schleife innerhalb von 5 Sekunden nicht allzu oft durchlaufen.

Wenn die Schleifendurchlaufbedingung schon gleich beim ersten Prüfen nicht erfüllt ist, wird der Schleifenkörper kein einziges Mal ausgeführt. Da es jedoch Fälle gibt, in denen er wenigstens einmal ausgeführt werden soll, egal ob die Schleifendurchlaufbedingung zutrifft oder nicht, gibt es auch fußgesteuerte Schleifen mit dowhile:

$Zahl = 10;  do {  echo $Zahl;  $Zahl += 1; }  while($Zahl < 10);  

In diesem Beispiel würde die Schleife, wenn die Durchlaufbedingung für while oberhalb des Schleifenkörpers notiert wäre, kein einziges Mal ausgeführt, weil $Zahl schon höher ist als die Schleifendurchlaufbedingung erlaubt. Durch das Konstrukt über dowhile wird der Schleifenkörper jedoch zuerst ausgeführt, ehe die Durchlaufbedingung geprüft wird. Die Prüfung entscheidet dann über den nächsten Schleifendurchlauf.


Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0