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.
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.
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.
http://webserver/cgi-bin/programm.pl
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.
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.
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.)
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.
ScriptAlias /cgi-bin/ /home/httpd/cgi-bin/2
<Directory /home/httpd/cgi-bin>
AllowOverride None
Options None
</Directory>
Starten Sie den Server danach neu (je nach Version /etc/rc.d/init.d/httpd restart oder / usr/local/apache/bin/apachectl restart).
Falls Sie ActivePerl installiert haben, können Sie auch in der HTML-Dokumentation nachschlagen (C:/Perl-Verzeichnis/html/index.html).
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.
#!/usr/bin/perl -w
print("Hallo Welt!\n");
perl hallo.pl
Hallo Welt!
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
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.)
#!/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.
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).
Unser erstes Perl-Programm haben wir bereits in Abschnitt 17.1.3, bei der Kontrolle der Perl-Installation aufgesetzt.
#!/usr/bin/perl -w
print("Hallo Welt!\n");
Die erste Zeile in diesem Programm ist ein Kommentar.
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 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.
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).
Perl kennt - im Gegensatz zu JavaScript - verschiedene Variablentypen: Skalare, Listen, Hashes, Referenzen und Datei-Handles. Konzentrieren wir uns zuerst auf die Skalare.
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
$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.
#!/usr/bin/perl -w
use strict;
my $zahl1 = 100;
my $zahl2 = 12;
print $zahl1 + $zahl2;
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.
#!/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.
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
#!/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!
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.
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.
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).
#!/usr/bin/perl -w
use strict;
my $zahl1 = 100;
my $zahl2 = 12;
my $ergebnis;
$ergebnis = $zahl1 / $zahl2;
printf "%d / %d = %.3f ", $zahl1, $zahl2, $ergebnis;
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>);
#!/usr/bin/perl -w
use strict;
my $name;
print("Wie heissen Sie? ");
$name = <STDIN>;
chomp($name);
print("Hallo $name!\n");
Wie jede anständige Programmiersprache verfügt auch Perl über eine Reihe von Operatoren zur Manipulation elementarer Daten.
Neben dem Zuweisungsoperator stehen die typischen arithmetischen Operatoren zur Verfügung.
Hinzukommen die Operatoren für Exponent und Modulo.
Teilt op1 durch op1 und gibt den dabei verbleibenden Rest als Ergebnis zurück. |
Tabelle 17.3: Exponent und Modulo
sowie Inkrement, Dekrement und die kombinierten Zuweisungen.
Haben wir irgend etwas vergessen? Ach ja, die Operatoren zum Vergleichen von Zahlen:
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
$str = "Hallo" . $name;
$rahmen = '*' x 10; # erzeugt **********
Wie in fast allen strukturierten Programmiersprachen gibt es auch in Perl if-else- Verzweigung und verschiedene Schleifenkonstruktionen.
#!/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.
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.
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 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.
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.
Tabelle 17.7: Array-Elemente hinzufügen oder löschen
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, " ";
}
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
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).
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
}
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
}
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.
#!/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);
}
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:
my $dateiname = "hallo.html";
open(DATEI, ">> $dateiname");
Als Argumente übergibt man open einen Namen für den einzurichtenden Datei- Handle und den Namen der zu öffnenden Datei. Die Funktion richtet den gewünschten Datei-Handle ein (Datei-Handles stellen unter Perl einen eigenen Datentyp dar), öffnet die angegebene Datei und verbindet diese mit dem Datei- Handle. In unserem Skript wird die Datei jetzt durch den Datei-Handle repräsentiert, alle nachfolgenden Zugriffe auf die Datei erfolgen über den Datei-Handle.
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.
Für die Ausgabe von Daten in Dateien verwendet man die gleichen Funktionen wie für die Ausgabe von Daten auf die Konsole: print oder printf. Der einzige Unterschied ist, dass man den Funktionen als erstes Argument den Datei-Handle übergibt.
print DATEI $var1, $var2;
Wenn Sie die Argumente zu einer Dateifunktionen nicht in Klammern einfassen, wird der Datei-Handle nicht durch Komma von den anderen Argumenten getrennt!
Das Schließen der Datei führt dazu, dass eventuell noch gepufferte Daten in die Datei geschrieben werden und die Datei mit dem Dateiendezeichen abgeschlossen wird. Wenn Sie das Programm mit geöffnetem Datei-Handle beenden, kann es passieren, dass Daten verloren gehen oder die Datei beschädigt wird.
#!/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);
Das Lesen einer Datei erfolgt in drei Schritten:
Zum Öffnen von Dateien verwendet man wie beim Schreiben die Funktion open. Zu beachten ist nur, dass man zum Öffnen im Lese-Modus das <-Zeichen vor den Dateinamen stellt (oder nur den Dateinamen angibt, siehe Tabelle 17.8).
my $dateiname = "hallo.html";
open(DATEI, "< $dateiname");
Wie man die Daten einliest, hängt davon ab, wie der Inhalt der Datei aufgebaut ist. Eine Möglichkeit ist, den Inhalt mit Hilfe des <>-Operators und einer while-Schleife Zeile für Zeile einzulesen und die einzelnen Zeilen in einer Schleifenvariable zu speichern.
while($zeile = <HANDLE>)
{
# Zeile weiter verarbeiten
}
#!/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);
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).
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;
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.
modulfunktion();
Modul:: modulfunktion();
use CGI qw(:standard); # Modulelemente importieren
modulfunction();
Was aber wenn man feststellt, dass das betreffende Modul gar nicht installiert ist?
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.
ppm> set more 20
ppm> search
ppm> install PackageName
ppm > quit
Weitere Informationen zu ppm finden Sie in der Online-Dokumentation auf der ActiveState-Website (www.activestate.com).
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
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
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.
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
print "Location: /WebPub/Kap02/auto.html\n\n";
print "Content-type: text/html\n\n";
Schauen wir, inwieweit unser Beispielskript aus Abschnitt 17.1.3 diese Voraussetzungen erfüllt.
#!/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.
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.
#!/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.
#!/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:
<!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>
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.
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>
Um diese clientseitige ImageMap in eine serverseitige zu verwandeln, bedarf es dreier Schritte:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Serverseitige ImageMap</title>
</head>
<body>
<h2>Serverseitige Imagemap</h2>
<a href="http://localhost/cgi-bin/imagemap.pl">
<img border="0" src="saturn.jpg" ismap="ismap"
width="640" height="480" />
</a>
</body>
</html>
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 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.
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.
#!/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.
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.
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.
Die CGI-Spezifikation sieht zwei Wege vor, wie ein CGI-Programm Daten von einem Browser entgegen nehmen kann.
Tabelle 17.9: CGI-Methoden zur Datenübergabe
Auf welchem Weg die Daten übertragen wurden, wird in der Umgebungsvariablen REQUEST_METHOD abgespeichert.
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:
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
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.
Abbildung 17.4: Das Formular im Browser
Der HTML-Code des Formular aus Abbildung 17.4 sieht wie folgt aus:
<!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.
#!/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
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.)
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>
...
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:
Beginnen wir mit dem Gästebuch.
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>
Das folgende Formular kennen Sie bereits aus Kapitel 3. Es verfügt über drei Eingabefelder und ein mehrzeiliges Textfeld mit den Namen: name, email, website und kommentar. Als CGI-Programm zur Bearbeitung der Formulardaten wurde /cgi- bin/gaestebuch.pl angegeben (wenn das CGI-Verzeichnis Ihres Servers anders lautet, müssen Sie die Pfadangabe entsprechend anpassen).
<!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> </td>
<td><input type="submit" value="In Gästebuch eintragen" />
<input type="reset" value="Formular zurücksetzen" /></td>
</tr>
</table>
</form>
<hr />
</body>
</html>
#!/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> </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.
Abbildung 17.8: Von gaestebuch.pl generierte Antwortseite
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.
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.
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.
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.
Noch ein Hinweis zur Auswertung der Optionsfelder: Der Browser liefert als Wert für eine Gruppe von Optionsfeldern (gleiches name-Attribut) den value-Wert des Optionsfeldes, das gesetzt ist.
Perl wird wie JavaScript interpretiert. Der Interpreter muss dazu aber extra auf dem Rechner, auf dem die Perl-Programme ausgeführt werden sollen, installiert werden.
Je nach Apache-Version kann das Verzeichnis auch /usr/local/apache/cgi-bin/ lauten. Sie können aber auch ein eigenes Verzeichnis für Ihre CGI-Programme festlegen.
Wenn das aktuelle Verzeichnis nicht im Ausführungspfad eingetragen ist, müssen Sie es explizit mit angeben: ./hallo.pl.