Skip to content
Theory · PHP and the web · lesson 6 of 7

Validating input

Let's go back to the order slip. Anyone could have filled it in: the "quantity" box says "lots", and the "dish" box names something that is not on the menu. A good cook first checks the slip against the menu and only then cooks.

A program needs the same check. Every value from a request was chosen by the visitor, and (int) does not check it: (int) '15kg' quietly gives 15. Input validation is comparing a value with a rule before the program uses it. A value that fails the check is not used, and the visitor is shown an error.

filter_var

The function filter_var checks a string against a ready-made rule. The rule is the second argument: the constant FILTER_VALIDATE_INT means a whole number, FILTER_VALIDATE_EMAIL an email address.

PHP
<?php

var_dump(filter_var('15', FILTER_VALIDATE_INT)); // int(15)
var_dump(filter_var('15kg', FILTER_VALIDATE_INT)); // bool(false)
var_dump((int) '15kg'); // int(15)
var_dump(filter_var('bob@mail.com', FILTER_VALIDATE_EMAIL)); // string(12) "bob@mail.com"
var_dump(filter_var('bob@', FILTER_VALIDATE_EMAIL)); // bool(false)

A whole number

FILTER_VALIDATE_INT checks the whole string. '15' passes and comes back already as the number int(15). In '15kg' letters follow the digits, so the result is false.

A cast with (int) in the same situation would find the digits at the start and quietly give 15.

An email address

FILTER_VALIDATE_EMAIL returns the address itself if it is valid, and false if it is not. 'bob@' has nothing after the @, and the check fails.

The general rule of filter_var: on success it returns the value, on failure false. That is why the result is compared strictly: === false.

An allowlist

Some values do not need a shape check: they are picked from a short list. A sort order is by name or by price, and any other word in ?sort= is a mistake or an attack. An allowlist is a list of permitted values: anything not in it is rejected.

PHP
$_GET = ['sort' => 'date'];
$sort = $_GET['sort'] ?? 'name';
if (!in_array($sort, ['name', 'price'], true)) {
    $sort = 'name';
}
echo $sort, "\n"; // name

The value 'date' is not in the list, so the program takes 'name'. As you already know, the third argument true switches in_array to strict comparison. An allowlist is more reliable than a list of what is forbidden: you cannot list everything dangerous, but what is permitted fits on one line.

This is where the menu picture falls short: the cook has one menu for all orders. The program, though, needs its own rule for each field—a number, an email address or a list.

Checking a form

A visitor often copies an email address together with a space, and FILTER_VALIDATE_EMAIL rejects such an address. So before the check the spaces at the ends are removed with the function trim. It is handy to collect errors in an array to show the visitor all of them at once:

PHP
$_POST = ['email' => ' ann@mail.ru ', 'age' => '15kg'];
$errors = [];
$email = filter_var(trim($_POST['email'] ?? ''), FILTER_VALIDATE_EMAIL);
if ($email === false) {
    $errors[] = 'Check the email';
}
$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT);
if ($age === false) {
    $errors[] = 'Age must be a whole number';
}
echo $email, "\n"; // ann@mail.ru
echo count($errors), ' ', $errors[0], "\n"; // 1 Age must be a whole number

Let's go through it step by step:

  1. trim removes the spaces at the ends, and the address passes the check.
  2. The age '15kg' fails the check, and a message appears in $errors.
  3. An empty $errors means the data can be used. Otherwise the page shows the form again, with the errors above it.

Validation does not replace escaping. The surname O'Brien passes any sensible check, but on output into HTML it still goes through htmlspecialchars. Interviewers like to ask how input validation differs from output escaping: the first decides whether to accept a value, the second how to print it safely.

Common mistake

The result of filter_var is checked with !:

PHP
<?php

$qty = filter_var('0', FILTER_VALIDATE_INT);
if (!$qty) { // ✗
    echo 'Not a number', "\n";
}

The program prints Not a number, although '0' is a valid whole number. filter_var returned int(0), and zero is a falsy value, so !$qty is true. The fix is a strict comparison: if ($qty === false).

After the course

This is the last theory lesson of the course. Sessions, databases and frameworks were not part of the course.

Next, test yourself on the PHP interview simulator on this site. It asks questions on the course topics and on those the course did not touch, and it grades the answers.

Key points

  • Every value from a request was chosen by the visitor, so it is checked before use.
  • filter_var returns the checked value or false: '15kg' with FILTER_VALIDATE_INT gives false, although (int) would give 15.
  • A value from a short set is checked against an allowlist with a strict in_array.
  • The result of filter_var is compared strictly, === false, because 0 is a valid number.