Gebruikersgegevens verwerken
- Formulieren
- Een GET-querystring
$_GET
en$_POST
GET
versusPOST
- Een formulier en zijn verwerking in één bestand
- Sessions
header('Location: pagina.html')
- Een formulier en zijn verwerking in twee bestanden
- Feedback voor de gebruiker
Formulieren
Formulieren hebben we eerder bij HTML besproken. Maar nu wordt het menens. Tot nu toe konden we ze bouwen, nu gaan we ze gebruiken. Even ter herinnering een typisch formulier met alles erop en eraan.
<form method="get" action="inschrijven.php">
<label for="naam">Naam: </label>
<input type="text" name="naam" id="naam"><br>
<label for="vak">Vak: </label>
<select name="vak" id="vak">
<option value="">...</option>
<option value="fat">FAT</option>
<option value="spb">SPB</option>
<option value="dbsql">DBSQL</option>
<option value="wtux">WTUX</option>
</select><br>
<label for="opmerking">Opmerkingen: </label>
<textarea name="opmerking" id="opmerking"></textarea><br>
<input type="submit" name="verzenden" value="Verzenden">
</form>
-
We hebben een
<form>
-element en daarbinnen verschillende formulierelementen zoals<input>
in verschillende smaken,<select>
,<textarea>
. -
<form>
heeft eenmethod
-attribuut. De mogelijke waarden hiervan zijnget
enpost
.get
is al de standaardwaarde, dus hoef je niet op te geven. -
<form>
heeft ook eenaction
-attribuut. Hierin wordt vastgelegd, welk bestand opgeroepen wordt nadat op ‘Verzenden’ of ‘Submit’ is geklikt. -
Alle formulierelementen hebben een
name
-attribuut. Metname
wordt vastgelegd onder welke naam de stukjes informatie die zijn ingevuld, verstuurd worden. -
Sommige elementen hebben ook een
value
-attribuut. Dat is de waarde die er of standaard is ingevuld of bij een keuzelijst bijvoorbeeld als mogelijk antwoord opgestuurd wordt.
Een GET-querystring
Als we bij <form method="get">
instellen (of beter nog, method
niet zelf instellen), kunnen we (en iedere bezoeker) in de URL lezen wat we bij welk veld (het name
-attribuut geeft dit aan) ingevuld hebben en op deze manier naar de server verstuurd wordt.
Bij ons voorbeeldformulier zou dit zoiets kunnen zijn:
localhost/inschrijven.php?naam=Pieter&vak=wtux&opmerking=Dit%20wil%20ik%20leren&verzenden=Verzenden
- We zien
inschrijven.php
, het bestand dat bijaction
stond ingevuld. - Het vraagteken daarna geeft aan dat hierachter de gegevens uit het formulier komen te staan.
- We zien daarna telkens een sleutel (
naam
,vak
,opmerking
,verzenden
) en een bijbehorende waarde (Pieter
,wtux
,Dit wil ik leren
,Verzenden
). - De verschillende sleutel/waardeparen worden door een
&
gescheiden. - Spaties en andere bijzondere tekens moeten door een procentcode worden vervangen.
Hierboven zien we steeds
%20
waar in het formulier een spatie getypt werd.
$_GET
en $_POST
Aan de serverkant kunnen we gegevens die per get
verstuurd zijn met de array $_GET
en gegevens die per post
verstuurd zijn met de array $_POST
uitlezen.
Deze superglobals worden automatisch ingevuld als een formulier verstuurd is.
GET
De querystring kan je met de volgende broncode verwerken:
print_r($_GET);
$antwoord = "<p>Beste {$_GET['naam']},</p>
<p>Je hebt je opgegeven voor:
<strong>{$_GET['vak']}</strong>
</p>
<p>Opmerking:<br> <em>{$_GET['opmerking']}</em></p>";
$antwoord .= "<p>Bedankt voor je inschrijving!</p>";
echo $antwoord;
- De
print_r()
staat voor ‘print array’ en wordt hier gebruikt om alle binnengekomen gegevens weer te geven. $_GET['naam']
enzovoort zijn elementen van de GET-superglobal-array die nu met hun sleutel (= hetname
-attribuut van het formulierelement) op te vragen zijn.- Het is good practice om HTML-broncode eerst op te bouwen en dan in één keer op de goede plek te echo’en.
POST
Hier nu met method="post"
:
<form method="post" action="aanmelden.php">
<!-- Hier wordt de waarde ingevuld met de datum van vandaag. -->
<input type="hidden" name="datum" value="<?=date('d-m-Y')?>">
<div>
<label for="naam">Naam:</label>
<input type="text" name="naam" id="naam">
</div>
<div>
<label for="password">Wachtwoord:</label>
<input type="password" name="password" id="password">
</div>
<div>
<label for="vak">Afgerond:</label>
<select name="vakken[]" id="vak" multiple>
<option value="fat" selected>FAT</option>
<option value="spb" selected>SPB</option>
<option value="dbsql">DBSQL</option>
<option value="wtux">WTUX</option>
</select>
</div>
<div>
<p>Vooropleiding:</p>
<label for="opleiding_havo">havo</label>
<input type="radio" name="opleiding" id="opleiding_havo" value="havo">
<label for="opleiding_mbo">mbo</label>
<input type="radio" name="opleiding" id="opleiding_mbo" value="mbo">
<label for="opleiding_anders">anders</label>
<input type="radio" name="opleiding" id="opleiding_anders" value="anders" checked>
</div>
<input type="submit" name="verzenden" value="Verzenden">
</form>
Bijzonderheden hier zijn:
- Een
input
-veldtype="hidden"
. Dit is een voor de bezoeker onzichtbaar veld waar door de programmeur gegevens ingevuld kunnen worden. Vanuit de server in een verborgen veld data invullen die niet veranderd mag worden is geen goede praktijk. De waarde kan door de client worden aangepast. Als de datum in voorgaand voorbeeld alleen een suggestie is, verberg dan de datum niet. - een
input
-veldtype="password"
met de beroemde sterretjes die je te zien krijgt als iemand zijn wachtwoord invult. Belangrijk is dat we gegevens die niet zomaar gezien mogen worden niet metmethod="get"
versturen omdat dan alsnog iets in de adresbalk en ook in de geschiedenis van de browser te zien is. - Bij de keuzelijst, het
select
-element kunnen we meerdere keuzes tegelijk maken (attribuutmultiple
). Deze keuzes moeten we dan wel als array meegeven. Dit doen we door bijname="vakken[]"
met vierkante haakjes aan te geven dat dit meerdere waardes zullen zijn.
En dit uitgebreide formulier met de method="post"
kan nu aan de serverkant zo worden verwerkt:
$naam = $_POST['naam'];
$password = $_POST['password'];
$datum = $_POST['datum'];
$opleiding = $_POST['opleiding'];
// Hier wordt een array van vakken binnengehaald.
$vakken = $_POST['vakken'];
$antwoord = "<p>Beste $naam, </p>
<p>Je vooropleiding is:
<strong>$opleiding</strong><br>
De volgende vakken heb je afgerond: <strong>";
// Om alle elementen langs te gaan, gebruiken we `foreach`.
foreach ($vakken as $vak) {
$antwoord .= $vak . ' ';
}
$antwoord .= '</strong></p>';
echo $antwoord;
- Nu is het de
$_POST
-array die op de server ingevuld wordt met alle gegevens van hetpost
-formulier, met telkens weer dename
van elk element als sleutel. - Omdat er meerdere vakken konden worden gekozen en die in een mini-arraytje verwerkt werden, moeten we in de verwerkingsbroncode in PHP op de server ook alle elementen van de array vakken langslopen. Let op! Dit zijn alleen de door de bezoeker geselecteerde opties.
GET
versus POST
Er zijn duidelijke situaties wanneer we GET
als method
kiezen en andere, waar POST
aangewezen is.
Zoals eerder gezegd zullen we wachtwoorden, bankgegevens en dergelijke altijd met POST
versturen.
Overal waar we iets toevoegen (bijvoorbeeld een overschrijving, een bestelling, een registratie) gebruiken we in principe POST
.
Daar waar we iets opvragen/opzoeken, gebruiken we GET
.
POST
voor posten en GET
voor krijgen.
Het grote voordeel van GET
is dat we de URL met de bijbehorende gegevens als hyperlink kunnen bewaren en/of aan iemand doorsturen.
Door een simpele klik krijg je dan hetzelfde resultaat, dat eerder door het invullen van een formulier verkregen is.
Elke hyperlink, met of zonder querystring erachter leidt bij het klikken erop tot een HTTP GET
-request.
HTTP is het protocol dat de communicatie tussen browsers, of clients in het algemeen, en servers regelt.
Een formulier en zijn verwerking in één bestand
Soms is het handig om de verwerking van een formulier op dezelfde pagina te doen. Denk bijvoorbeeld aan een soort rekenmachine. Je wilt het resultaat zien, maar ook het formulier omdat je misschien meteen een andere berekening wilt kunnen uitvoeren. Hieronder een voorbeeld waar of het formulier of de reactie op het ingevulde weergegeven wordt.
<?php
if(!isset($_GET['verzenden'])){
?>
<!-- Let op. Hier begint weer zuivere HTML-broncode. -->
<h3>Graag uw gegevens!</h3>
<form>
<div>
<label for="voornaam">Voornaam:</label>
<input type="text" name="voornaam" id="voornaam">
</div>
<div>
<label for="achternaam">Achternaam: </label>
<input type="text" name="achternaam" id="achternaam">
</div>
<input type="submit" name="verzenden">
</form>
<?php
} else {
$welkom = '<h3>Hartelijk welkom, '. htmlspecialchars($_GET['voornaam']) . ' ' . htmlspecialchars($_GET['achternaam']) . '!</h3>';
echo $welkom;
}
?>
- Met
isset($_GET['verzenden'])
kijken we of iemand via de verzendknop van het formulier of via een gewone paginalink is binnengekomen. - Als er niet op ‘Verzenden’ is geklikt,
!isset()
, laten we het formulier zien, anders genereren we met de ingevulde en opgestuurde gegevens een antwoord. - Hier is ook mooi te zien dat
if
/else
-blokken ook over stukken HTML-broncode heen gebruikt kunnen worden. - De
<form>
-attributenaction
enmethod
zijn niet ingesteld. Bij gebrek aan een waarde vooraction
, stuurt de browser het formulier op naar de URL van de huidige pagina.method
is standaard gelijk aanget
.
Nadeel van verwerken in één bestand
Een belangrijk nadeel van bovenstaande oplossing treedt op wanneer het formulier verstuurd is en de pagina herladen wordt. In dat geval wordt het formulier een tweede keer verwerkt. In bovenstaand voorbeeld is dat natuurlijk geen probleem, maar wanneer de gegevens daadwerkelijk verwerkt worden (denk aan het opslaan van gegevens of het doen van een aankoop in een webwinkel) wil je voorkomen dat er op deze manier per ongeluk twee keer iets gedaan wordt wat maar één keer mag gebeuren. Een oplossing daarvoor wordt verderop in dit hoofdstuk behandeld. Die oplossing wordt gezien als good practice.
Sessions
Sessions zijn een apart verhaal bij een webapplicatie. In het begin van je carrière als webontwikkelaar is dit soms lastig te begrijpen. Het ‘probleem’ bij HTTP is dat het stateless is. Dat wil zeggen dat de server niet bijhoudt wie er bij hem allemaal aan de deur klopt om een pagina op te vragen. Als jij tien keer dezelfde pagina opvraagt zal de server dat in principe niet opmerken. Er komen immers zoveel mensen langs, er is geen beginnen aan.
Het lastige is echter dat de server uit zich zelf helemaal niks bijhoudt. Dus als jij inlogt en naar een andere pagina gaat, weet de server al niet meer wie je bent. Onhandig als jij bij een webshop iets wilt bestellen. Een winkelwagen is dan niet zomaar mogelijk.
Voor dit soort situaties zijn sessions bedacht. Als de programmeur bepaalt dat er met sessies gewerkt moet worden, houdt de server bij wie je bent en kan de server jouw gegevens (bestellingen) over verschillende pagina’s heen vasthouden. De server bewaart dan alle gegevens per sessie (= per bezoeker of client) vijftien minuten. Nadat server vijftien minuten of een andere tijd (die we kunnen instellen) niks van de klant gehoord heeft, vergeet de server deze sessie. De client (browser) bewaart een unieke ID voor de sessie in een cookie. De ID geeft de browser telkens door aan de server, zodat de server de juiste sessiegegevens kan opzoeken.
Fig. 1: Het opbouwen van een sessie in PHP. Een sessie kan in een database worden bewaard maar bijvoorbeeld ook als bestand op de server.
session_start()
Elke pagina die mee wil doen aan de sessie moet in eerste instantie een sessie starten of weer oppikken. De bezoeker krijgt dan een session-ID. Deze wordt gedurende het hele ‘gesprek’ bewaard.
$_SESSION[]
Met session-variabelen die in de superglobal $_SESSION
kunnen worden opgeslagen worden gegevens over pagina’s heen doorgegeven.
Elke pagina die hier aan mee wil doen moet wel eerst een session_start()
-aanroep doen.
Voorbeelden
De volgende stukken broncode staan voor twee pagina’s.
// Pagina 1
<?php
session_start();
if ($_POST['user'] === 'Jan' && $_POST['password'] === "1234") {
$_SESSION['user'] = $_POST['user'];
$_SESSION['paginasBezocht'] = 0;
}
Hier wordt een sessie opgestart of een bestaande weer opgepakt, en er worden sessievariabelen $_SESSION['user']
en $_SESSION['paginasBezocht']
aangemaakt.
<?php
session_start();
if (!isset($_SESSION['user'])){
session_destroy();
header('Location: login.php');
} else {
$_SESSION['paginasBezocht']++;
?>
<!DOCTYPE html>
<html lang="nl">
...
</html>
<?php } ?>
- Dit is een andere pagina, waar een bestaande sessie weer wordt opgepakt of een nieuwe wordt aangemaakt.
- Dan wordt er gecheckt of de sessionvariabele
$_SESSION['user']
bestaat. Als die niet bestaat (iemand komt op deze pagina binnen) wordt de sessie opgeheven en de loginpagina (het vorige broncodevoorbeeld) weergegeven. - Als de
$_SESSION['user']
bestaat, bestaat ook$_SESSION['paginasBezocht']
en wordt deze teller met één verhoogd. Op deze manier kan bijhouden worden hoeveel pagina’s een bezoeker bekijkt of tenminste aanklikt.
header('Location: pagina.html')
Met de functie header()
en de string Location: …
met op de puntjes een werkende URL voor GET-requests, kan de bezoeker naar een andere URL worden omgeleid.
Heel typisch voor sites waar je je moet aanmelden om mee te mogen doen, is dat je naar login.php
wordt omgeleid als je je nog niet hebt aangemeld.
Een formulier en zijn verwerking in twee bestanden
Bij de uitleg van de verwerking van een formulier in één bestand werd al aangegeven dat dat een belangrijk nadeel heeft: bij herladen van de pagina worden de gegevens opnieuw verstuurd. De volgende oplossing, die als good practice wordt beschouwd, lost dat probleem op.
Het formulier: formulierpagina.php
<?php
// De sessie wordt gestart.
session_start();
// In het verwerkingsbestand wordt deze sessievariabele gezet.
if (isset($_SESSION['voornaam'])) {
$welkom = '<h3>Hartelijk welkom, ' . htmlspecialchars($_SESSION['voornaam']) . ' ' . htmlspecialchars($_SESSION['achternaam']) . '!</h3>';
echo $welkom;
} else {
?>
<form method="post" action="verwerk.php">
<div>
<label for="voornaam">Voornaam:</label>
<input type="text" name="voornaam" id="voornaam">
</div>
<div>
<label for="achternaam">Achternaam: </label>
<input type="text" name="achternaam" id="achternaam">
</div>
<input type="hidden" name="oorsprong" value="formulierpagina">
<input type="submit" name="verzenden">
</form>
<?php
}
// De sessievariabelen weer opruimen:
unset($_SESSION['voornaam']);
unset($_SESSION['achternaam']);
?>
De verwerking: verwerk.php
Naar dit bestand wordt verwezen in het bovenstaande bestand formulierpagina.php
.
Dit is geen ‘view’ (een scherm voor de gebruiker), maar een technische pagina.
Je moet dus de bezoeker na het uitvoeren van deze broncode weer terugsturen naar een normale pagina.
<?php
// De sessie wordt gestart of opnieuw opgepakt.
session_start();
// We kopiëren deze naar de sessie zodat de formulierpagina de gegevens kan tonen.
$_SESSION['voornaam'] = $_POST['voornaam'];
$_SESSION['achternaam'] = $_POST['achternaam'];
// Hier zou je iets met de gegevens kunnen doen, zoals ze opslaan in een database.
// Maak zelf de URL aan met een ‘allowlist’.
// Vertrouw niet letterlijk de waarde uit het formulierveld `oorsprong`.
// Daar kan vanalles in staan.
if ($_POST['oorsprong'] === 'formulierpagina') {
// Dit zorgt ervoor dat de browser de bezoeker weer terugstuurt naar een andere pagina.
header('Location: /formulierpagina.php');
}
?>
Feedback voor de gebruiker
Als je als gebruiker een fout hebt gemaakt bij het invullen van het formulier is het handig dat je weet wat er mis is gegaan. Als programmeur kan je dat makkelijk maken voor de gebruiker, door op de volgende dingen te letten:
- Is het veld ingevuld?
Met
isset($_POST['veldnaam'])
- Is het veld niet leeg?
Met
!empty($_POST['veldnaam')]
- Zijn het genoeg velden?
Met
count($_POST)
Als je dan overal waar het misgaat ook nog bijhoudt in bijvoorbeeld een $errors
-array, dan kan je dat makkelijk teruggeven aan de gebruiker.
Zie het volgende voorbeeld.
$fields = [
'username',
'password',
'password_check'
];
$errors = [];
foreach ($fields as $field) {
if (!empty($_POST[$field])) {
// Doe iets met de data.
} else {
$errors[] = $field;
}
}
if (count($errors) > 0) {
foreach ($errors as $error) {
$html .= "<p class=\"error\">{$error} geeft een fout.<p>";
}
}