Menu

PHP Formulare: verarbeiten, validieren, erneut anzeigen

Ein PHP-Formular sendet mit <form method="post"> an sich selbst, und das Skript liest die Felder aus $_POST['name'], sobald $_SERVER['REQUEST_METHOD'] gleich 'POST' ist. Hier: Validierung, Fehlermeldungen neben den Feldern, eingegebene Werte behalten, Checkboxen und Selects und das Escapen der Ausgabe.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Ein PHP-Formular sendet an das Skript zurück, das es ausgegeben hat: Schreib <form method="post"> mit benannten Eingabefeldern, und wenn $_SERVER['REQUEST_METHOD'] gleich 'POST' ist, liest du jedes Feld als $_POST['name']. Escape alles, was du zurückgibst, mit htmlspecialchars().

Führe diesen Block aus, tippe einen Namen ins Formular und klicke auf Send: Dasselbe Skript läuft erneut, diesmal mit gefülltem $_POST.

Hier passieren drei Dinge, die jeder Formular-Handler braucht:

  • Die Prüfung der Methode. Beim ersten Besuch ist die Anfrage ein GET, und es gibt nichts zu verarbeiten. ?? 'GET' spielt nur eine Rolle, wenn die Datei auf der Kommandozeile läuft, wo REQUEST_METHOD nicht gesetzt ist; auf einem Webserver ist es das immer.
  • ?? '' bei jedem Feld. Ein Feld kann fehlen (ein umbenanntes Eingabefeld, eine Anfrage von einem Skript), und einen fehlenden Schlüssel zu lesen gibt eine Warnung aus.
  • Escapen bei der Ausgabe. Der Name kommt zweimal zurück in die Seite, und htmlspecialchars() sorgt dafür, dass ein Wert wie <b>Ada</b> als Text angezeigt wird, statt zu Markup zu werden. Die Details stehen auf der Seite zu htmlspecialchars.

Ein Formular validieren und Fehler neben den Feldern zeigen

Sammle Fehler in einem Array mit dem Feldnamen als Schlüssel. Ist das Array leer, ist das Formular gültig; sonst gibst du das Formular erneut aus, mit jeder Meldung unter ihrem Feld und den Eingaben des Besuchers an Ort und Stelle. Sende es leer ab, dann mit einer ungültigen E-Mail, dann mit gültigen Werten.

novalidate schaltet die eigenen Prüfungen des Browsers ab, damit du die PHP-Prüfungen arbeiten siehst. Behalte in einem echten Formular beide: Die Browser-Validierung ist eine Bequemlichkeit für den Besucher, die PHP-Validierung ist die, die zählt, denn jeder kann eine Anfrage ohne dein Formular senden. filter_var() erledigt die E-Mail und den Ganzzahlbereich mit je einem Aufruf; die Seite zu filter_var behandelt seine anderen Filter.

Checkboxen, Radiobuttons und Select

Ein Textfeld sendet immer einen Wert, auch einen leeren. Die anderen Steuerelemente nicht:

  • Eine nicht angehakte Checkbox sendet gar nichts, also fehlt ihr Schlüssel in $_POST.
  • Checkboxen mit dem Namen topics[] kommen als Array an: $_POST['topics'] ist ['php', 'sql'].
  • Eine Radiogruppe oder ein <select> sendet den value der gewählten Option.

Um die Auswahl nach dem Absenden zu behalten, gibst du bei der passenden Option checked oder selected aus. Hak ein paar Boxen an, wähle eine Stufe und klicke auf Save.

Die zwei Prüfungen nach dem Lesen der Eingabe sind wichtiger, als sie aussehen. array_intersect() verwirft jedes Thema, das nicht auf deiner Liste stand, und die Prüfung mit is_string() plus array_key_exists() setzt die Stufe auf einen Standardwert zurück, wenn jemand einen Wert sendet, den du nie angeboten hast (oder ein Array, bei dem $levels[$level] werfen würde). Ein Select begrenzt, was ein Browser senden kann, nicht, was eine Anfrage enthalten kann.

Mehrstufige Formulare mit versteckten Feldern

Ein verstecktes Eingabefeld trägt einen Wert von einem Schritt zum nächsten, ohne ihn anzuzeigen. Hier fragt Schritt 1 nach einem Namen, Schritt 2 nach einer Sprache und schickt den Namen in einem versteckten Feld mit, und Schritt 3 zeigt beides. Geh alle drei Schritte durch.

Versteckte Felder sind im Seitenquelltext sichtbar und lassen sich wie jedes andere Feld bearbeiten, also sind sie in Ordnung, um die eigenen Antworten des Besuchers zwischen Schritten zu tragen, aber nie für einen Preis, eine Nutzer-ID oder eine Rolle. Behalte so etwas auf dem Server, in einer Session; siehe Sessions und Cookies.

Post/Redirect/Get: das erneute Senden beim Neuladen stoppen

Gibt das Skript nach einem erfolgreichen POST direkt eine Dankeseite aus, bittet ein Neuladen den Browser, den POST erneut zu senden, und die Bestellung oder der Kommentar wird zweimal gespeichert. Die Lösung ist: speichern, weiterleiten und das Ergebnis per GET zeigen:

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $errors = validate($_POST);
    if (!$errors) {
        save_comment($_POST);
        $_SESSION['flash'] = 'Comment posted.';
        header('Location: /comments.php', true, 303);
        exit;
    }
    // On errors, fall through and print the form with the messages.
}

$flash = $_SESSION['flash'] ?? null;
unset($_SESSION['flash']);

303 See Other sagt dem Browser, der Weiterleitung mit einem GET zu folgen. exit nach header() ist wichtig: Ohne läuft der Rest des Skripts weiter. Die Weiterleitung muss außerdem vor jeder Ausgabe passieren, sonst verweigert PHP sie mit „headers already sent“.

Echte Formulare fügen außerdem ein CSRF-Token hinzu: ein Zufallswert, der in der Session und in einem versteckten Feld gespeichert und beim Absenden mit hash_equals() verglichen wird, damit eine andere Website nicht im Namen eines angemeldeten Besuchers an dein Formular senden kann.

Die Validierung ohne Browser testen

Pack die Regeln in eine Funktion, die ein Array nimmt und die Fehler liefert. Die Seite ruft sie mit $_POST auf, und du kannst sie mit jedem beliebigen Array aufrufen, was die Sonderfälle leicht ausprobierbar macht. Füge der Liste deine eigenen Fälle hinzu.

Den vierten Fall vergessen viele. Eine Anfrage kann name[]=x senden, wodurch $_POST['name'] ein Array wird, und ein Array an trim() zu übergeben wirft einen TypeError und stoppt das Skript. Vorher is_string() zu prüfen, macht aus feindlicher Eingabe einen gewöhnlichen Validierungsfehler. Der Cast (string) in den früheren Blöcken vermeidet den TypeError auch, verwandelt das Array aber in das Wort Array und gibt eine Warnung aus, also ist is_string() die sauberere Prüfung.

Typischer Fehler: $_POST lesen, bevor das Formular gesendet wurde

Das häufigste erste Formularskript liest die Felder am Anfang der Datei:

<?php
$name = $_POST['name'];
echo "Hello, $name";

Beim ersten Besuch wurde nichts gesendet, also gibt PHP aus:

Warning: Undefined array key "name" in /var/www/form.php on line 2

Die Lösung ist das Muster aus dem ersten Block: Verarbeite das Formular nur, wenn die Anfrage ein POST ist, und lies jedes Feld mit einem Standardwert, $_POST['name'] ?? ''. Erscheint die Warnung nach dem Absenden, vergleiche das Attribut name im HTML mit dem Schlüssel in PHP: name="Name" und $_POST['name'] sind verschiedene Schlüssel.

Häufig gestellte Fragen

Wie sende ich ein PHP-Formular an dieselbe Seite?

Schreib <form method="post"> ohne action (oder mit action=""). Der Browser sendet an die aktuelle URL zurück, also zeigt dasselbe Skript das Formular und verarbeitet es: Prüfe $_SERVER['REQUEST_METHOD'] === 'POST' und lies die Felder aus $_POST.

Wie behalte ich in PHP Formularwerte nach dem Absenden?

Gib den gesendeten Wert escapt wieder ins Feld aus: <input name="email" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">. Bei einer Checkbox gibst du checked aus und bei einer Select-Option selected, wenn der gesendete Wert passt.

Wie bekomme ich in PHP die Werte mehrerer Checkboxen?

Gib den Checkboxen denselben Namen mit eckigen Klammern am Ende, name="topics[]". PHP legt die angehakten Werte dann in einem Array ab, $_POST['topics']. Nicht angehakte Boxen senden nichts, also verwendest du $_POST['topics'] ?? [] und prüfst jeden Wert gegen die angebotene Liste.

Warum sendet ein Neuladen der Seite mein PHP-Formular erneut?

Die letzte Anfrage war ein POST, also wiederholt der Browser ihn beim Neuladen. Leite nach einem erfolgreichen Absenden mit header('Location: /thanks.php'); exit; weiter (das Muster Post/Redirect/Get). Das Neuladen wiederholt dann ein harmloses GET.

Ist $_POST in PHP sicher?

Nein, jeder Wert in $_POST kommt vom Besucher und kann alles sein, auch HTML, SQL oder ein Array, wo du einen String erwartet hast. Validiere ihn beim Hereinkommen (filter_var, Prüfungen auf Länge und erlaubte Werte) und escape ihn beim Hinausgehen (htmlspecialchars für HTML, Prepared Statements für SQL).

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

LOS GEHT'S