8. Validatie in PHP

Je wil niet dat er foute gegevens – door onzorgvuldigheid van gebruikers of kwade wil van hackers en spammers – in je database terechtkomen en aangezien PHP meestal zorgt voor de verbinding met de database, is het PHP-code die moet zorgen voor de laatste controle van de ingevulde gegevens.

Enkele technieken kwamen al eerder ter sprake:

8.1. $_SERVER["PHP_SELF"]

Dikwijls wordt de PHP-code op dezelfde pagina als het formulier zelf verwerkt, zodat foutboodschappen op dezelfde pagina verschijnen.
In de form-tag komt dan:
<form method="post" action="<?php echo $_SERVER["PHP_SELF"];?>">

Dit kan echter door hackers misbruikt worden: normaal wordt PHP_SELF vervangen door de URL van de pagina, maar hackers kunnen daar een ‘/’ en kwaadwillige code aan toevoegen, zie bv. op W3schools

Dit is een vorm van XSS, Cross Site Scripting, waarbij hackers client-side scripts in de pagina laten insluipen.

Oplossing: zorg ervoor dat er geen HTML- of JavaScript-code kan doorgegeven worden met de functie htmlspecialchars().
Die vervangt HTML-karakters zoals < en > door &lt; en &gt;

Praktisch wordt de form-tag:
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

Een andere mogelijkheid is om verschillende validatiemethodes samen in een functie te zetten:

Een voorbeeld:
if ($_SERVER["REQUEST_METHOD"] == "POST") {
  $name = test_input($_POST["naam"]);
  $email = test_input($_POST["email"]);
  $website = test_input($_POST["url"]);
  ...
}
function test_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

Met $_SERVER["REQUEST_METHOD"] kan je testen of het formulier verzonden werd: dan is de request method = POST, anders is ze leeg.

8.2. Lege velden

Als je geen JavaScript-validatie op lege velden ingebouwd hebt, dan zal je dat in PHP moeten doen.
Bijvoorbeeld:
if (empty($_POST["naam"]))
  $naamErr = “Naam invullen AUB!”;

En in het formulier moeten we dan wel – zo nodig – de fout zichtbaar maken, bv:
<input type="text" name="naam" /> <span class="error">* <?php echo $naamErr;?></span>

Je kan ook een naamveld checken met een reguliere expressie, om te zien of het enkel letters en spaties bevat, maar voor vele Europese namen is dat helaas niet voldoende: daar kan je immers ook accenten tegenkomen.
$naam = test_input($_POST["naam"]);
if (!preg_match("/^[a-zA-Z ]*$/",$naam))
  $naamErr = "Enkel letters en spaties zijn toegelaten";

8.3. Filter_var()

De functie filter_var($var, filter, [opties]) controleert of de opgegeven variabele voldoet aan de filtervoorwaarden.
Ze is vooral nuttig om een e-mailadres te controleren:
$email = test_input($_POST["email"]);
if (!filter_var($email, FILTER_VALIDATE_EMAIL))
  $emailErr = "Invalid email format";

Ook hier heb je een filter FILTER_SANITIZE_STRING die enkel letters, cijfers en spaties toelaat.
Of nuttiger voor ons, een FILTER_VALIDATE_INT

8.4. Oeps, alle velden leeg!

Als je een formulier via PHP controleert en de gebruiker na foute invoer terugstuurt naar het formulier, zijn alle tevoren ingevulde velden terug standaard, leeg dus.

Dat kan je vermijden door in het formulier zelf de waarden uit de PHP-variabelen terug te tonen:
<input type="text" name="naam" value="<?php echo $naam;?>”>

Of bv. bij een checkbox:
<input type="radio" name="Regime" <?php if (isset($Regime) && $regime=="ko") echo "checked";?> value="ko" id="KO" />

Uiteraard wordt je formuliercode er wel niet duidelijker op ...

8.5. Exception handling

Net zoals in Java en JavaScript, zullen we in PHP de code waarin een fout, exceptionele situatie, exception, kan optreden in een try-blok zetten en de fout opvangen, catch, in een catch-blok.

Je kan ook eigen exceptions 'opwerpen' en opvangen.
Een eenvoudig voorbeeld:
<?php
  $a = 5;
  $b = 0;
  try {
    if($b == 0)
      throw new Exception("Delen door nul mag niet!");
    $resultaat = $a / $b;
  }
  catch (Exception $ex) {
    print "<p>Er is een fout opgetreden: { $ex->getMessage() }</p>";
    print "<p>{ $ex->getfile() }: { $ex->getline() }</p>";
  }
?>

Dit geeft als uitvoer:
Er is een fout opgetreden: Delen door nul mag niet!
c:\[pad]\exception.php: 9

Ook de voorgedefinieerde functies van PHP werpen exceptions of errors op.

8.6. Reguliere expressies

Een reguliere expressie of regular expression beschrijft een patroon van tekens. Zulke expressies worden veel gebruikt om op te zoeken of om tekenreeksen op juist formaat te controleren.

Enkele veel voorkomende symbolen en tekenreeksen:

^begin van de regel
$einde van de regel
a?nul of één 'a'
a+tenminste één 'a'
a*nul of meer a's
a{3}3 keer de letter 'a' – herhaling met { } kan je op alle expressies toepassen
a{1,3}1, 2 of 3 keer de letter 'a'
.de punt staat voor een willekeurig teken
\.een punt – om speciale tekens op te nemen in een regex, gebruik je \ als escapeteken
[bdf]een 'b' of een 'd' of een 'f'
[a-z]een kleine letter (a tot z)
[A-Z]een hoofdletter
[A-z]een letter, hoofdletter of kleine letter
[0-9] of \deen cijfer van 0 tot 9
[^0-9]alles wat geen cijfer is
[0-9]{3}drie cijfers na elkaar
[rood|groen|blauw]het woord 'rood' of 'groen' of 'blauw'
\switruimte: een spatie, tab, line feed of return

Er zijn twee functies die veel gebruikt worden in dit verband:

8.7. CAPTCHA

CAPTCHA is de afkorting van Completely Automated Public Turing Test to Tell Computers and Humans Apart. Met andere woorden: een test om vast te stellen dat een formulier door een mens ingevuld wordt en niet door een "spam bot".

Zowat de meest gebruikte test is een afbeelding met willekeurige letters op een niet-effen achtergrond, letters die door de gebruiker nagetypt moeten worden. Die achtergrond is nodig om OCR – optical character recognition – door bots onmogelijk te maken.

Om bv. 6 willekeurige kleine letters te genereren kan je volgende lus gebruiken:
$letters = "";
for($i = 0; $i < 6; $i++) {
  $letters .= chr(rand(97, 122));
}

Verder bevat PHP een grafische bibliotheek GD (graphics draw) waarmee dynamisch afbeeldingen gegenereerd kunnen worden.
Voor PHP onder Windows, controleer even dat in php.ini volgende optie actief is: extension=php_gd2.dll

Vervolgens kan je dit voorbeeld bestuderen en proberen.