vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Tag 17

CGI und Perl

Heute kommen wir auf ein Thema zu sprechen, dass wir bereits an mehreren Stellen im Buch angekündigt haben: CGI und die Implementierung serverseitiger Programme zur Unterstützung und dynamischen Erzeugung von Webseiten.

Die Themen im Einzelnen:

17.1 Wie funktioniert CGI?

CGI steht für Common Gateway Interface - eine Schnittstellenspezifikation, die festlegt, wie der Server CGI-Programme aufruft, Eingaben vom Browser an das CGI-Programm weiterleitet und die Ausgabe des CGI-Programms an den Browser zurückliefert.

Was aber ist ein CGI-Programm? CGI-Programme sind ganz normale Programme, die in praktisch jeder beliebigen echten Programmiersprache geschrieben sein können. Das Einzige, was CGI-Programme von anderen Programmen unterscheidet (was sie also überhaupt erst zu CGI-Programmen macht), ist, dass sie wissen, wie der Webserver Daten vom Browser an sie weiterreicht und dass sie gegebenenfalls Ausgaben erzeugen, die der Webserver an den Browser zurückgibt.

Das CGI-Modell

Im einfachsten Fall läuft die CGI-Kommunikation wie folgt ab:

Ein Webautor möchte, dass beim Klick auf einen bestimmten Hyperlink (oder den Abschicken-Schalter eines Formulars) ein CGI-Programm aufgerufen wird, das er auf dem Webserver installiert hat. Also weist er dem href-Attribut des Hyperlinks statt des URLs eines HTML-Dokuments den URL des CGI-Programms zu.

<a href="http://webserver/cgi-bin/programm.pl">CGI-Programm</a>

Die Webseite mit diesem Hyperlink wird nun von einem Websurfer geladen und im Browser des Websurfers angezeigt. Irgendwann klickt der Websurfer auf den Hyperlink.

  1. Der Browser schickt jetzt den URL des CGI-Programms an den Webserver.
    http://webserver/cgi-bin/programm.pl
  2. Der Webserver nimmt die Anforderung entgegen, sucht auf seiner Festplatte das CGI- Programm programm.pl und ruft es auf.
  3. Das CGI-Programm erzeugt als Ausgabe eine HTML-Datei, die es an den Server zurückgibt. Dazu gibt das CGI-Programm einfach den HTML-Code der Datei Zeile für Zeile auf der Konsole aus.
  4. Der Webserver schickt die Ausgabe des Programms an den Browser.

    Abbildung 17.1:  CGI-Kommunikation

Was kann man mit CGI machen?

Mit CGI können wir:

Der Vorteil der CGI-Programme ist, dass Sie im Gegensatz beispielsweise zu JavaScript- Code auf der Serverseite ausgeführt werden, das heißt, Sie können Daten, die vom Browser kommen, auf dem Webserver abspeichern (beispielsweise Kundenbestellungen), oder umgekehrt, Daten, die auf dem Webserver verfügbar sind (beispielsweise Datenbankinhalte) ad hoc in Webseiten einbauen.

Zuvor aber muss der Webserver so eingerichtet werden, dass er die von uns geschriebenen CGI-Programme verarbeiten kann.

Einrichtung des Webservers

Als Programmiersprache für die Erstellung unserer CGI-Programme haben wir uns für Perl entschieden. Um die Beispiele dieses Tages nachvollziehen zu können, müssen Sie also einen Perl-Interpreter auf Ihrem System installieren. Später, bei der Konfiguration des Webservers für die Verarbeitung von Perl-CGI-Programmen werden wir dem Webserver mitteilen, wo er den Perl-Interpreter1 finden kann.

Perl installieren

Wenn Sie mit Unix/Linux arbeiten, müssen Sie wahrscheinlich nichts weiter tun, da Perl vermutlich bereits auf Ihrem System installiert ist. Rufen Sie einfach ein Konsolenfenster (je nach verwendetem Windowing-Manager auch Terminal genannt) auf, und tippen Sie am Prompt

perl -v 

ein. Wenn Perl auf Ihrem System installiert ist, wird Ihnen daraufhin die Kennnummer der installierten Version angezeigt. Zum Zeitpunkt der Drucklegung dieses Buches war 5.6.0 aktuell. Jede Version, deren Kennnummer mit 5 beginnt, sollte aber für den Einstieg vollkommen ausreichend sein. (Wenn Sie eine aktuellere Perl-Version installieren wollen, surfen Sie zur Website Ihres Linux-Vertreibers, zu www.activestate.com/ActivePerl oder zu www.perl.com und laden Sie sich von dort kostenfrei die aktuelle Version herunter.)

Wenn Sie mit Windows arbeiten, surfen Sie zur Website www.activestate.com/ActivePerl und laden Sie sich von dort die Binärversion (Binary Distribution) für Windows (Win32) herunter. Folgen Sie den Anleitungen zur Installation. (An sich brauchen Sie nur auf die heruntergeladene Installationsdatei doppelzuklicken. Windows 95/98-Anwender müssen aber eventuell zuvor noch instmsi.exe installieren. Lesen Sie also auf jeden Fall die Installationsanweisungen auf der ActiveState-Website.)

Webserver für CGI mit Perl konfigurieren

Die vermutlich schwierigste Aufgabe bei der CGI-Programmierung ist die Einrichtung des Webservers. Dieser muss so konfiguriert werden, dass er die Ausführung von Programmen aus bestimmten Verzeichnissen gestattet und dass er zur Ausführung von Perl-Programmen den Perl-Interpreter heranzieht.

Sofern Sie noch keinen Webserver eingerichtet haben, müssen Sie dies jetzt nachholen. In Kapitel 2 und Anhang A ist die Einrichtung und Konfiguration gängiger Webserver beschrieben (Apache, PWS, IIS, OmniHTTPd).

Danach müssen Sie den Webserver so konfigurieren, dass er die Ausführung von Perl-CGI- Programmen unterstützt.

Die in diesem Abschnitt angegebenen Pfadangaben sind größtenteils Standardwerte, die bei der Installation verändert werden können. Es ist also durchaus möglich, dass auf Ihrem Rechner abweichende Pfade und Verzeichnisse verwendet werden.

CGI-Kommunikation testen

Bevor wir prüfen, ob unser Webserver korrekt für die Verarbeitung von Perl-CGI- Programmen eingerichtet ist, sollten wir uns vergewissern, ob überhaupt der Perl- Interpreter korrekt installiert ist.

Perl-Installation testen

  1. Rufen Sie einen Texteditor auf (Notepad, vi, etc.) und tippen Sie folgenden Quelltext ein:

Listing 17.1: hallo.pl

#!/usr/bin/perl -w

print("Hallo Welt!\n");
  1. Speichern Sie die Datei als hallo.pl.
  2. Führen Sie das Skript aus. Rufen Sie dazu vom Prompt der Konsole (unter Windows ist dies die MSDOS-Eingabeaufforderung, die über Start/Programme aufgerufen wird) den Perl-Interpreter auf und übergeben Sie ihm das Perl-Skript:
    perl hallo.pl           

Danach sollte als Ausgabe

Hallo Welt! 

erscheinen.

Wenn Sie mit Windows NT arbeiten und die Dateiextension .pl mit dem Perl-Interpreter verbunden haben, können Sie die Skripte auch direkt mit ihrem Namen aufrufen und ausführen lassen. Auch unter Unix/Linux genügt der Aufruf des Skriptnamens, der zugehörige Interpreter wird dann der auskommentierten ersten Zeile des Skripts entnommen. Voraussetzung ist allerdings, dass das Skript ausführbar ist:

> chmod +x hallo.pl
> hallo.pl3

Perl/CGI-Konfiguration des Webservers testen

Testen Sie die Ausführung von Perl-CGI-Skripten auf dem Server. Setzen Sie dazu das folgende Perl-Skript auf und speichern Sie die Datei im cgi-bin-Verzeichnis Ihres Servers. (Führen Sie das Skript zur Probe einmal aus, um sich zu vergewissern, dass es syntaktisch korrekt ist.)

Listing 17.2: testecgi.pl

#!/usr/bin/perl -w

print "Content-type: text/html\n\n";

print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \n";
print " http://www.w3.org/TR/html4/strict.dtd\"> \n";
print "<html>\n";
print "<head>\n";
print "<title>CGI-Testprogramm</title>\n";
print "</head>\n";
print "<body>\n";

print "<p>Hallo vom CGI-Programm!</p>\n";

print "</body></html>\n";

Achten Sie darauf, dass die Datei vom Webserver ausgeführt werden kann.

Rufen Sie dann das Skript über Ihren Browser auf, indem Sie einfach statt dem URL einer Webseite den URL des Perl-Programms eingeben (siehe Abbildung 17.2).

Abbildung 17.2:  Ausgabe des Testskriptes im Browser

Je nachdem, wie das Dokumentenverzeichnis Ihres lokalen Webservers heißt und in welchen Verzeichnissen Sie Ihre Webseiten abgelegt haben, müssen Sie die URL-Angaben in den Skripten und Webseiten dieses Kapitels ändern.

17.2 Perl

Perl ist, wie bereits gesagt, nur eine von vielen Programmiersprachen, die man für die Erstellung von CGI-Programmen verwenden kann. Perl wird traditionell aber häufig zur CGI-Programmierung herangezogen - und zwar aus gutem Grund:

Der nachfolgende Programmierkurs soll Sie in die Programmierung mit Perl einführen und Sie mit den wichtigsten syntaktischen Elementen bekannt machen. Der Kurs ist recht stringent aufgebaut und erinnert streckenweise mehr an eine Referenz als an einen Lehrkurs. Dies hat seine Gründe. Zum einem sind Ihnen viele der hier vorgestellten Konzepte und Konstrukte bereits von JavaScript her bekannt und bedürfen daher keiner weiteren Erläuterung, zum anderen interessiert uns an Perl vor allem die Verwendung als Sprache zur CGI-Programmierung und so wollen wir den Syntax-Teil möglichst schnell abschließen, um mehr Zeit für die CGI-Programmierung zu haben. Schließlich kommt noch hinzu, dass Perl eine so umfangreiche Sprache mit so vielen Facetten ist, dass man ihr unmöglich im Rahmen dieses Buches gerecht werden könnte. Wer mehr über Perl erfahren möchte, der sei an entsprechende Fachbücher wie »Jetzt lerne ich Perl« oder »Programming Perl« verwiesen (siehe Anhang E).

Ein erstes Perl-Programm

Unser erstes Perl-Programm haben wir bereits in Abschnitt 17.1.3, bei der Kontrolle der Perl-Installation aufgesetzt.

Listing 17.3: hallo.pl

#!/usr/bin/perl -w

print("Hallo Welt!\n");

Die erste Zeile in diesem Programm ist ein Kommentar.

Kommentare

Kommentare beginnen mit einem #-Zeichen und reichen bis zum Ende der Zeile. Sie werden vom Perl-Interpreter ignoriert - sie dienen lediglich zur Dokumentation des Quelltextes.

Der Shebang-Kommentar

Der Kommentar in der ersten Zeile erfüllt allerdings unter UNIX/Linux einen besonderen Zweck: er teilt UNIX/Linux mit, mit welchem Interpreter das Skript auszuführen ist; wobei auch - wie man sieht - Kommandozeilenargumente an den Interpreter übergeben werden können (-w). Wenn Sie also unter UNIX/Linux arbeiten und Ihr Perl-Interpreter in einem anderen Verzeichnis als /usr/bin/ steht, ändern Sie die erste Zeile entsprechend ab. Webserver der Microsoft Windows-Plattform ignorieren meist die Pfadangabe und werten nur die Optionen für den Interpreter aus.

Die Amerikaner bezeichnen die anfängliche Kommentarzeile als Shebang, da sie mit einem # (im Amerikanischen als sharp bezeichnet) und einem ! (dem bang) beginnt.

Unter dem Kommentar folgt eine Leerzeile, die der besseren Lesbarkeit des Skripts dient.

Der eigentliche Code des Skripts steht in der dritten Zeile. Diese Zeile enthält eine Anweisung, was an dem abschließenden Semikolon zu erkennen ist. Die Anweisung ruft die Funktion print auf.

Ausgaben

print ist der Name einer Funktion, die in den Perl-Bibliotheken vordefiniert ist und die dazu dient, Text auszugeben. Der auszugebende Text folgt direkt hinter dem Namen der Funktion und ist in doppelte Anführungszeichen gefasst, woran der Perl-Interpreter erkennt, dass es sich um Text handelt, den das Programm (und nicht der Interpreter) verarbeitet. Die Anweisung aus der dritten Zeile besteht also aus dem Aufruf der Funktion print, der als auszugebender Text der String "Hallo Welt!\n" übergeben wird.

In JavaScript müssen die Argumente, die man einer Funktion beim Aufruf übergibt, immer in runden Klammern auf den Funktionsnamen folgen. In Perl können Sie sich die runden Klammern sparen, wenn auf den Funktionsnamen nur noch Argumente für die Funktion folgen (siehe beispielsweise Listing 17.2).

Variablen und Konstanten

Perl kennt - im Gegensatz zu JavaScript - verschiedene Variablentypen: Skalare, Listen, Hashes, Referenzen und Datei-Handles. Konzentrieren wir uns zuerst auf die Skalare.

Skalare Variablen definieren

Skalare Variablennamen beginnen in Perl immer mit einem $. Auf das Präfix folgt der eigentliche Variablenname. Dieser Variablenname darf aus Buchstaben, Ziffern und Unterstrichen bestehen, darf aber nicht mit einer Ziffer anfangen. Des Weiteren muss er eindeutig sein (wobei Perl zwischen Groß- und Kleinschreibung unterscheidet).

Gültige Variablennamen wären also

$meineVariable
$meine_variable
$_meineVariable
$meine3teVariable

Nicht gültig wären

$3teVariable           # beginnt mit einer Ziffer
$meine%Variable # enthält ungültiges Zeichen %

Wie in JavaScript brauchen Variablen nicht vorab deklariert zu werden.

#!/usr/bin/perl -w

$zahl1 = 100;
$zahl2 = 12;

print $zahl1 + $zahl2; # Ausgabe: 112

Diese Verfahrensweise ist allerdings recht fehleranfällig. Vertippt man sich bei einem Variablennamen (schreibt man in obigem Code beispielsweise print $zahl1 + $zakl2;), erzeugt der Perl-Interpreter unbemerkt eine neue Variable $zakl2 mit undefiniertem Wert und rechnet mit dieser weiter - was natürlich zu falschen Ergebnissen führt. Um dies zu vermeiden, sollten Sie Ihre Perl-Programme so aufsetzen, dass der Interpreter nur vorab definierte Variablennamen akzeptiert.

  1. Aktivieren Sie das Perl-Pragma use strict;
  2. Deklarieren Sie alle Variablen mit dem Schlüsselwort my

Listing 17.4: variablen.pl

#!/usr/bin/perl -w

use strict;

my $zahl1 = 100;
my $zahl2 = 12;

print $zahl1 + $zahl2;

Konstanten

Zahlenkonstanten können in Perl auf verschiedene Weise angegeben werden:

100             # als Ganzzahl
333.33 # als Gleitkommazahl
3.1e2 # in Exponentialschreibweise
0x12E # als Hexadezimalzahl

Na, das erinnert doch sehr an JavaScript. Auch die String-Konstanten scheinen auf den ersten Blick der von Javascript gewohnten Syntax zu folgen:

"Ich bin ein String"
'Ich bin auch ein String'

Allerdings gibt es in Perl einen wichtigen Unterschied zwischen Strings in einfachen oder doppelten Anführungszeichen.

In Perl kann man nämlich Variablennamen direkt in Strings einbauen:

"100 + 12 = $summe"

Steht der String in doppelten Anführungszeichen ersetzt der Perl-Interpreter den Variablennamen durch den Wert der Variablen. Steht der in einfachen Anführungszeichen, gibt der Interpreter den Variablennamen wie eine ganz normale Zeichenfolge aus.

Listing 17.5: konstanten.pl

#!/usr/bin/perl -w
use strict;

my $summe = 100.33 + 12;

print "100.33 + 12 = $summe";

In Strings mit doppelten Anführungszeichen können Sie auch Sonderzeichen - wie \n (Zeilenumbruch), \t (Tabulator), \" (doppeltes Anführungszeichen) und andere - einbauen.

Eine weitere Spezialität von Perl sind die HERE-Texte.

HERE-Texte

Dabei wird der auszugebende Text, der ruhig mehrere Zeilen umfassen kann (und sollte) zwischen zwei frei wählbaren Bezeichnern definiert und mit Hilfe des <<-Operators in einer Variablen gespeichert

Listing 17.6: here.pl

#!/usr/bin/perl -w

$gedicht = <<HERE_DOUGLAS;
Archibald Douglas

von
Theodor Fontane

Ich habe es getragen sieben Jahr,
Und ich kann es nicht tragen mehr!
Wo immer die Welt am schönsten war,
Da war sie öd und leer.
...

HERE_DOUGLAS

print $gedicht;

Später, wenn wir mit Perl-CGI-Programmen dynamisch Webseiten erzeugen, wird uns diese Technik sehr zugute kommen!

Aus- und Eingabe

Wie man mit Hilfe der print-Funktion Strings und Werte von Variablen ausgibt, konnten Sie bereits in den vorangehenden Beispielen sehen. Als Ergänzung zu dieser Funktion kennt Perl noch eine weitere Ausgabefunktion, printf, mit der man das Ausgabeformat für in den String eingebaute Variableninhalte bestimmen kann.

Formatierte Ausgabe mit printf

Betrachten wir folgenden Code:

#!/usr/bin/perl -w
use strict;

my $zahl1 = 100;
my $zahl2 = 12;
my $ergebnis;


$ergebnis = $zahl1 / $zahl2;

print "$zahl1 / $zahl2 = $ergebnis ";

Wenn Sie dieses Programm ausführen, erhalten Sie folgende Ausgabe:

100 / 12 = 8.33333333333333

Die vielen Nachkommastellen in dieser Ausgabe sind etwas unschön. Mit Hilfe von printf können wir festlegen, dass nicht mehr als, sagen wir, 3 Nachkommastellen ausgegeben werden.

Der Trick von printf ist, dass man statt der Variablennamen Platzhalter in den String einfügt (beispielsweise %s für einen String, %d für eine Ganzzahl, %f für eine Gleitkommazahl). Im Gegensatz zu den Variablennamen kann man für die Platzhalter angeben, wie die Werte, die sie repräsentieren, formatiert werden sollen.

Formatierung

Effekt

Beispiel

+

Zahlen immer mit Vorzeichen

"%+d"

-

Linksbündige Ausrichtung

"%-d"

b

Angabe der Feldbreite der Ausgabe

"%5d"

.n

Angabe der Nachkommstellen für Gleitkommazahlen

"%.3f"

Tabelle 17.1: Ausgesuchte Formatflags für printf

Die Variablen, deren Werte an der Stelle der Platzhalter ausgegeben werden sollen, werden im Anschluss an den auszugebenden String aufgeführt (in der Reihenfolge, in der die Werte den Platzhaltern zugeteilt werden sollen).

Listing 17.7: printf.pl

#!/usr/bin/perl -w
use strict;

my $zahl1 = 100;
my $zahl2 = 12;
my $ergebnis;


$ergebnis = $zahl1 / $zahl2;

printf "%d / %d = %.3f ", $zahl1, $zahl2, $ergebnis;

Umlaute auf der Windows-Konsole

Wenn Sie Text mit deutschen Umlauten auf die Windows-Konsole (MSDOS-Eingabeaufforderung) ausgeben, sehen Sie statt der Umlaute nur grafische Sonderzeichen. Dies liegt daran, dass die Windows-Konsole einen anderen Zeichensatz (OEM-Zeichensatz) verwendet als Windows. Sie können diese Manko beheben, indem Sie statt der Zeichen den ASCII-Code angeben:

print "Umlaute \x84 \x94 \x81 \n";

Daten über die Konsole einlesen

Mit Perl kann man Daten über die Tastatur einlesen. Für CGI-Programme ist diese Technik zwar uninteressant (weil es keinen Anwender gibt, der die Daten eintippen könnte), aber sie ermöglicht es uns, die Beispielprogramme in diesem Abschnitt etwas interessanter zu gestalten.

In Perl-Programmen wird die Tastatur durch das vordefinierte Handle STDIN repräsentiert. In Verbindung mit dem Einleseoperator <> kann man über diesen Handle Daten von der Tastatur einlesen.

my $meineVar;
$meineVar = <STDIN>;
chomp($meineVar);

Zuerst definiert man eine Variable (hier $meineVar).

Im nächsten Schritt werden die Daten eingelesen. Dazu wendet man auf den Datei- Handle den Einleseoperator <> an, was dann wie folgt aussieht: <STDIN>. Der Einleseoperator liest so lange Daten aus der Datenquelle, bis er auf ein bestimmtes Trennzeichen trifft. Per Voreinstellung ist dieses Trennzeichen das Neue-Zeile-Zeichen. Die Konstruktion <STDIN> liest also so lange Zeichen über die Tastatur ein, bis der Anwender die (Enter) -Taste drückt. Die eingelesenen Daten speichern wir in der Variablen $meineVar.

Dabei ist zu beachten, dass das Neue-Zeile-Zeichen mit abgespeichert wird. Da das Neue- Zeile-Zeichen allerdings meist unerwünscht ist (es gehört ja im Grunde nicht zur Eingabe, sondern diente lediglich zum Abschicken der Eingabe), ist es Usus, das Neue-Zeile- Zeichen gleich aus der Eingabe zu entfernen. Dies erledigt die vordefinierte Funktion chomp, der die Variable mit der Eingabe übergeben wird.

Statt $meineVar = <STDIN>; chomp($meineVar); schreiben erfahrene Perl-Programmierer meist verkürzt: chomp($meineVar = <STDIN>);

Listing 17.8: eingabe.pl

#!/usr/bin/perl -w
use strict;

my $name;
print("Wie heissen Sie? ");

$name = <STDIN>;
chomp($name);

print("Hallo $name!\n");

Operatoren

Wie jede anständige Programmiersprache verfügt auch Perl über eine Reihe von Operatoren zur Manipulation elementarer Daten.

Operatoren für Zahlen

Neben dem Zuweisungsoperator stehen die typischen arithmetischen Operatoren zur Verfügung.

Operator

Bedeutung

Verwendung

op1 + op2

Addition

$var = 10 + 4; # $var = 14

op1 - op2

Subtraktion

$var = 10 - 4; # $var = 6

op1 * op2

Multiplikation

$var = 10 * 4; # $var = 40

op1 / op2

Division

$var = 10 / 4; # $var = 2.5

Tabelle 17.2: Die Grundrechenarten

Hinzukommen die Operatoren für Exponent und Modulo.

Operator

Bedeutung

Verwendung

op1 ** op2

Exponent

$var = 10 ** 2; # $var = 100

Berechnet op1 hoch op2.

op1 % op2

Modulo

$var = 10 % 3; # $var = 1

Teilt op1 durch op1 und gibt den dabei verbleibenden Rest als Ergebnis zurück.

op1 und op2 müssen Ganzzahlen sein!

Tabelle 17.3: Exponent und Modulo

sowie Inkrement, Dekrement und die kombinierten Zuweisungen.

Operator

Bedeutung

Entspricht

++op1

Inkrement

op1 = op1 + 1

--op1

Dekrement

op1 = op1 - 1

op1 += op2

Addition

op1 = op1 + op2

op1 -= op2

Subtraktion

op1 = op1 - op2

op1 *= op2

Multiplikation

op1 = op1 * op2

op1 /= op2

Division

op1 = op1 / op2

op1 %= op2

Modulo

op1 = op1 % op2

op1 **= op2

Exponent

op1 = op1 ** op2

Tabelle 17.4: Die kombinierten Zuweisungsoperatoren

Haben wir irgend etwas vergessen? Ach ja, die Operatoren zum Vergleichen von Zahlen:

Operator

Bedeutung

Verwendung

==

Gleichheit

$var1 == $var2

!=

Ungleichheit

$var1 != $var2

<

kleiner

$var1 < $var2

>

größer

$var1 > $var2

<=

kleiner gleich

$var1 <= $var2

>=

größer gleich

$var1 >= $var2

Tabelle 17.5: Die Vergleichsoperatoren für Zahlen

Operatoren für Strings

Im Gegensatz zu anderen Programmiersprachen kennt Perl eine Reihe von speziellen Operatoren zur Verarbeitung von Strings.

Neben dem Zuweisungsoperator (=) und dem Umleitungsoperator << (siehe HERE- Texte) erlaubt Perl

Operator

Bedeutung

Verwendung

eq

Gleichheit

$str1 eq $str2

ne

Ungleichheit

$str1 ne $str2

lt

kleiner

$str1 lt $str2

gt

größer

$str1 gt $str2

le

kleiner gleich

$str1 le $str2

ge

größer gleich

$str1 ge $str2

Tabelle 17.6: Die Vergleichsoperatoren für Strings

Kontrollstrukturen

Wie in fast allen strukturierten Programmiersprachen gibt es auch in Perl if-else- Verzweigung und verschiedene Schleifenkonstruktionen.

Die if-Bedingung

Listing 17.9: if.pl

#!/usr/bin/perl -w
use strict;

my $eingabe = 0;

print "\nWie alt sind Sie? ";
chomp ($eingabe = <STDIN>);

if ($eingabe >= 10)
{
print "Sorry, dieses Programm ist nur für Kinder!";
}
else
{
print "Hallo, freut mich Dich kennenzulernen!";
}

Mit Hilfe der logischen Operatoren !, &&, || und ^ kann man Vergleiche miteinander kombinieren.

Die Schleifen

Auch die for-Schleife ist nach dem typischen Muster aufgebaut:

# Ausgabe der ersten 10 Potenzen von 2

my $i;
for ($i = 0; $i <= 10; ++$i)
{
print "2 hoch $i ist \t", 2**$i, "\n";
}

Als while-Schleife formuliert, sähe der obige Code wie folgt aus:

my $i = 0;                                   
while ($i <= 10)
{
print "2 hoch $i ist \t", 2**$i, "\n";
++$i;
}

Perl kennt auch eine Mehrfachverzweigung (switch-Konstruktion) und noch weitere Schleifenvarianten, auf die wir hier aber nicht weiter eingehen.

Arrays

Als Arrays bezeichnet man gemeinhin Datenstrukturen, in denen man mehrere gleichartige und zusammengehörende Daten (beispielsweise Messwerte) abspeichern und verwalten kann. In Perl stellen Arrays einen eigenen Variablentyp dar.

Arrays definieren

Arrays werden in Perl mit dem Präfix @ definiert:

my @meinArray;

Wenn man will, kann man dem Array auch gleich bei der Definition die ersten Werte zuweisen:

my @meinArray = (1, 2, 3, 4, 5);

oder - einfacher noch - einen Wertebereich zuweisen:

my @meinArray = (1..5);  # Array mit 5 Elementen, in denen die Werte
# 1, 2, 3, 4 und 5 gespeichert sind

Auf die einzelnen Elemente im Array kann man durch Angabe des zugehörigen Index (der standardmäßig mit 0 anfängt) zugreifen:

my $var;
$var = $meinArray[0]; # Wert des 1. Elements nach $var kopieren

Ooops, da hat sich wohl ein Tippfehler eingeschlichen! Vor meinArray steht statt @ das Präfix $. Nein, das Präfix $ ist an dieser Stelle kein Fehler, denn es steht nicht vor einem Array (meinArray), sondern vor einem Element eines Arrays (meinArray[0]) und dieses Element ist ein... Skalar.

Arrays dynamisch erweitern

Wie in JavaScript sind Arrays in Perl dynamische Strukturen, das heißt, wir können jederzeit neue Elemente in ein Array einfügen oder bestehende Elemente löschen. Perl stellt uns dafür folgende vier Funktionen zur Verfügung.

Funktion

Beschreibung

push

Hängt eines oder mehrere Elemente an das Ende eines Arrays an.

@array = (1, 2, 3); 
push(@array, 4); # (1, 2, 3, 4)
@array = (1, 2, 3);
push(@array, 4..5); # (1, 2, 3, 4, 5)
@demo = (4, 5);
@array = (1, 2, 3);
push(@array, @demo); # (1, 2, 3, 4, 5)

unshift

Hängt eines oder mehrere Elemente an den Anfang eines Arrays an.

@array = (1, 2, 3); 
unshift(@array, 4..5); # (4, 5, 1, 2, 3)

pop

Entfernt das letzte Element eines Arrays und liefert es zurück.

@array = (1, 2, 3); 
$elem = pop @array; # (1, 2)
print $elem; # Ausgabe: 3

shift

Entfernt das erste Element eines Arrays und liefert es zurück.

@array = (1, 2, 3); 
$elem = shift @array; # (2, 3)
print $elem; # Ausgabe: 1

Tabelle 17.7: Array-Elemente hinzufügen oder löschen

Arrays durchlaufen

Mit Hilfe des indizierten Zugriffs, einer for-Schleife und der Anzahl der Elemente im Array ist es kein Problem, ein Array Element für Element zu durchlaufen und zu bearbeiten.

Listing 17.10: Auszug aus arrays.pl

my @meinArray = (0..20);
my $i;

for($i = 0; $i < @meinArray; ++$i) {
print $meinArray[$i], " ";
}

Wenn Sie eine Array-Variable in einem Umfeld verwenden, wo Perl einen Skalar erwartet (wie zum Beispiel in dem Vergleich $i < @meinArray) ersetzt der Perl-Interpreter die Array- Variable durch die Anzahl der Elemente im Array.

Einfacher und sicherer durchläuft man Arrays aber mit der foreach-Schleife.

my $elem;
foreach $elem (@meinArray) {
print $elem, " ";
}

Hashes

Ein Hash ist eine weitere Datenstruktur, die insbesondere in Kombination mit Arrays und Referenzen sehr effizient zur Verwaltung und Verarbeitung großer, strukturierter Datenmengen eingesetzt werden kann. Entsprechende Implementierungen werden aber auch schnell recht kompliziert, weswegen wir hier nicht näher darauf eingehen werden. Trotzdem sollte man zumindest wissen, wie Hashes definiert werden und wie man auf in Hashes abgelegte Elemente zugreifen kann.

Stellen Sie sich ein Hash einfach als ein Array vor, auf dessen Elemente man nicht über einen Index in eckigen Klammern, sondern über einen Namen in geschweiften Klammern zugreift.

Wenn man ein neues Hash anlegt, gibt man die einzelnen Elemente als Name/Wert-Paare an - also immer zuerst den Namen, den man später als Index zum Zugreifen auf das Element verwendet, und dann den eigentlichen Inhalt des Elements:

%adresse = ("Vorname"    => "Sven", 
"Nachname" => "Olsen",
"Straße" => "Fliederstrasse",
"Hausnummer" => 23,
"PLZ" => 81830,
"Stadt" => "München");

Wie Sie sehen, haben auch Hash-Variablen ihr eigenes Präfix: %.

Wenn Sie auf den Wert eines Elements in einem Hash zugreifen wollen, geben Sie den Namen des Elements als Index in geschweiften Klammern an:

$person{Vorname} = "Ole";       # Wert ändern/setzen
print $person{Vorname}; # Wert abfragen

Funktionen

Größere Programme haben die Eigenschaft, dass ihr Code schnell sehr unübersichtlich wird. Man kann dem entgegenwirken, indem man Teilprobleme identifiziert und diese in Form von Funktionen löst. Funktionen haben überdies den Vorteil, dass man sie nach erfolgter Definition an beliebiger Stelle aufrufen kann (siehe Kapitel 9.4).

Funktionen definieren

In Perl werden Funktionsdefinitionen mit dem Schlüsselwort sub eingeleitet. Dann folgt der Funktionsname und in geschweiften Klammern der Anweisungsblock zu der Funktion.

sub funktionsname {
# Anweisungen
}

Parameter und Rückgabewert

Perl hat einen recht eigenwilligen Mechanismus zur Übergabe von Argumenten an Funktionen. Es legt alle Argumente in einem Array namens @_ ab. Dieses Array müssen Sie nicht explizit definieren, es ist automatisch in jedem Perl-Programm verfügbar. In der Funktion können Sie dann die einzelnen Argumente über ihren Index $_[0], $_[1] zugreifen.

Beachten Sie, dass Sie über die Elemente $_[n] direkt auf die Variablen zugreifen, die beim Aufruf als Argumente übergegeben wurden (die Funktion kann die Variablen aus dem Aufruf ändern). Wenn Sie dies verhindern wollen, weisen Sie die Werte aus dem Array @_ an lokale Variablen der Funktion zu.

sub funktionsname {
my param1 = $_[0];
my param2 = $_[1];

# weitere Anweisungen
}

Aufruf:

funktionsname($var1, 312);

Soll die Funktion einen Rückgabewert zurückliefern, übergeben Sie diesen zum Schluss der return-Anweisung:

return $ergebnis;

Das folgende Programm nutzt beispielsweise eine Funktion namens berechne_zins, um auszurechnen, wie sich ein einmalig eingesetzter Betrag bei fester Verzinsung (mit Zinseszins) vermehrt.

Listing 17.11: funktionen.pl

#!/usr/bin/perl 
use strict;
# Funktion zur Berechnung des Endkapitals
sub berechne_zins
{
my $kp = $_[0]; # Startkapital
my $zs = $_[1]; # Zinssatz
my $lz = $_[2]; # Laufzeit

return $kp * ( 1 + $zs/100.0) ** $lz;
}


my $kapital;
my $zsatz;


# Startkapital und Zinssatz von Anwender abfragen

print "\n Programm zur Zinsberechnung\n\n";

print " Wie hoch ist Ihr Startkapital: ";
chomp($kapital = <>);

print " Wie viel Zinsen bekommen Sie (in Prozent): ";
chomp($zsatz = <>);


# Kapitalentwicklung für die nächsten 20 Jahre berechnen

my $laufzeit;
my $endkapital;

for ($laufzeit = 1; $laufzeit <= 20; ++$laufzeit)
{
$endkapital = berechne_zins($kapital, $zsatz, $laufzeit);
printf("Endkapital nach %d Jahren: %.2f DM\n", $laufzeit,
$endkapital);
}

Dateien

Mindestens ebenso wichtig wie die Ein- und Ausgabe über die Konsole ist das Verarbeiten von Daten aus Dateien. Zur Programmierung mit Dateien gehört, dass man weiß, wie man Dateien öffnet, wie man Daten aus Dateien einlesen kann, wie man Daten in Dateien abspeichert und wie man die Dateien zum Schluss wieder schließt.

Das Schreiben einer Datei erfolgt in drei Schritten:

  1. Datei öffnen und mit Datei-Handle verbinden.

Wenn die zu öffnende Datei nicht im gleichen Verzeichnis wie das Perl-Skript steht, müssen Sie im Dateinamen auch den Pfad zur Datei angeben. Windows-Anwender, die es gewohnt sind, Verzeichnisse mit einem Backslash \ zu trennen, müssen beachten, dass der Backslash in doppelten Anführungszeichen ein Sonderzeichen ist. Um einen Backslash in einen Dateinamen einzubauen, muss man daher zwei Backslashes hintereinander schreiben: "C:\\datei.txt" (oder einfache Anführungszeichen verwenden). Die meisten Windows-Versionen erlauben aber auch die Verwendung eines einfachen Slashes, "C:/datei.txt", wie es auch unter UNIX/Linux üblich ist.

Soweit ist alles ganz einfach. Etwas störend ist nur die Zeichenfolge >> vor dem Dateinamen (bzw. der Variablen, die den Dateinamen enthält). Diese Zeichenfolge gibt an, zu welchem Zweck die Datei geöffnet werden soll.

Öffnen-Modus

Beschreibung

open(FH, "datei");

open(FH, "< datei");

Öffnet die Datei zum Lesen.

Ist die Datei nicht vorhanden, scheitert die Funktion.

open(FH, "> datei");

Öffnet die Datei zum Schreiben.

Ist die Datei nicht vorhanden, wird sie neu angelegt. Ist die Datei bereits vorhanden, wird ihr alter Inhalt gelöscht.

open(FH, ">> datei");

Öffnet die Datei zum Anhängen.

Ist die Datei nicht vorhanden, wird sie neu angelegt. Ist die Datei bereits vorhanden, wird der neue Inhalt an den alten Inhalt angehängt.

open(FH, "+< datei");

Öffnet die Datei zum Lesen und Schreiben.

(Erfordert meist fortgeschrittene Funktionen zur Dateibehandlung).

Tabelle 17.8: Öffnen-Modi

Fehlerbehandlung für Dateien

Zum Schluss müssen wir noch klären, was geschehen soll, wenn die open-Funktion scheitert und die Datei nicht mit dem Datei-Handle verbunden wird. Wenn das Programm ohne die Datei nicht sinnvoll arbeiten kann, empfiehlt es sich den open-Aufruf an den Anfang des Skripts zu stellen und mit einem die-Aufruf zu kombinieren:

open(DATEI, ">> $dateiname") 
or
die "\nDatei $dateiname konnte nicht geoeffnet werden\n";

Diese Konstruktion sieht man in Perl-Skripten, die mit Dateien arbeiten, sehr häufig. Sie funktioniert wie folgt.

¯ Wenn open die Datei öffnen konnte, liefert sie einen Wert zurück, der dem Wahrheitswert »wahr« entspricht. Da eine OR-Konstruktion bereits »wahr« ist, wenn nur einer der Teilausdrücke »wahr« ist, wird der nachfolgende Ausdruck mit die überhaupt nicht mehr ausgewertet (die wird nicht aufgerufen) und das Skript wird normal fortgesetzt.

¯ Wenn open scheitert, liefert open den Wahrheitswert »false«, so dass der Interpreter auch noch den zweiten Ausdruck der OR-Verknüpfung auswertet. Er ruft also die Funktion die auf. Die Funktion die gibt noch den als Argument übergebenen String aus und beendet dann sofort das Programm.

  1. Daten in Datei schreiben.

Wenn Sie die Argumente zu einer Dateifunktionen nicht in Klammern einfassen, wird der Datei-Handle nicht durch Komma von den anderen Argumenten getrennt!

  1. Zum Schluss wird die Datei geschlossen.
    close(DATEI);

Listing 17.12: schreiben.pl

#!/usr/bin/perl -w
use strict;

my $dateiname = "hallo.html"; # Name der zu öffnenden Datei

# Datei öffnen und mit Datei-Handle DATEI verbinden
open(DATEI, "> $dateiname")
or
die "\nDatei $dateiname konnte nicht geoeffnet werden\n";

# Anwendername von Konsole einlesen
my $name = "";
print "\n";
print "Geben Sie Ihren Vornamen ein: ";
chomp($name = <STDIN>);

# Daten in Datei schreiben
my $htmlcode = <<HERE_HTML;

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Hallo</title>
</head>

<body>
<h1>Hallo $name !</h1>
</body>
</html>
HERE_HTML

print DATEI $htmlcode;


# Datei schliessen
close(DATEI);

Lesen einer Datei

Das Lesen einer Datei erfolgt in drei Schritten:

  1. Datei öffnen und mit Datei-Handle verbinden.
  1. Daten aus Datei lesen.
  1. Zum Schluss wird die Datei geschlossen.
    close(DATEI);

Listing 17.13: lesen.pl

#!/usr/bin/perl -w
use strict;

my $dateiname = "hallo.html"; # Name der zu öffnenden Datei

# Datei öffnen und mit Datei-Handle DATEI verbinden
open(DATEI, "<$dateiname")
or
die "\nDatei $dateiname konnte nicht geoeffnet werden\n";

# Daten aus Datei einlesen
while(my $zeile = <DATEI>)
{
print $zeile;
}


# Datei schliessen
close(DATEI);

Auf Module zurückgreifen

Zu Perl gehört eine umfangreiche Sammlung von Modulen, die jeder Perl-Programmierer in seinen Programmen frei verwenden kann - das sogenannte CPAN. Jedes der CPAN- Module ist einem bestimmten Themengebiet gewidmet und enthält Funktionen (manchmal auch Objekte) zur Lösung typischer Aufgaben aus dem entsprechenden Themengebiet. So gibt es beispielsweise Module mit zusätzlichen mathematischen Funktionen (Math), Module zur Programmierung von Windows-Anwendungen mit Perl (Tk), Grafikbibliotheken (GD, Chart) oder auch zur Erstellung von CGI-Programmen (CGI).

Module einbinden

Um Elemente eines bestimmten Perl-Moduls in einem Skript verwenden zu können, müssen Sie das Modul mit Hilfe des use-Befehls einbinden:

use Tk;

oder

use Text::Wrap;

falls sich das Modul in einem Unterverzeichnis befindet.

Wie Sie danach auf die Elemente des Moduls zugreifen können, hängt davon ab, wie das Modul implementiert ist und wie es seine Elemente exportiert.

Was aber wenn man feststellt, dass das betreffende Modul gar nicht installiert ist?

Module herunterladen und installieren

Grundsätzlich gehört zu jeder Perl-Installation eine mehr oder weniger umfangreiche Auswahl an mitinstallierten CPAN-Modulen. Dann brauchen Sie die Module, die Sie nutzen wollen, nur einzubinden und die darin enthaltende Funktionalität zu nutzen.

Wenn Sie jedoch versuchen, ein Modul zu verwenden, das auf Ihrem System nicht installiert ist, ernten Sie nur Fehlermeldungen. Dann ist es an der Zeit, sich ins Internet unter http://www.perl.com einzuloggen und sich im CPAN nach dem betreffenden Modul umzuschauen. Einen guten Überblick über das CPAN und die darin enthaltenen Module erhalten Sie unter http://www.perl.com/CPAN-local/README.html. Auf dieser Webseite finden Sie auch Informationen über die Installation der CPAN-Module.

Wenn Sie mit einer ActiveState-Version arbeiten (egal ob unter Windows oder Linux), können Sie neue CPAN-Module auch mit Hilfe des Programms ppm nachinstallieren.

  1. Stellen Sie über Modem oder ISDN-Karte eine Verbindung zum Internet her.
  2. Rufen Sie ppm über die Konsole auf (unter Windows die MSDOS- Eingabeaufforderung). Es erscheint ein eigener ppm-Prompt.
  3. Lassen Sie sich eine Liste der verfügbaren Module anzeigen. (Eventuell sollten Sie zuvor einstellen, nach wie vielen Zeilen die Ausgabe der Liste unterbrochen werden soll.
    ppm> set more 20
    ppm> search
  4. Wenn Sie das gewünschte Modul (ActiveState spricht von Packages) gefunden haben, lassen Sie es installieren.
    ppm> install PackageName
  5. Beenden Sie ppm nach erfolgreicher Dateiübertragung.
    ppm > quit

Weitere Informationen zu ppm finden Sie in der Online-Dokumentation auf der ActiveState-Website (www.activestate.com).

Was wir ausgelassen haben

Perl ist einerseits recht einfach zu erlernen, andererseits von überwältigender Komplexität. Dies liegt nicht nur an den vielen zu Perl angebotenen CPAN-Modulen, sondern auch

Sie sehen: es gäbe noch viel über Perl zu lernen. Wichtige Themen, die wir nicht angesprochen haben, weil Sie für die nachfolgenden CGI-Beispiele nicht relevant sind, wären beispielsweise:

Auch haben wir nur andeuten können, warum Perl eigentlich Perl heißt. Perl ist nämlich ein Akronym für »Practical Extraction and Reporting Language«, das heißt, die Sprache empfiehlt sich als besonders geeignet zur Verarbeitung von Texten und zur Erstellung von Berichten. Wer Perl in diesem Gebiet einsetzt (das sich auch mit den Aufgaben von CGI- Programmen überschneidet), profitiert unter anderem von

17.3 Webseiten dynamisch erzeugen

Nachdem wir uns im vorangehenden Abschnitt ein wenig mit der Perl-Syntax vertraut gemacht haben, wollen wir nun den nächsten Schritt wagen und mit der CGI- Programmierung mit Perl beginnen.

Aus Sicht eines Programms besteht die CGI-Spezifikation aus zwei wichtigen Teilen:

Wir beginnen mit dem zweiten Teil: dem Zurücksenden von Daten an den Browser

Daten via CGI zurücksenden

CGI-Programme können jedwede Art von Daten zurückzuliefern, die von den Browsern verarbeitet werden kann: HTML-Dokumente, Grafiken, Sounddateien, etc. Wichtig ist,

Die erste Bedingung ist nicht schwer zu erfüllen, besagt sie schließlich nichts anderes, als dass wir die Daten mit print oder printf ausgeben sollen - ganz so als würden wir auf die Konsole schreiben (nur dass in diesem Fall der Server die Daten entgegen nimmt und an den Browser leitet).

Auch das richtige Format ist leicht gefunden. Solange wir uns darauf beschränken, Webseiten als Ergebnis der CGI-Programme zurückzuliefern, ist das richtige Format einfach der HTML-Code der Webseite.

Neu für uns ist, dass wir den HTTP-Header mitliefern müssen. Diese Aufgabe übernimmt nämlich ansonsten der Webserver. Jetzt müssen wir uns selbst darum kümmern.

HTTP-Header

Die Übertragung von Webseiten über das Internet (oder ein lokales Netz) erfolgt nach den Vorgaben des HTTP-Protokolls (HTTP steht für Hyper Text Transfer Protocol). Dieses Protokoll sieht vor, dass sämtliche übertragenen Dateien von speziellen (teils erforderlichen, teils optionalen) Headern begleitet werden. Diese Header kann man in vier Kategorien aufteilen:

¯ Allgemeine Header: enthalten unspezifische Daten (beispielsweise eine Datumsangabe)

¯ Anforderungs-Header: werden vom Browser an den Server geschickt (beispielsweise der Header User-Agent, über den sich der Browser beim Server ausweisen kann)

¯ Antwort-Header: werden vom Server an den Browser geschickt

¯ Entity-Header: beziehen sich auf die übertragenen Daten

Verschiedene HTTP-Header sind auch für die CGI-Programmierung interessant. Erwähnen möchten wir hier aber nur zwei: den Location-Header und den Content-type- Header. Einen dieser beiden Header muss das CGI-Programm zurückliefern.

Location: http://www.andererserver.com

Schicken Sie diesen Header, wenn Sie keine Webseite zurückliefern, sondern den Browser zu einer anderen Webseite umleiten wollen (geben Sie einen absoluten URL oder einen URL relativ zu Ihrer Website an).

Content-type: text/html

Schicken Sie diesen Header, um den Datentyp der nachfolgend ausgegebenen und zu übertragenden Daten anzugeben. Für HTML-Seiten lautet der Datentyp text/html, für GIF-Bilder image/gif, für Textdateien text/plain, etc.

Auch wenn Sie selbst keine weiteren Header angeben, wird der Server vermutlich noch den einen oder anderen Header anhängen (beispielsweise den Server-Header, durch den sich der Server beim Browser ausweisen kann). Beachten Sie aber, dass Sie auf jeden Fall einen der oben erwähnten Header angeben müssen, da diese bei der CGI-Übertragung nicht vom Server generiert werden können!

Des Weiteren ist zu beachten, dass jeder Header an sich mit der Zeichenkombination Wagenrücklauf (\r) und Zeilenumbruch (\n) abgeschlossen werden muss. Unter Windows erzeugt der Perl-Interpreter aber für die Zeichenkombination statt eines Wagenrücklaufs einen weiteren Zeilenumbruch. Wenn Sie Perl zur CGI-Programmierung unter Windows benutzen oder Ihre Programme einfach nur portabel halten wollen (ausführbar auf Windows- wie auf UNIX/Linux-Rechnern), dürfen Sie die Header daher nur mit \n abschließen (auch wenn es nicht ganz korrekt ist).

Schließlich müssen Sie zwischen dem letzten Header und dem Beginn des HTML-Codes noch einmal einen weiteren Zeilenumbruch (in Vertretung einer Wagenrücklauf/ Zeilenumbruch-Kombination) ausgeben.

Das klingt alles weit komplizierter als es ist. Letzten Endes brauchen Sie sich nur zu merken, dass ein CGI-Programm ein HTML-Dokument auf zweierlei Weise zurückliefern kann

Beispiel

Schauen wir, inwieweit unser Beispielskript aus Abschnitt 17.1.3 diese Voraussetzungen erfüllt.

Listing 17.14: testcgi.pl

#!/usr/bin/perl -w

print "Content-type: text/html\n\n";

print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \n";
print " \"http://www.w3.org/TR/html4/strict.dtd\"> \n";
print "<html>\n";
print "<head>\n";
print "<title>CGI-Testprogramm</title>\n";
print "</head>\n";
print "<body>\n";

print "<p>Hallo vom CGI-Programm!</p>\n";

print "</body></html>\n";

Als Erstes wird der HTTP-Header ausgeben. Wir liefern als einziges Header-Feld Content- type zurück, das wir mit einem doppelten Zeilenumbruch beenden (ein Umbruch für das Header-Feld, ein zweiter Umbruch, um das Ende des Headers anzuzeigen). Danach geben wir den HTML-Code der Webseite aus.

Alles ordnungsgemäß.

Etwas lästig sind die vielen print-Aufrufe und Anführungszeichen. Auch müssen wir darauf achten, dass wir doppelten Anführungszeichen innerhalb der Ausgabestrings das Escape-Zeichen \ voranstellen, damit sie der Perl-Interpreter auch als normale auszugebende Anführungszeichen und nicht als Sonderzeichen zum Abschluss des Strings interpretiert. In Perl können wir uns die Ausgabe vereinfachen, indem wir den HTML- Code der Webseite als HERE-Dokument aufsetzen.

Listing 17.15: hallocgi.pl

#!/usr/bin/perl -w
use strict;

my $webseite = <<HERE_ANTWORT;
Content-type: text/html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>CGI-Testprogramm</title>
</head>
<body>

<p>Hallo vom CGI-Programm!</p>

</body>
</html>
HERE_ANTWORT

print $webseite;

Na, ist das nicht übersichtlicher?!

Wir können uns die Ausgabe sogar noch weiter vereinfachen, indem wir das CGI-Modul aus dem CPAN verwenden. In obigen Beispiel kann man sich nämlich leicht mit der Ausgabe des Headers vertun. Diesem darf keine Leerzeile vorausgehen, es muss sich aber eine Leerzeile anschließen. Will man auf Nummer sicher gehen, ruft man zur Ausgabe des Headers die Perl-Funktion header auf.

Listing 17.16: hallocgi2.pl

#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);

my $webseite = <<HERE_ANTWORT;

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>CGI-Testprogramm</title>
</head>
<body>

<p>Hallo vom CGI-Programm!</p>

</body>
</html>
HERE_ANTWORT

print header, $webseite;

Zu Beginn des Programms wird das CGI-Modul eingebunden und die Standardfunktionen aus dem Modul werden importiert, so dass wir sie ohne Voranstellung des Modulnamens aufrufen können:

use CGI qw(:standard);

Im HERE-Dokument steht jetzt nur noch der reine HTML-Code, dafür lassen wir vor dem HERE-Dokument (das in $webseite abgespeichert ist), noch den Rückgabewert der Perl- Funktion header ausgeben:

print header, $webseite;

Falls das CGI-Modul auf Ihrem System nicht installiert sein sollte, erhalten Sie vom Perl-Interpreter eine entsprechende Fehlermeldung (testen Sie das Skript dazu auf der Konsole). Sie müssen das Modul dann nachinstallieren (siehe Abschnitt 17.2.10).

Um das Programm zu testen, gehen Sie wie folgt vor:

  1. Führen Sie das Skript zur Probe einmal auf der Konsole aus, um sich zu vergewissern, dass es syntaktisch korrekt ist.
  2. Speichern Sie das Skript im cgi-bin-Verzeichnis Ihres Servers (siehe 17.1.2).
  3. Rufen Sie das Skript direkt vom Browser aus auf, indem Sie den URL des Skripts im Adressfeld eingeben oder setzen Sie eine Webseite auf, die das Skript über einen Hyperlink aufruft.

Listing 17.17: hallocgi2.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Hallo CGI</title>
</head>
<body>

<p>Wenn Sie Ihren Webserver für die Verarbeitung von CGI-Perl-Skripten konfiguriert, den Code des <a href="http://localhost/cgi-bin/hallocgi2.pl">hallocgi-Programms</a>
korrekt abgetippt, das Programm im cgi-bin-Verzeichnis Ihres Servers abgespeichert und gegebenenfalls noch den Hyperlink in diesem Absatz angepasst haben, sollte beim Klick auf obigen Hyperlink die vom CGI-Programm dynamisch erzeugte Webseite angezeigt werden.</p>

</body>
</html>

17.4 Serverseitige ImageMaps

Statt nur den URL des CGI-Programms an den Server zu schicken, kann der Browser zusätzlich auch noch Daten als Eingaben für das CGI-Programm übertragen. Das CGI- Programm kann dann diese Eingaben auswerten (vorausgesetzt, es wurde entsprechend programmiert), und differenziert auf die Eingaben reagieren. Dies nutzt man beispielsweise bei der Implementierung serverseitiger ImageMaps.

Wie man clientseitige ImageMaps implementiert, haben Sie bereits in Kapitel 2.6 gelernt (lang ist´s her). Heute wollen wir Ihnen zeigen, wie man die dort vorgestellte clientseitige ImageMap in eine serverseitige ImageMap verwandeln kann. In der Praxis würde man die ImageMap aus diesem Beispiel aus verschiedenen Gründen fast immer clientseitig realisieren (warum wird bald klar), aber didaktisch macht es Sinn, die beiden Technologien einmal im direkten Vergleich vorzustellen.

Der HTML-Code

Die clientseitige ImageMap bestand aus dem eigentlichen Bild und einem MAP- Abschnitt, in dem die Flächen definiert waren, die mit Hyperlinks verbunden sein sollten. Die Verbindung zwischen dem Bild und dem MAP-Abschnitt wurde über das usemap- Attribut und den Namen des <map>-Elements hergestellt.

<img border="0" src="saturn.jpg" usemap="#meineMap" 
width="640" height="480" />

<map name="meineMap">
<area href="s1.html" shape="circle"
coords="193, 312, 125">
<area href="s2.html" shape="polygon"
coords="260, 202, 280, 173, 324, 158, 369, 176, 394, 226, 375,
274, 346, 290, 321, 287, 299, 232, 300, 232">
</map>

Abbildung 17.3:  Zur Erinnerung: das Bild zur ImageMap

Um diese clientseitige ImageMap in eine serverseitige zu verwandeln, bedarf es dreier Schritte:

Die Übertragung der Koordinaten

Damit das CGI-Programm Mausklicke auf das Bild der ImageMap verarbeiten kann, muss es wissen, an welchen Bildkoordinaten der Mausklick erfolgt. Glücklicherweise brauchen wir uns um die Übertragung der Koordinaten nicht selbst zu kümmern, das übernimmt der Browser. Er hängt an den URL des CGI-Programms ein Fragezeichen (das dem Server mitteilt, dass jetzt Eingabedaten für das Programm folgen) und dann die Koordinaten, die durch ein Komma getrennt sind:

http://localhost/cgi-bin/imagemap.pl?143,74

Der Server speichert die Eingaben hinter dem Fragezeichen in einer Umgebungsvariablen namens QUERY_STRING und ruft dann das CGI-Programm auf. Dessen Aufgabe ist es nun, die Koordinaten aus der Umgebungsvariablen QUERY_STRING einzulesen und zu verarbeiten.

Eine Umgebungsvariable ist ein Variable, die global auf einem Rechner gespeichert wird und auf die alle Programme, die auf diesem Rechner ablaufen, zugreifen können.

Das CGI-Programm

Das Perl-Programm zur Unterstützung der ImageMap erstellen wir in zwei Schritten: zuerst konzentrieren wir uns darauf, die Koordinaten entgegen zu nehmen, dann implementieren wir die eigentliche Unterstützung für die ImageMap.

Die erste Version, die sich darauf beschränkt, die Koordinaten aus QUERY_STRING einzulesen und zur Kontrolle als HTML-Code auszugeben, sieht wie folgt aus:

Listing 17.18: Erste Version von imagemap.pl

#!/usr/bin/perl -w

use CGI qw(:standard);
use strict;

my $daten = $ENV{'QUERY_STRING'};
my @koord = split(",", $daten);


my $antwort = <<HERE_ANTWORT;

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Hallo</title>
</head>

<body>

<p>Klick bei $koord[0] und $koord[1].</p>

</body>
</html>
HERE_ANTWORT

print header, $antwort;

Als Erstes kopiert das Programm den Inhalt der Umgebungsvariablen in die Variable $daten. Perl verfügt über eine vordefinierte Hash-Variable %ENV, in der beim Programmstart alle auf dem Rechner verfügbaren Umgebungsvariablen abgelegt werden.

Mit folgender Syntax kann man den Inhalt der Umgebungsvariablen QUERY_STRING aus dem %ENV-Hash herausziehen:

my $daten = $ENV{'QUERY_STRING'};

Angenommen der Webserver hat folgenden URL an den Server geschickt:

http://localhost/cgi-bin/imagemap.pl?143,74

Dann ist jetzt in der Variablen $daten der String "143,74" abgespeichert.

Der nächste Schritt ist, aus diesem String die beiden Koordinaten herauszulesen und in echte Zahlen umzuwandeln. In vielen Programmiersprachen stellt dies ein mittleres Problem dar, nicht aber in Perl, der »Practical Extracton and Reporting Language«. Diese stellt uns eine Funktion namens split() zur Verfügung, mit der man einen String in ein Array verwandeln kann. Dazu übergibt man der Funktion als erstes Argument die Zeichenfolge, die die einzelnen Daten voneinander trennt (in unserem Fall also das Komma), und als zweites Argument den String:

my @koord = split(",", $daten);

Jetzt steht in $koord[0] der Wert 143 und in $koord[0] der Wert 74.

Zum Schluss setzen wir die Antwortseite als HERE-Text auf (wobei Variablennamen durch ihren Wert ersetzt werden) und geben sie aus.

Das endgültige Programm

Für die Verarbeitung der Mausklicks im CGI-Programm gibt es viele Möglichkeiten. Wir werden im Folgenden prüfen, ob der Besucher der Webseite in einen der größeren Planeten geklickt hat und dementsprechend dem Browser eine Location-Header zurückschicken, der ihn zu einer passenden Webseite umleitet.

Listing 17.19: imagemap.pl

#!/usr/bin/perl -w

use CGI qw(:standard);
use strict;

my $daten = $ENV{'QUERY_STRING'};
my @koord = split(",", $daten);

my $url;

if ( ($koord[0] > 100) && ($koord[0] < 280) &&
($koord[1] > 200) && ($koord[1] < 400) )
{
$url = "http://localhost/WebPub/Kap17/imagemap/jupiter.html";
}
elsif ( ($koord[0] > 280) && ($koord[0] < 370) &&
($koord[1] > 160) && ($koord[1] < 290) )
{
$url = "http://localhost/WebPub/Kap17/imagemap/saturn.html";
}
else
{
$url = "http://localhost/WebPub/Kap17/imagemap/daneben.html";
}

print "Location: $url\n\n";

Die if-Bedingung prüft, ob die x-Koordinate zwischen 100 und 280 und die y-Koordinate zwischen 200 und 400 liegt - dies entspricht der Abfrage, ob der Mausklick in einem Rechteck liegt, dessen obere linke Ecke bei 100,200 und die rechte untere Ecke bei 280,400 liegt (dieses Rechteck deckt ungefähr den vorderen Planeten ab). Lag der Mausklick innerhalb dieses Rechtecks, wird der URL der Jupiter-Webseite in der Variablen $url abgespeichert.

Die erste elsif-Bedingung prüft analog, ob der Mausklick im Kernbereich des Saturns lag. Wenn ja wird in der Variablen $url der URL der Saturn-Webseite abgespeichert

Der abschließende else-Teil wird ausgeführt, wenn der Besucher irgendwo ins Niemandsland geklickt hat.

Zum Schluss wird der ermittelte URL als Location-Header verpackt an den Browser zurückgeschickt. Der Browser wertet den Header aus und lädt die angegebene Datei.

17.5 Eigene Daten an CGI-Programme schicken

Analog zu der Art und Weise, wie der Browser die Koordinaten für die Mausklicks an den URL des CG-Programms hängt, können Sie auch selbst in Hyperlinks Daten an den URL eines CGI-Programms hängen.

http://server/cgi-bin/antwort.pl?Hallo_CGI

Beachten Sie, dass zwischen Perl-Programm und den Daten immer ein Fragezeichen stehen muss und dass Leerzeichen und Sonderzeichen nicht erlaubt sind. Leerzeichen können Sie durch Pluszeichen ersetzen.

17.6 Formulare

CGI-Programme werden häufig dazu verwendet, Formulareingaben auf Seiten des Servers zu verarbeiten. Um Programme zur Verarbeitung von Formulareingaben schreiben zu können, muss man wissen, wie die Daten vom Browser an den Server und vom Server an das Programm weitergereicht werden. Im Abschnitt zu den serverseitigen ImageMaps haben wir bereits einen ersten Einblick in die CGI-Datenübertragung bekommen. Diesen wollen wir nun vertiefen und auf eine solide theoretische Basis stellen.

GET und POST

Die CGI-Spezifikation sieht zwei Wege vor, wie ein CGI-Programm Daten von einem Browser entgegen nehmen kann.

Methode

Beschreibung

GET

Bei dieser Methode hängt der Browser die Zeichenkette an den URL des CGI-Programms an, das aufgerufen werden soll, und schickt den URL des Programms mit den angehängten Daten an den Server. Der Server trennt die Zeichenkette mit den Eingaben wieder von dem URL und speichert sie in der Umgebungsvariablen QUERY_STRING ab. Danach ruft der Server das CGI-Programm auf. Dieses muss so programmiert sein, dass es die Eingabe aus der Umgebungsvariablen QUERY_STRING einliest und auswertet.

Ein CGI-Perl-Programm kann die Daten aus dem globalen ENV-Hash auslesen:

my $daten = $ENV{'QUERY_STRING'};

Dieses Verfahren ist an sich recht unkompliziert, hat aber den Nachteil, dass die Länge der übergebenen Zeichenkette beschnitten wird, wenn der Speicherplatz für die Umgebungsvariable nicht ausreicht (üblicherweise 1 KByte = 1024 Zeichen).

POST

Bei dieser Methode schickt der Browser die Daten verpackt in die HTTP-Anfrage an den Server. Der Server speichert die Eingabe-Zeichenkette nicht in einer Umgebungsvariablen, sondern übergibt Sie über die Standardeingabe an das CGI-Programm. Lediglich die Länge der codierten Eingabe-Zeichenkette wird in einer Umgebungsvariablen (CONTENT_LENGTH) abgelegt.

Ein CGI-Perl-Programm kann die Daten mit Hilfe der read-Funktion einlesen:

$groesseDaten = $ENV('CONTENT_LENGTH');
read (STDIN, $form_info, $groesseDaten);

Tabelle 17.9: CGI-Methoden zur Datenübergabe

Auf welchem Weg die Daten übertragen wurden, wird in der Umgebungsvariablen REQUEST_METHOD abgespeichert.

Umgebungsvariablen

Eine Umgebungsvariable ist ein Variable, die global auf einem Rechner gespeichert wird und auf die alle Programme, die auf diesem Rechner ablaufen, zugreifen können. Auf Webservern stehen zur CGI-Unterstützung unter anderem folgende Umgebungsvariablen zur Verfügung

¯ GATEWAY_INTERFACE - Version des CGI-Protokoll. Standardmäßig CGI/1.1.

¯ SERVER_PROTOCOL - Version des HTTP-Protokolls. Meist HTTP/1.0.

¯ REQUEST_METHOD - Entweder POST oder GET.

¯ PATH_INFO - Daten, die nach einem Slash an einen URL gehängt werden. Wird normalerweise verwendet, um einen Pfad relativ zum Dokument-Wurzelverzeichnis anzugeben.

¯ PATH_TRANSLATED - Der vollständige Pfad von PATH_INFO.

¯ QUERY_STRING - Enthält die Eingabedaten, wenn die GET-Methode verwendet wird. Unabhängig von der Methode enthält QUERY_STRING die Daten, die nach einem Fragezeichen (?) an den URL gehängt werden.

¯ CONTENT_TYPE - Beschreibt, wie die Daten kodiert sind. Normalerweise application/x-www-form-urlencoded. Wird für das Hochladen von Dateien via HTTP auf multipart/form-data gesetzt.

¯ CONTENT_LENGTH - Länge der Eingabe, wenn Sie die POST-Methode verwenden.

¯ SERVER_SOFTWARE - Name und Version der Server-Software.

¯ SERVER_NAME - Host-Name der Maschine, auf der der Server läuft.

¯ SERVER_ADMIN - E-Mail-Adresse des Web-Server-Verwalters.

¯ SERVER_PORT - Port, auf dem der Server läuft - normalerweise 80.

¯ SCRIPT_NAME - Name des CGI-Programms.

¯ DOCUMENT_ROOT - Dokumentenwurzelverzeichnisses auf dem Server.

¯ REMOTE_HOST - Name der Client-Maschine, die Informationen anfordert oder sendet.

¯ REMOTE_ADDR - IP-Adresse der mit dem Server verbundenen Client-Maschine.

¯ HTTP_ACCEPT - Liste von durch Kommata getrennter MIME-Typen, die der Browser interpretieren kann.

¯ HTTP_USER_AGENT - Name, Version und normalerweise auch Plattform des Browsers.

¯ HTTP_REFERER - Speichert den URL der Seite, von der aus auf die aktuelle Seite verwiesen wurde.

URL-Codierung

Unabhängig davon. welchen Weg der Browser wählt, muss er die Daten für die Übertragung an den Server noch codieren (Leerzeichen werden durch Plus-Zeichen, Sonderzeichen durch einfache Zeichenfolgen, Eingaben aus Formularfeldern als Name=Wert-Paare codiert).

Angenommen der Besucher einer Webseite tippt in ein Eingabefeld seinen Namen ein:

Ihr Name : Dirk Louis

Wenn das Eingabefeld im HTML-Code die Name-ID Feld1 hat und das Formular so konfiguriert ist, dass es seine Eingaben zur Auswertung per GET an ein CGI-Programm namens cgiskript.pl schickt, so würde der fertige URL, der vom Browser an den Server gesendet wird, wie folgt aussehen:

http://server/cgi-bin/cgiskript.pl?Feld1=Dirk+Louis

Formulareingaben entgegen nehmen

Wird ein CGI-Programm vom Server zur Verarbeitung von Formulardaten aufgerufen, muss es als erstes feststellen, auf welchem Weg (GET oder POST) ihm die Daten übergeben wurden, dann muss es die Daten einlesen und dekodieren. Jetzt erst kann es darangehen, die Daten zu verarbeiten.

Grundsätzlich kann man all dies mit Hilfe der Umgebungsvariablen REQUEST_METHOD, QUERY_STRING und CONTENT_LENGTH selbst implementieren. Man kann es sich aber auch einfacher machen und das Perl-Modul CGI und dessen Funktionen verwenden. Dann braucht man nämlich nur mit Hilfe des Import-Tags :standard die Standardfunktionalität des Moduls einbinden und kann danach die Eingaben problemlos mit Hilfe der Funktion param einlesen:

use CGI qw(:standard); 
...
my $name = param('name');

Doch eines nach dem anderen! Beginnen wir damit, eine Webseite mit einem passenden Testformular aufzusetzen.

Das Formular

Abbildung 17.4:  Das Formular im Browser

Der HTML-Code des Formular aus Abbildung 17.4 sieht wie folgt aus:

Listing 17.20: formular.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Formular</title>
</head>

<body>
<h1>Formular</h1>

<form action="http://localhost/cgi-bin/formular.pl" method="get">

<p>Geben Sie bitte Ihren Namen ein: <input name="name" size="50" />
</p><p><input type="submit" value="Abschicken" /></p>

</form>

</body>
</html>

Zwei Dinge sind aus Sicht des CGI-Programmierers am HTML-Code des Formulars besonders wichtig:

Damit steht fest: das verarbeitende Skript muss formular.pl heißen, im Verzeichnis cgi-bin stehen und die Eingabe aus dem Feld mit dem Namen »name« verarbeiten.

Das CGI-Skript

Listing 17.21: formular.pl

#!/usr/bin/perl -w

use CGI qw(:standard);
use strict;

my $name = param('name');


my $antwort = <<HERE_ANTWORT;

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Hallo</title>
</head>

<body>
<h1>Hallo $name!</h1>

<p>Es freut mich, Dich auf meiner Homepage willkommen heißen zu dürfen</p>

</body>
</html>
HERE_ANTWORT

print header, $antwort;

Zuerst wird das CGI-Modul eingebunden. Dieses stellt uns die Methode param zur Verfügung, die das Entgegennehmen der Formulareingaben vereinfacht. Wir brauchen der Funktion nur den Namen des Formularfeldes zu übergeben, an dessen Inhalt wir interessiert sind, und die Funktion liefert uns diesen zurück:

my $name = param('name');

Der Rest des Skripts besteht darin, in HTML-Code eine Antwortseite aufsetzen. Für diese Aufgabe gibt es im CGI-Modul übrigens eine Reihe von vordefinierten Funktionen (start_html, start_form, end_form, table, h1, end_html, etc.). Wir ziehen es aber vor, den HTML-Code der zurückzuliefernden Seite in Form eines HERE-Dokuments aufzusetzen, da der typische Aufbau der HTML-Seite dann sichtbar bleibt und im Bedarfsfall leichter zu korrigieren oder anzupassen ist.

Zum Schluss wird der HTML-Code mit print an die Standardausgabe geschickt - nicht jedoch ohne vorher die CGI-Methode header aufzurufen, die den HTTP-Header zurückliefert.

Speichern Sie jetzt das Skript unter dem Namen Formular.pl im cgi-Verzeichnis Ihres Webservers (wenn das CGI-Verzeichnis Ihres Servers anders lautet, müssen Sie die Pfadangabe in Formular.html ändern). Laden Sie die Datei Formular.html in Ihren Browser, geben Sie Ihren Namen in das Eingabefeld des Formulars ein und klicken Sie auf den Abschicken-Schalter. Wenn alles korrekt eingerichtet ist, sollte kurz darauf die Antwortseite im Browser erscheinen.

Abbildung 17.5:  Antwortseite im Browser

CGI-Skripte von der Konsole aus testen

Falls Sie keine Antwortseite sehen, könnte dies daran liegen, dass sich beim Aufsetzen des Skripts ein Fehler eingeschlichen hat. Dann empfiehlt es sich, die Korrektheit des Skripts erst einmal von der Konsole aus zu testen. Syntaxfehler können so schnell festgestellt werden. Was macht man aber, wenn der Fehler im logischen Aufbau des Skripts liegt oder die Ausgabe des Skripts fehlerhaft ist.

Um solche Fehler aufspüren zu können, ist es erforderlich, dass das Skript über die Konsole die gleichen Daten entgegen nimmt, die es auch vom Browser erhalten würde. Dazu müssen Sie die Eingaben in der Kommandozeile als name=wert-Paare übergeben und Leerzeichen im wert-Teil durch +-Zeichen ersetzen.

Abbildung 17.6:  CGI-Skripte von der Konsole aus testen

Das funktioniert allerdings nur, wenn das Skript die Daten nach der POST-Methode entgegen nehmen kann. Die CGI-Methode param() unterstützt GET- und POST-Methode.

Ansonsten schreiben die meisten Webserver Fehler, die beim Aufruf einer Seite oder eines CGI-Programmes auftreten, in eine Error-Datei. (Lesen Sie in der Dokumentation Ihres Webservers nach, wie diese Datei heißt und wo sie abgespeichert ist.)

17.7 Verborgene Formularfelder

Wenn der Websurfer den Submit-Schalter eines Formulars drückt, erstellt der Browser die Name/Wert-Paare für alle Steuerelemente im Formular (die Formularfelder) und schickt diese zusammen mit dem URL des verarbeitenden CGI-Programms an den Server. Manchmal reicht uns dies jedoch nicht und wir würden gerne - zusammen mit den Formulareingaben - noch weitere Daten an das CGI-Programm schicken.

Dies könnte beispielsweise der Fall sein, wenn Sie ein CGI-Programm zum Verarbeiten mehrerer Formulare haben (vielleicht führen Sie eine größere Umfrage durch, so dass Sie die Fragen auf mehrere Formulare verteilt haben, die nacheinander von dem CGI- Programm erzeugt und ausgewertet werden). Dann wäre es praktisch, wenn zusammen mit den Formulareingaben auch eine Kennnummer für das Formular gesendet würde, zu dem die aktuellen Daten gehören.

Glücklicherweise ist dies sogar erstaunlich einfach zu realisieren. Man baut einfach verborgene Formularfelder (input-Elemente mit dem Typ hidden) in das Formular ein, weist die zu übertragenden Daten deren value-Attributen zu und nutzt ansonsten die automatische Datenübertragung durch den Browser.

<form action="/cgi-bin/meinprogramm.pl" method="post">
<input type="hidden" name="formularNr" value="1" />

<table border="0" cellspacing="0" cellpadding="10">
<tr>
<td> Name: <input name="name" size="30" /></td>
</tr>
...

<form action="/cgi-bin/meinprogramm.pl" method="post">
<input type="hidden" name="formularNr" value="2" />

<table border="0" cellspacing="0" cellpadding="10">
<tr>
<td> Lieblingsfarbe: <input name="farbe" size="30" /></td>
</tr>
...

17.8 Gästebücher

Was ein Gästebuch ist, wissen Sie sicher. Zahllose private Homepages verfügen über ein Gästebuch, in das sich die Besucher der Website eintragen und das sie sich natürlich auch ansehen können.

Im Folgenden wollen wir uns anschauen, wie man ein solches Gästebuch mit Hilfe von CGI und Perl realisieren kann. Die hier gezeigte Implementierung besteht aus drei Elementen:

Das Gästebuch

Beginnen wir mit dem Gästebuch.

  1. Legen Sie unter dem Dokumentenverzeichnis Ihres Webservers ein neues Unterverzeichnis namens Gaestebuch an.
  2. Setzen Sie das Grundgerüst des Gästebuchs auf. Achten Sie vor allem auf die letzte Zeile, die genauso wie hier abgedruckt aussehen muss.

Listing 17.22: gaestebuch.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<title>Gästebuch</title>
</head>

<body>
<h1> Mein Gästebuch </h1>


</body></html>
  1. Speichern Sie die Datei des Gästebuchs als gaestebuch.html im Verzeichnis Gästebuch (und achten Sie besonders unter UNIX/Linux darauf, dass das Perl-Skript Schreibrechte hat).

Das Formular zum Eintragen ins Gästebuch

  1. Setzen Sie den HTML-Code für das Formular zum Eintragen in das Gästebuch auf.

Listing 17.23: eintragen.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Gästebucheintrag</title>
</head>
<body>

<h1>Tragen Sie sich bitte in mein Gästebuch ein:</h1>
<form action="/cgi-bin/gaestebuch.pl" method="post">
<table border="0" cellspacing="0" cellpadding="10">
<colgroup span=2">
<col width="230">
<col width="450">
</colgroup>
<tr>
<td align="right" valign="top" width="230">
Geben Sie bitte Ihren Namen an : </td>
<td><input name="name" size="30" maxlength="50" /></td>
</tr>
<tr>
<td align="right" valign="top" width="230">
Möchten Sie Ihre EMail hinterlassen : </td>
<td><input name="email" size="30" maxlength="50" /></td>
</tr>
<tr>
<td align="right" valign="top" width="230">
Haben Sie auch eine eigene Website : </td>
<td><input name="website" size="30" maxlength="50" /></td>
</tr>
<tr>
<td align="right" valign="top" width="230">Ihr Kommentar :</td>
<td><textarea name="kommentar" rows="9"
cols="50"></textarea></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="submit" value="In Gästebuch eintragen" />
<input type="reset" value="Formular zurücksetzen" /></td>
</tr>
</table>
</form>
<hr />

</body>
</html>

Abbildung 17.7:  Das Formular zum Eintragen in das Gästebuch

  1. Speichern Sie die Datei des Eingabeformulars als eintragen.html im Verzeichnis Gästebuch.

Das Perl-Programm

  1. Setzen Sie das Perl-Programm auf.

Listing 17.24: gaestebuch.pl

#!/usr/bin/perl -w

use CGI qw(:standard);
use strict;


# ******** Gästebuch öffnen ********

my $gaestebuch =
"../htDocs/WebPub/Kap17/Gaestebuch/ gaestebuch.html";

open(BUCH, "+< $gaestebuch")
or
die "\nDatei $gaestebuch konnte nicht geoeffnet
werden\n";


# ******** Eingaben aus Formular lesen ********

my ($name, $email, $website, $kommentar) =
(param('name'), param('email'), param('website'),
param('kommentar'));


# ******** Eintrag an Gästebuch anhängen ********

my $neuereintrag = <<HERE_EINTRAG;
<p>$name schrieb: </p>
<p><i>$kommentar</i></p>
<p><font size="1">$name ist über $email erreichbar
und unterhält eine eigene Website ($website).
</font size="1"></p>
<hr>
HERE_EINTRAG

seek(BUCH,-14,2);
print BUCH "$neuereintrag\n</body></html>";
close(BUCH);


# ******** Dankseite zurücksenden ********

my $danke = <<HERE_DANKE;
<html>
<head>
<title>Danke!</title>
</head>

<body>
<h1>Danke!</h1>

<p>Danke, dass Sie sich in mein Gästebuch
eingetragen haben!</p>
<p>Haben Sie denn auch schon einmal in das
<a href="http://localhost/
¬/WebPub/Kap17/Gaestebuch/gaestebuch.html">Gästebuch</a>
hineingeschaut?</p>

<p>&nbsp;</p>
<p><i>So long</i></p>

</body></html>
HERE_DANKE

print header, $danke;

Der Pfad zur Gästebuchdatei muss für Ihren Server angepasst werden!

Als Erstes öffnet das Skript die Gästebuchdatei:

my $gaestebuch = 
"../htDocs/WebPub/Kap17/Gaestebuch/ gaestebuch.html";

open(BUCH, "+< $gaestebuch")
or
die "\nDatei $gaestebuch konnte nicht geoeffnet
werden\n";

Gegen den Code an sich ist nichts zu sagen, er könnte jedoch noch ein wenig verbessert werden. Bedenken Sie, dass das Skript unter Umständen von zwei verschiedenen Besuchern Ihrer Website mehr oder weniger gleichzeitig aufgerufen werden könnte. Wenn die beiden Instanzen dann in die Datei schreiben, kann es passieren, dass Daten verloren gehen oder gar die ganze Datei korrumpiert wird. Sie können dies verhindern, indem Sie die Datei nach dem Öffnen mit Hilfe der Funktion flock aus dem Fcntl-Modul sperren:

use Fcntl qw(:flock);
...
flock(BUCH, LOCK_EX);

In den darauf folgenden Zeilen werden die Inhalte der Formularfelder ausgelesen:

my ($name, $email, $website, $kommentar) =
(param('name'), param('email'), param('website'),
param('kommentar'));

Hierzu ist anzumerken, dass das Skript nicht prüft, ob für alle Felder sinnvolle Eingaben vorliegen. Wir haben im Beispiel darauf verzichtet, um den Code nicht unnötig zu komplizieren. Wenn Sie CGI-Skripte schreiben, die Eingaben verarbeiten, sollten Sie die Eingaben aber unbedingt prüfen (mit if(!defined param('name') ) .

Achten Sie auch darauf, was Sie mit den Formulareingaben machen. Böswillige Hacker könnten statt dem erwarteten Namen oder Kommentar einen Betriebssystembefehl, eine Perl-Befehlsfolge oder ähnlichen Code eingeben, der je nachdem wie die Eingabe weiterverarbeitet wird, ein riesiges Loch in das Sicherheitsnetz Ihres Servers reißen kann.

Unter Verwendung der Formulareingaben wird - in Form eines HERE-Dokuments - ein neuer Eintrag für das Gästebuch aufgesetzt. Dieser wird anschließend an das Gästebuch angehängt. Allerdings wollen wir nicht, dass der neue Eintrag an die abschließende </ body></html>-Zeile angehängt wird, sondern er soll diese ersetzen. Wie kann man dies bewerkstelligen? Jeder Datei-Handle verfügt über einen Positionsmarker, der angibt, an welche Stelle der Datei als Nächstes geschrieben bzw. von wo gelesen wird. Diese Positionsmarke kann man mit Hilfe der Perl-Funktion seek verschieben. Dazu übergibt man seek den Datei-Handle, die Anzahl der Zeichen, um die verschoben werden soll, und die Position, ab der verschoben werden soll. Der Aufruf

seek(BUCH,-14,2);

verschiebt die Positionsmarke vom Dateiende an (Argument 2) um 14 Zeichen nach vorne (Argument -14).

Danach werden der neue Eintrag und eine neue abschließende Zeile in die Datei geschrieben:

print BUCH "$neuereintrag\n</body></html>";

Zu guter Letzt wird eine Antwortseite zurückgeliefert, die dem Besucher der Webseite anzeigt, dass seine Eingaben verarbeitet wurden, und die ihm einen Link zum Anzeigen des Gästebuchs anbietet.

  1. Speichern Sie das Skript unter dem Namen gaestebuch.pl im cgi-Verzeichnis Ihres Webservers. Wenn Sie unter UNIX/Linux arbeiten, müssen Sie die Zugriffsrechte ändern, damit das Skript vom Webserver ausgeführt werden kann (beispielsweise chmod 755 gaestebuch.pl) und auch sicherstellen, dass das Perl-Skript Schreib- und Leserecht für die Gästebuchwebseite besitzt (beispielsweise chmod 666 gaestebuch.html).
  2. Rufen Sie jetzt das HTML-Dokument mit dem Formular über Ihren Browser auf, füllen Sie die Felder des Formulars aus und klicken Sie auf den Schalter. Wenn alles korrekt eingerichtet ist, sollten kurz darauf die Antwortseite im Browser erscheinen.

    Abbildung 17.8:  Von gaestebuch.pl generierte Antwortseite

  3. Über den Link können Sie das Gästebuch einsehen.

    Abbildung 17.9:  Das Gästebuch

Wenn Sie das Gästebuch beim Testen des Skripts wiederholt aufrufen, kann es passieren, dass die zuletzt eingetragenen Formularangaben nicht angezeigt werden. Dies muss nicht am Skript liegen, sondern kann damit zu tun haben, dass Ihr Browser den Inhalt der Gästebuchdatei aus seinem Cache rekonstruiert. Aktualisieren Sie dann die Seite oder löschen Sie den Cache.

17.9 Zusammenfassung

Die CGI-Spezifikation legt fest, wie Daten vom Browser an ein Programm auf einem Server übertragen werden können, wie das Programm die Daten entgegen nehmen und wie es seine Ausgabe an den Browser zurücksenden kann.

In diesem Kapitel haben wir uns sowohl mit der Theorie der CGI-Datenübertragung (GET- und POST-Methode, URL-Codierung, CGI-Umgebungsbvariablen auf dem Server) als auch mit der Implementierung von CGI-Programmen beschäftigt und dazu eine neue Programmiersprache, Perl, kennen gelernt.

Perl ist eine an sich recht einfach zu erlernende Programmiersprache (kein Vergleich zu Java!) - auch wenn die Vielzahl von Symbolen und alternativen Syntaxformen, die es in der Sprache gibt, den Anfänger eher verwirren. Dass sich die Beschäftigung mit Perl für Webdesigner und Webprogrammierer durchaus lohnen kann, beweist allein schon die große Zahl der weltweit installierten CGI-Perl-Programme. Und sollte es darüber hinaus noch weiterer Argumente bedürfen, so braucht man nur die Beispiele im zweiten Teil dieses Kapitels durchzugehen und sich anzuschauen, mit welcher Eleganz und Leichtigkeit Aufgaben wie das Parsen von QUERY_STRING (Beispiel zur serverseitigen ImageMap) das Lesen und Schreiben von Dateien (Gästebuch-Beispiel) oder das Ausgeben von HTML-Seiten (Beispiel zur dynamischen Erstellung von Webseiten, Gästebuch) in Perl realisiert werden können.

17.10 Fragen und Antworten

Frage:
Kann man mit Perl nur CGI-Programme schreiben?

Antwort:
Nein, mit Perl können Sie praktisch jede beliebige Art von Programm schreiben (mit Hilfe des Tk-Moduls sogar Programme mit grafischer Benutzeroberfläche). Dass Perl so häufig in Zusammenhang mit der CGI-Programmierung erwähnt wird, hängt einfach damit zusammen, dass die Stärken von Perl in der Textverarbeitung liegen - was für die CGI-Programmierung sehr vorteilhaft ist.

Frage:
Wie ist es möglich, dass die CGI-Methode param() sowohl via GET als auch via POST verschickte Formulareingaben einlesen und an das CGI-Programm zurückliefern kann?

Antwort:
Nun, ganz einfach. Bei jeder CGI-Datenübertragung wird der gewählte Übertragungsweg (GET oder POST) in der Umgebungsvariablen REQUEST_METHOD abgelegt. Die param()-Methode fragt den Wert von REQUEST_METHOD ab und liest die Daten je nach verwendeter Übertragungsmethode aus der Umgebungsvariable QUERY_STRING (GET-Methode) oder der Standardeingabe (POST-Methode) ein.

Frage:
Gibt es Adressen im Web, von wo man fertige CGI-Programme herunterladen kann?

Antwort:
Die gibt es: http://www.cgi-resources.com, http://www.worldwidemart.com/scripts, http://www.freecode.com oder http://www.scriptsearch.com.

17.11 Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.

Quiz

  1. Mit welchen Präfixen beginnen in Perl skalare Variablen und Array-Variablen?
  2. Welcher Unterschied besteht zwischen Perl-Strings in einfachen und doppelten Anführungszeichen?
  3. Mit welchem Operator prüft man in Perl, ob der Wert eines Skalars $var1 kleiner ist als der Wert von Skalar $var2?
  4. Mit welchem Operator prüft man in Perl, ob der String in einem Skalar $str1 kleiner ist als der String in dem Skalar $str2?
  5. Was macht man, wenn man in Perl eine Lösung für eine Aufgabe sucht, mit der sich vermutlich schon andere Perl-Programmierer herumschlagen mussten?
  6. Wozu dient die Perl-CGI-Methode header?
  7. Wie kann man zusammen mit den Formulareingaben eines Formulars zusätzliche Daten an ein CGI-Programm schicken?
  8. Wie kann ein CGI-Programm den Browser zu einer bestimmten Webseite leiten?

Übungen

  1. In Übung 1 aus Kapitel 13 haben Sie ein Umfrage-Formular zum Thema »internetfreier Sonntag« erstellt. Zu diesem Formular sollen Sie nun ein passendes CGI-Programm aufsetzen. Um die Aufgabe nicht unnötig zu komplizieren, soll das CGI-Programm nur die Pro/Kontra-Optionsfelder auswerten und den Stand der Umfrage aktualisieren. Den Stand der Umfrage speichert man am besten in einer Datei. Beim Start des CGI-Programms lädt es diese Datei und liest den alten Stand ein. Dann aktualisiert das Programm den augenblicklichen Stand der Umfrage auf der Grundlage der vorliegenden Formulardaten und speichert den neuen Stand wieder in der Datei. Zum Schluss soll das Programm eine Webseite mit dem aktuellen Stand der Umfrage zurückliefern.



vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbackKapitelanfangnächstes Kapitel


123

© Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH