Unterabschnitte

PHP & HTML

Formulare

Mit PHP können Formulardaten relativ einfach eingelesen werden. Man muß im Formular als action den Dateinamen der das Formular auswertenden PHP-Datei angeben und als method empfiehlt sich häufig `post`. Bei `get` kann man sonst die Werte der Eingabe in der URL wiederfinden[*].

In bestimmten Fällen macht es auch Sinn, kein action anzugeben, nämlich dann, wenn das Zielscript dasselbe wie das aktuelle ist. Zu beachten ist dabei, daß in diesem Fall alle GET-Parameter des Scriptaufrufs, der zum Anzeigen des Formulars führte, implizit wieder übergeben werden - wenn das Formular also mit GET arbeitet, hat man nach dem Absenden sowohl die ursprünglichen als auch die neuen GET-Parameter!

Die Felder eines verschickten Formulares[*] werden beim Laden der Empfängerseite (angegeben im ,action` des Formulars) automatisch in Variablen gleichen Namens verwandelt, auf die man im Verlauf des Script-Hauptteils direkt zugreifen kann.[*] Will man auf solche Variablen auch in Funktionen zugreifen, ohne sie global definieren zu müssen, kann man ab PHP 4.1 die ,,superglobalen`` Arrays $_GET und $_POST, je nach Übergabeart, verwenden. In älteren PHP-Versionen heißen diese Arrays $HTTP_GET_VARS bzw. $HTTP_POST_VARS und müssen außerhalb des Script-Hauptteils explizit als global deklariert werden (global $var;).

Nun ein kleines Beispiel. Die Eingaben, die auf der Seite `eingabe.html` eingetragen wurden, werden durch Drücken von OK an die Datei `ausgabe.php` übermittelt. Durch diese werden sie dann ausgegeben.
Quelltext der Datei `eingabe.html`:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
 <title>Eingabe</title>
</head>
<body>
<div align="center">
<form action="ausgabe.php" method="post">
  Feld1: <input name="feld1" size="60" maxlength="60"><br>
  Feld2: <input name="feld2" size="60" maxlength="60"><br>
  <input type="submit" value="OK">
  <input type="reset" value="Abbrechen">
</form>
</div>
</body>
</html>

Quelltext der Datei `ausgabe.php`:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
 <title>Ausgabe</title>
</head><body>
<?php
printf("Feld 1:%s<br>Feld 2:%s",
       $_POST["feld1"],
       $_POST["feld2"]);
?>
</body>
</html>

Natürlich kann die Seite zum Senden und die zum Auswerten auch genau dieselbe sein; in diesem Fall muß sie logischerweise eine PHP-Endung haben.

Diese Variante (Formular und Auswertung in einer Seite) hat sich in vielen Situationen als praktisch erwiesen und wird deshalb auch häufig empfohlen. Siehe auch Kapitel 9.4.

Werte übergeben

Es gibt auch Situationen, da möchte man dem PHP-Script Werte übergeben, ohne ein Formular zu verwenden. Dies ist natürlich auch möglich. Ein normaler HTML-Link sieht folgendermaßen aus:
<a href="datei.php">Linktext</a>
Wenn man jetzt der Datei die Werte `Wert1` und `2` in den Variablen ` VAR1` und `VAR2` übergeben will, sieht der Link folgendermaßen aus:

<a href="datei.php?var1=Wert1&var2=2">Linktext</a>

Allgemeiner formuliert: An das Verweisziel (in unserem Fall `datei.php`) wird mit einem `?` beginnend der Variablenname und mit einem Gleichheitszeichen der Wert angehängt; weitere Werte mit einem `&` statt `?`. Es dürfen keine Leerzeichen dabei entstehen. Sonderzeichen in Werten, wie Umlaute, Leerzeichen, das Kaufmannsund- oder Fragezeichen, müssen nach einem bestimmten Schema kodiert werden, das URL-encoded genannt wird. PHP bietet mit den Funktionen urlencode und urldecode die Möglichkeit, Strings von und in dieses Format zu wandeln; beim Verschicken von Formularen wird quasi für jedes Feld urlencode aufgerufen und beim Bereitstellen der POST/GET-Daten in Dateien umgekehrt urldecode. Tabelle ,urlencoded` listet die wichtigsten Sonderzeichen und ihre Kodierung auf.


Tabelle 9.1: urlencoded
Zeichen Code Zeichen Code Zeichen Code
Leerzeichen + / %2F { %7B
! %21 : %3A | %7C
,, %22 ; %3B } %7D
# %23 < %3C ~ %7E
$ %24 = %3D ä %E4
% %25 > %3E ö %F6
& %26 ? %3F ü %FC
' %27 @ @ Ä %C4
( %28 [ %5B Ö %D6
) %29 \ %5C Ü %DC
* * ] %5D ß %DF
+ %2B ^ %5E --
, %2C _ _ --
- - ` %60 --
. . --


Wenn man die übergebenen Werte verwendet, darf man nicht vergessen, daß jeder[*] die Werte beim Aufruf verändern kann. Deshalb sollten die Variablen vor der Weiterverarbeitung auf korrekte Werte hin überprüft werden.


Dynamische Formulare

Formulare zur Übermittlung von Daten (per POST oder GET) an sich sind schon ganz brauchbar, aber für wirklich interaktive, dynamische Formulare braucht es noch etwas mehr Verständnis von HTML, insbesondere den Formularelementen. Grundsätzlich hat jedes solcher Elemente einen Namen (name) und einen Wert (value). Der Name wird beim Verschicken des Formulars zum Namen der Variable bzw. des Indexes im Array, die den Wert des entsprechenden Formularelements annimmt. Im Wesentlichen sind folgende Elemente zu unterscheiden:

  1. INPUT-Felder gliedern sich in folgende Typen (type):
    1. TEXT: Normales Texteingabefeld
    2. PASSWORD: Paßworteingabefeld (Anzeige von Sternchen statt Zeichen) -- Achtung: Die Übermittlung erfolgt i.A. ungesichert!
    3. SUBMIT: Button, der das Formular abschickt. Wird ein Name angegeben, so hat dieser der HTML-Syntax entsprechend den Wert des Buttontitels.
    4. RADIO: Radiobutton. Mehrere Radiobuttons mit demselben Namen aber unterschiedlichem Wert werden zu einer Gruppe zusammengefaßt, aus der nur ein (oder am Anfang ggf. kein) ,,Knopf`` gleichzeitig gedrückt sein kann. Der standardmäßig gewählte Knopf wird mit dem Attribut checked (bei HTML ohne Wert) definiert.
    5. CHECKBOX: Checkbox. Soll sie standardmäßig gesetzt sein, wird dies mit dem Attribut checked (bei HTML ohne Wert) angegeben. Haben mehrere Checkboxen den gleichen Namen und endet dieser mit den Zeichen [], so werden sie bei der Umwandlung durch PHP in ein Array umgewandelt, das genau die Einträge (mit den jeweils entsprechenden Werten) enthält, deren zugehörige Checkboxen beim Verschicken gesetzt waren. Auf diese Weise lassen sich besonders gut logisch zusammengehörende Mehrfachauswahlen realisieren.
    6. HIDDEN: verstecktes Feld. Dient der Übermittlung von Informationen, die weder veränderbar noch sichtbar sein sollen [*].
  2. SELECT-Boxen erlauben die Auswahl eines Eintrags aus einer Dropdown-Liste. Ist das value-Attribut bei den option-Tags nicht vorhanden, so dienen die Einträge selbst als Werte.

Das folgende Beispiel zeigt, wie die genannten Formularelemente eingesetzt werden können und wie ihre Vorbelegung implementiert wird. Interessant ist dabei u.a. die Verwendung zweier neuer PHP-Funktionen: is_array() prüft, ob eine Variable ein Array ist[*], und mit in_array() kann man feststellen, ob ein Wert (Parameter 1) in einem Array (Parameter 2) enthalten ist.[*]

Anstelle der statischen Arrays in den beiden Funktionen für die Ausgabe der Optionen können, in Verbindung mit einer Datenbank-Anbindung, natürlich auch Arrays, die aus einer SQL-Abfrage resultieren, verwendet werden. Solche Arrays erstellt man i.A. mittels einer while-Schleife.


functions.inc:

<?php
  /**
   * Beruf-Optionen ausgeben
   *
   * @param $beruf Bisheriger Wert
   */
  function print_beruf_options($beruf=0) {
    $berufe = array("Angestellter", 
                    "Oedie", 
                    "Student/Schueler");
    for ($i=0;$i<count($berufe);$i++) {
      printf("<option value=\"%d\"%s>%s</option>\n",
        ($i+1), ($beruf==($i+1) ? " selected" : ""),
        htmlentities($berufe[$i])
      );
    }
  }

  /**
   * Hobby-Optionen ausgeben
   *
   * @param $hobby Array bisheriger Werte
   */
  function print_hobby_options($hobby) {
    if (!is_array($hobby))
      $hobby = array();
    $hobbies = array("Lesen", "Radfahren", "Schwimmen");
    for ($i=0;$i<count($hobbies);$i++) {
      printf("<input type=\"checkbox\" name=\"hobby[]\" ".
             "value=\"%s\"%s> %s\n",
        htmlentities($hobbies[$i]),
        (in_array($hobbies[$i],$hobby) ? " checked" : ""),
        $hobbies[$i]
      );
    }
  }
?>

daten.php:

<?php include("./functions.inc"); ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
 <title>Pers&ouml;nliche Daten</title>
</head>
<body>
<?php
  // lokale Variablen setzen
  $vars = array("sender", "geschl", "vorname",
                "name", "beruf", "hobby");
  foreach ($vars as $var)
    $$var = $_POST[$var];

  if ($sender)
    printf("Die Daten wurden von %s aus verschickt.",
           htmlentities($sender));
  if (!isset($geschl))
    $geschl = 'm';
?>
<form action="daten.php" method="post">
<input type="hidden" name="sender" value="daten.php">
  Vorname: <input name="vorname" size="25" maxlength="60"
    value="<?php
      printf("%s", htmlentities($vorname));
    ?>"><br>
  Name: <input name="name" size="25" maxlength="60"
    value="<?php
      printf("%s", htmlentities($name));
    ?>"><br>
  Geschlecht: <input type="radio" name="geschl"
    value="m"<?php
      printf("%s", ($geschl=='m' ? " checked" : ""));
    ?>> m&auml;nnlich &nbsp;
  <input type="radio" name="geschl"
    value="w"<?php
      printf("%s", ($geschl=='w' ? " checked" : ""));
    ?>> weiblich<br>
  Beruf: <select name="beruf">
  <option value="0"<?php
    echo (!isset($beruf) ? " selected" : "");
  ?>>--- Bitte w&auml;hlen ---</option>
  <?php
    print_beruf_options($beruf);
  ?>
  </select><br>
  Hobbies: <?php
    print_hobby_options($hobby);
  ?><br>
  <input type="submit" value="Weiter">
  <input type="reset" value="Zur&uuml;cksetzen">
</form>
</body>
</html>

Der Effekt dieser ganzen Abfragen und Zusatzangaben ist letztlich nicht nur, daß die Daten verschickt werden, sondern daß auch das Formular nach dem Verschicken wieder genau so aussieht, wie zuvor. In diesem Beispiel werden die verschickten Daten, bis auf das Ausfüllen des Formulars, nicht weiterverarbeitet. Anbieten würde sich für persönliche Daten z.B. das Anlegen oder Aktualisieren von Datensätzen einer entsprechenden Datenbank. Da im obigen Beispiel die Hobbies über Checkboxen realisiert wurden, die eine Auswahl ermöglichen, die aus mehreren Werten besteht, müßte dieses Feld datenbanktechnisch wohl relational definiert werden, also über eine Zusatztabelle, die die Beziehung zwischen persönlichen Daten und einer Hobbytabelle herstellt:


Tabelle 9.2: Datenbank-Realisierung der Hobbies-Mehrfachauswahl
pers_daten pd_hob hobbies
ID $ \leftarrow$ PID, HID $ \rightarrow$ ID



Normalform von Formularen

Die Verarbeitung von Daten, die über ein Formular verschickt werden, folgt allgemein einem bestimmten Schema, das sich einfach daraus ergibt, daß die Daten nicht nur verifiziert und damit ggf. auf neue, korrigierte Werte gesetzt werden müssen (nebst Erstellung einer Fehlermeldung, die zusammen mit allen anderen an zentraler Stelle ausgegeben werden kann), sondern im Verlauf der Datenverarbeitung auch der Fall auftreten kann, daß man HTTP-Header, wie für eine Weiterleitung notwendig, schicken will. Wie wir aus Kapitel 11.1 wissen, darf jedoch keine direkte Ausgabe, also z.B. mittels echo, print(f) oder bloßer Text außerhalb der PHP-Marken, erfolgen, bevor nicht alle Header ausgegeben worden sind. Dies wiederum bedeutet, daß alle Codeabschnitte, die unter irgendwelchen Bedingungen HTTP-Header ausgeben könnten, vor der ersten direkten Ausgabe stehen müssen.

Es bietet sich also an, die gesamte Datenverarbeitung an den Beginn eines PHP-Scriptes zu stellen und darauf zu achten, daß kein einziges anderes Zeichen vor der ersten PHP-Marke (am Dateianfang) steht. Eine Weiterleitung über HTTP-Header macht v.a. dann Sinn, wenn das Formular nach erfolgtem Eintragen und Verschicken von korrekten Daten nicht mehr gebraucht wird[*]. In diesem Fall hat dies auch den Vorteil, daß ein ,,Reload`` der Seite keinen Effekt hat. Auch sonst sollte man aber immer überprüfen, ob eine doppelte Eintragung durch falsche bzw. mißbräuchliche Browserbenutzung möglich ist und entsprechende Vorkehrungen bei der Konstruktion der Datenverarbeitung treffen (z.B. nach gleichen Einträgen suchen, bevor ein INSERT versucht wird).

Die Erkennung, ob Daten verschickt wurden oder nicht, macht man i.A. davon abhängig, ob die Variable, die den gleichen Namen hat wie der Submit-Button des Formulars, das die Daten verschickt, gesetzt ist oder nicht. Zur Erinnerung: Ein solcher Submit-Button trägt dann, wenn er einen Namen hat, den Wert, der als value-Attribut angegeben wurde - welcher in diesem Fall gleichbedeutend ist mit Button-Titel. In PHP spielt es dabei meist eine untergeordnete Rolle, ob man für die Überprüfung die Funktion isset(var) benutzt, die auf Existenz einer Variable testet, oder einfach direkt die Variable abfragt, denn in PHP ist jeder String außer dem leeren und ,,0`` gleich dem booleschen Wert TRUE.

Im folgenden Schema sei $submitted true, falls das (hier nicht angegebene) Formular abgeschickt wurde. Das könnte man z.B. durch Abfragen des Submit-Buttons erreichen, dessen Beschriftung bekanntlich gleich seinem Wert ist. Wie alle anderen Formularwerte findet sich auch der des Submit-Buttons im passenden superglobalen, assoziativen Array[*], genau gesagt an der Indexposition mit Namen des name-Attributs des Submit-Buttons.

Der gesamte Datenverarbeitungsblock kann natürlich auch über Funktions- oder Methodenaufrufe (bei OOP, siehe Kapitel 20) erledigt werden, dann ist jedoch unbedingt darauf zu achten, eine saubere Parameterübergabe zu verwenden; hierbei bieten sich besonders assoziative Arrays an[*].

<?php
// ggf. Funktions-Include
if ($submitted) {
  // Daten prüfen, ggf. Fehlermeldungen erzeugen
  if (<Daten OK>) {
    // eigentliche Verarbeitung:
    // - neuen Datensatz anlegen
    // - bestehenden Datensatz aktualisieren
    // - bestehenden Datensatz löschen
    // und ggf. weiterleiten
  } else {
    // Fehlermeldung erzeugen
    // (in Variable schreiben)
  }
} elseif ($fillform) {
  // alte Daten lesen und in Variablen speichern
}
// Frühestmögliche Ausgabe: ggf. HTML-Header-Include;
// Fehler ausgeben
?>
Hier das Formular (mit Werten) anzeigen
<?php
// ggf. HTML-Footer-Include
?>

Mit den Includes sind mögliche Einbindungen von Funktions- und Klassendefinitionsdateien gemeint bzw. HTML-Ausgaben, die weitgehend unabhängig von der Datenverarbeitung sind (HTML-Header und -Footer).
Falls Daten geändert werden sollen (im Beispiel angedeutet durch Abfragen der booleschen Variable $fillform), muß natürlich das Formular ausgefüllt werden, d.h. entsprechende DB-Abfragen müssen gestartet und ausgewertet werden, so daß danach die Variablen, die die Formularfelder vorbelegen, sinnvolle Werte haben.

Ein schönes Beispiel für die Anwendung des hier gelesenen findet sich übrigens in Kapitel 16.


Wieso ist das Null?!

In obiger Formular-Normalform wird an der Stelle if (<Daten OK>) überprüft, ob die übergebenen Werte korrekt sind. Was genau an dieser Stelle zu tun ist, hängt ganz vom jeweiligen Fall ab. Ein häufiger Denkfehler (im Englischen nennt man das übrigens ,,common pitfall``) dabei ist, daß Werte, die per GET oder POST übermittelt werden, mit dem Integerwert 0 verglichen werden und dabei angenommen wird, daß dies nur wahr wird, wenn die fragliche Variable auch tatsächlich den Wert Null (nicht null) hat. Falsch gedacht! Wer das aktuelle DSP bis zu dieser Stelle aufmerksam gelesen hat, weiß, warum: In Kapitel 8.2.9.1 wurde gesagt, daß jeder String bei normalem Vergleich gleich dem Integerwert Null ist.

if ($_POST['stueckmenge']==0) {
  // Formular für Bestellung ausgeben
} else {
  // Bestellung aufnehmen
}

Da hier nicht mittels === auf Typgleichheit überprüft wurde, wird das Formular immer ausgegeben werden (d.h. die Abfrage ist in dieser Form nutzlos) - POST- und GET-Werte sind immer Strings...

Christoph Reeg