πŸ“

PHP Β· Les 7

Formulieren verwerken

$_GET, $_POST, htmlspecialchars, filter_var validatie en header redirect Β· 30 min

$_GET en $_POST β€” superglobals

PHP heeft ingebouwde superglobale arrays die formulierdata ontvangen. Ze zijn overal beschikbaar, ook in functies.

$_GET

  • β†’ Data in de URL: ?naam=Jan
  • β†’ Zichtbaar in de adresbalk
  • β†’ Geschikt voor zoeken, filteren, paginering
  • β†’ Max. ~2000 tekens
  • β†’ Nooit voor wachtwoorden!

$_POST

  • β†’ Data in de request body
  • β†’ Niet zichtbaar in adresbalk
  • β†’ Geschikt voor formulieren, inloggen
  • β†’ Geen limiet op grootte
  • β†’ Gebruik voor gevoelige data
<?php
// URL: /zoek?term=php&pagina=2

$zoekterm = $_GET["term"]   ?? "";      // "php"
$pagina   = $_GET["pagina"] ?? 1;       // "2" (string!)
$pagina   = (int) $pagina;              // Cast naar int: 2

// POST data na formulier submit
$naam  = $_POST["naam"]  ?? "";
$email = $_POST["email"] ?? "";

// $_SERVER β€” info over het verzoek
$methode = $_SERVER["REQUEST_METHOD"];  // "GET" of "POST"
$url     = $_SERVER["REQUEST_URI"];     // "/contact?ref=home"

// Formulier detect
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // verwerk formulier
} else {
    // toon formulier
}

⚠️ Let op verschil met JS

In JS lees je URL-parameters via new URLSearchParams(location.search). In PHP is $_GET automatisch beschikbaar. JS heeft geen directe toegang tot POST-data zonder fetch/XMLHttpRequest β€” dat doet PHP op de server.

htmlspecialchars β€” XSS voorkomen

Nooit gebruikersinvoer direct in HTML plaatsen. Een aanvaller kan anders JavaScript injecteren (XSS β€” Cross-Site Scripting).

❌ FOUT β€” kwetsbaar voor XSS

// Gebruiker voert in: <script>alert('gehackt!')</script>
echo "Hallo " . $_POST["naam"];  // Script wordt uitgevoerd!

βœ… CORRECT β€” veilig

<?php
$naam = htmlspecialchars($_POST["naam"] ?? "", ENT_QUOTES, "UTF-8");
echo "Hallo " . $naam;
// <script> wordt omgezet naar &lt;script&gt; β€” onschadelijk
<?php
// htmlspecialchars zet gevaarlijke tekens om:
// &  β†’  &amp;
// <  β†’  &lt;
// >  β†’  &gt;
// "  β†’  &quot;  (bij ENT_QUOTES ook ' β†’ &#039;)

// Standaard helper-functie die je kunt hergebruiken
function esc(string $waarde): string {
    return htmlspecialchars($waarde, ENT_QUOTES | ENT_HTML5, "UTF-8");
}

$invoer = '<script>alert("xss")</script>';
echo esc($invoer);
// Toont letterlijk de tekst, geen script

πŸ”— Komt terug in Symfony

In Symfony's Twig-templates is automatische escaping ingeschakeld. {{ gebruiker.naam }} roept automatisch htmlspecialchars() aan. Alleen {{ tekst|raw }} slaat escaping over β€” gebruik dat zorgvuldig.

Validatie met filter_var

<?php
$fouten = [];

// Naam valideren
$naam = trim($_POST["naam"] ?? "");
if (empty($naam)) {
    $fouten[] = "Naam is verplicht.";
} elseif (strlen($naam) < 2) {
    $fouten[] = "Naam moet minimaal 2 tekens bevatten.";
} elseif (strlen($naam) > 100) {
    $fouten[] = "Naam mag maximaal 100 tekens bevatten.";
}

// E-mail valideren
$email = trim($_POST["email"] ?? "");
if (empty($email)) {
    $fouten[] = "E-mail is verplicht.";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $fouten[] = "Ongeldig e-mailadres.";
}

// URL valideren
$website = trim($_POST["website"] ?? "");
if (!empty($website) && !filter_var($website, FILTER_VALIDATE_URL)) {
    $fouten[] = "Ongeldige URL.";
}

// Integer valideren
$leeftijd = filter_var($_POST["leeftijd"] ?? "", FILTER_VALIDATE_INT);
if ($leeftijd === false || $leeftijd < 0 || $leeftijd > 150) {
    $fouten[] = "Ongeldige leeftijd.";
}

// Sanitizen (schoonmaken, niet valideren)
$naam    = filter_var($naam, FILTER_SANITIZE_SPECIAL_CHARS);
$integer = filter_var("42abc", FILTER_SANITIZE_NUMBER_INT); // "42"

if (empty($fouten)) {
    echo "Formulier verwerkt!\n";
} else {
    foreach ($fouten as $fout) {
        echo "βœ— $fout\n";
    }
}

πŸ”— Komt terug in Symfony

Symfony's Validator component doet dit veel eleganter: #[Assert\NotBlank], #[Assert\Email], #[Assert\Length(min: 2, max: 100)] boven entity-properties. Geen handmatige if-checks meer nodig.

HTML-formulier bouwen

Het HTML-formulier stuurt data naar het PHP-script via method="POST" en action="".

<!-- contact.html (of PHP-bestand met HTML) -->
<form method="POST" action="/contact">

  <!-- Tekstveld -->
  <label for="naam">Naam:</label>
  <input type="text" id="naam" name="naam"
         value="<?= htmlspecialchars($oud['naam'] ?? '') ?>"
         required>

  <!-- E-mailveld -->
  <label for="email">E-mail:</label>
  <input type="email" id="email" name="email"
         value="<?= htmlspecialchars($oud['email'] ?? '') ?>"
         required>

  <!-- Selectbox -->
  <label for="onderwerp">Onderwerp:</label>
  <select id="onderwerp" name="onderwerp">
    <option value="algemeen">Algemeen</option>
    <option value="technisch">Technisch</option>
    <option value="factuur">Factuur</option>
  </select>

  <!-- Tekstgebied -->
  <label for="bericht">Bericht:</label>
  <textarea id="bericht" name="bericht" rows="5">
    <?= htmlspecialchars($oud['bericht'] ?? '') ?>
  </textarea>

  <!-- Submit -->
  <button type="submit">Verstuur</button>

</form>

Slimme truc: waarden bewaren na fout

Door value="<?= htmlspecialchars($oud['naam'] ?? '') ?>" te gebruiken, blijft de ingevulde waarde staan na een validatiefout. De gebruiker hoeft niet alles opnieuw in te typen.

Compleet verwerkingsscript + redirect

<?php
// contact.php β€” verwerkt GET (toon) en POST (verwerk)

$fouten = [];
$oud    = [];
$succes = false;

if ($_SERVER["REQUEST_METHOD"] === "POST") {

    // Lees en schoon invoer
    $naam    = trim($_POST["naam"]    ?? "");
    $email   = trim($_POST["email"]   ?? "");
    $bericht = trim($_POST["bericht"] ?? "");

    // Sla op voor terugplaatsen bij fout
    $oud = compact("naam", "email", "bericht");

    // Valideer
    if (empty($naam))    $fouten[] = "Naam is verplicht.";
    if (empty($email))   $fouten[] = "E-mail is verplicht.";
    elseif (!filter_var($email, FILTER_VALIDATE_EMAIL))
                         $fouten[] = "Ongeldig e-mailadres.";
    if (strlen($bericht) < 10)
                         $fouten[] = "Bericht te kort (min. 10 tekens).";

    if (empty($fouten)) {
        // βœ… Verwerk: opslaan in DB, e-mail sturen, etc.
        // mail($email, "Bevestiging", "Ontvangen: $bericht");

        // PRG-patroon: redirect na succesvolle POST
        // Voorkomt dat de gebruiker bij refresh opnieuw submits
        header("Location: /bedankt");
        exit;  // ALTIJD exit na header redirect!
    }
}
?>

<!-- HTML-formulier (vereenvoudigd) -->
<?php foreach ($fouten as $f): ?>
  <p style="color:red"><?= htmlspecialchars($f) ?></p>
<?php endforeach; ?>

<form method="POST">
  <input name="naam"  value="<?= htmlspecialchars($oud['naam']  ?? '') ?>">
  <input name="email" value="<?= htmlspecialchars($oud['email'] ?? '') ?>">
  <textarea name="bericht"><?= htmlspecialchars($oud['bericht'] ?? '') ?></textarea>
  <button>Verstuur</button>
</form>

PRG-patroon β€” Post/Redirect/Get

Na een succesvolle POST altijd redirecten. Zo voorkomt je dat de browser bij vernieuwen het formulier opnieuw instuurt ("Wilt u dit formulier opnieuw versturen?").

πŸ”— Komt terug in Symfony

In Symfony doe je exact hetzelfde maar eleganter: $form->handleRequest($request), if ($form->isSubmitted() && $form->isValid()), dan return $this->redirectToRoute('success'). Symfony's Forms + Validator doen de validatie; jij schrijft alleen de logica.

Sandbox β€” formulierverwerking simuleren

In de sandbox simuleren we POST-data via een PHP-array, omdat PHP-WASM geen echte HTTP-requests verwerkt.

// output

Klik op Uitvoeren...

Kennischeck

Les 7 afronden

Ga door naar klassen en namespaces β€” de laatste les β†’