Practice 9 - PHP: POST Forms and Input Validation
Introduction
Last session we handled a simple two-field form using GET. This session covers:
POSTforms and when to use them instead ofGETtrim()— removing accidental whitespace from user input- Validating different input types: email, word count, password length, non-negative integer, checkbox
- Sticky forms — preserving the user’s input values after a validation failure
- The ternary operator and chained null coalescing (
??)
1. GET vs POST
Both methods submit form data to the server. The difference is where the data travels:
GET | POST | |
|---|---|---|
| Data location | URL query string (?a=1&b=2) | Request body (not visible in URL) |
| Bookmarkable | Yes | No |
| Use for | Read-only queries, searches | Any form that submits or modifies data |
| PHP superglobal | $_GET | $_POST |
For a login form, a registration form, or any form where you collect sensitive data, always use POST:
<form method="POST" action="task1.php">
...
</form>
On submission, $_POST is populated just like $_GET was before:
if ($_POST) {
$email = $_POST['email'];
}
if ($_POST) is truthy when the superglobal array is non-empty, i.e. when the form was submitted. An empty array [] is falsy in PHP.
2. trim — removing whitespace
Users often accidentally type a leading or trailing space. trim removes it:
$email = trim($_POST['email']);
// " a@a.com " -> "a@a.com"
Always trim text inputs before validating them. A correct email with a trailing space would otherwise fail validation.
3. Validating an email address
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors["email"] = "The email you entered is not valid";
}
FILTER_VALIDATE_EMAIL checks that the string matches the expected format (user@domain.tld). As with numbers, use === false so that a legal email that happens to be falsy-looking is not rejected.
4. Validating word count — explode + count
$twoWords = trim($_POST['two_words']);
$words = explode(" ", $twoWords);
if (count($words) < 2) {
$errors["two_words"] = "The input cannot consist of less than 2 words";
}
explode(" ", $str) splits the string on every space and returns an array. If the result has fewer than 2 elements, fewer than 2 words were entered.
This is a minimal check. A production validator would also handle multiple consecutive spaces (e.g. using preg_split('/\s+/', trim($str))). For a classroom exercise, single-space splitting is sufficient.
5. Validating password length — strlen
$password = trim($_POST['password']);
if (strlen($password) < 7) {
$errors["password"] = "Your password must be at least 7 characters.";
}
strlen returns the number of bytes in the string (for ASCII passwords this equals the character count).
JavaScript equivalent:
if (password.length < 7) { ... }
6. Validating a non-negative integer
Two rules in sequence: first check that the value is a valid integer, then check the sign.
$num = trim($_POST['non-negative-number']);
if (filter_var($num, FILTER_VALIDATE_INT) === false) {
$errors["non-negative-number"] = "This is not a valid number";
} else if ($num < 0) {
$errors["non-negative-number"] = "Must not be a negative number";
}
PHP automatically coerces the string "5" to the integer 5 in the comparison $num < 0, so no explicit cast is needed here.
7. Handling checkboxes
Checkboxes are different from text inputs: if the checkbox is unchecked, the browser does not send the field at all — the key will be absent from $_POST.
Use the null coalescing operator to safely read it:
$tickme = $_POST['tickme'] ?? false;
if ($tickme != "on") {
$errors["tickme"] = "Must be checked";
}
When a checkbox is checked, the browser sends the string "on" as its value (unless a different value attribute is set). When unchecked, the key is missing, so $_POST['tickme'] would throw a notice — ?? false catches that and gives a safe default.
8. The ternary operator
PHP has the same ternary as JavaScript:
echo $condition ? "yes" : "no";
// condition ? value_if_true : value_if_false
Example — re-checking the checked state for the checkbox:
echo ($tickme ?? false) == "on" ? "checked" : "";
The parentheses around ($tickme ?? false) are important: ?? has lower precedence than ==, so without them PHP would evaluate false == "on" first.
9. Chained ?? — multiple fallbacks
?? chains: each operand is tried left to right; the first one that is set and not null wins.
echo $errors['email'] ?? $defaultEmail ?? "I cannot fallback to any emails";
- If
$errors['email']is set → use it - Else if
$defaultEmailis set → use it - Otherwise → use the final string literal
JavaScript equivalent:
errors.email ?? defaultEmail ?? "I cannot fallback to any emails"
10. Sticky form values
After a validation failure, the form is re-displayed with the user’s previous input pre-filled. Without this, every error forces the user to retype everything.
Use the short echo tag and ?? to either echo the submitted value or fall back to an empty string:
<input type="text" name="email"
value="<?= $email ?? "" ?>" />
- On first visit (
$_POSTis empty) →$emailis undefined → falls back to"" - After a failed submission →
$emailholds the trimmed submitted value → it is echoed into the attribute
For the checkbox, you need to conditionally add the checked attribute:
<input type="checkbox" name="tickme"
<?= ($tickme ?? false) == "on" ? "checked" : "" ?> />
11. Commenting in PHP
// Single line comment
# Also a single line comment (less common)
/*
Multi-line
comment
*/
Complete task1.php
<?php
$errors = [];
if ($_POST) {
$email = trim($_POST['email']);
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors["email"] = "The email you entered is not valid";
}
$twoWords = trim($_POST['two_words']);
$words = explode(" ", $twoWords);
if (count($words) < 2) {
$errors["two_words"] = "The input cannot consist of less than 2 words";
}
$password = trim($_POST['password']);
if (strlen($password) < 7) {
$errors["password"] = "Your password must be at least 7 characters.";
}
$nonNegativeNumber = trim($_POST['non-negative-number']);
if (filter_var($nonNegativeNumber, FILTER_VALIDATE_INT) === false) {
$errors["non-negative-number"] = "This is not a valid number";
} else if ($nonNegativeNumber < 0) {
$errors["non-negative-number"] = "Must not be a negative number";
}
$tickme = $_POST['tickme'] ?? false;
if ($tickme != "on") {
$errors["tickme"] = "Must be checked";
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Form:</h1>
<form method="POST" action="task1.php">
E-mail:
<input type="text" name="email" value="<?= $email ?? "" ?>" />
<?= $errors['email'] ?? "" ?>
<br>
At least two words:
<input type="text" name="two_words" value="<?= $twoWords ?? "" ?>" />
<?= $errors['two_words'] ?? "" ?>
<br>
Password:
<input type="password" name="password" />
<?= $errors['password'] ?? "" ?>
<br>
Non-negative number:
<input type="text" name="non-negative-number"
value="<?= $nonNegativeNumber ?? "" ?>" />
<?= $errors['non-negative-number'] ?? "" ?>
<br>
<input type="checkbox" name="tickme"
<?= ($tickme ?? false) == "on" ? "checked" : "" ?> />
Accept the terms & conditions
<?= $errors['tickme'] ?? "" ?>
<br>
<button type="submit">Send form</button>
</form>
</body>
</html>
Summary
| Concept | PHP |
|---|---|
| POST form | <form method="POST"> → $_POST["name"] |
| Strip whitespace | trim($str) |
| Validate email | filter_var($v, FILTER_VALIDATE_EMAIL) === false |
| Split by space | explode(" ", $str) |
| String length | strlen($str) |
| Validate integer | filter_var($v, FILTER_VALIDATE_INT) === false |
| Checkbox value | $_POST['name'] ?? false → "on" when checked, absent when not |
| Ternary | $cond ? $a : $b |
| Chained fallback | $a ?? $b ?? $c |
| Sticky input value | value="<?= $var ?? "" ?>" |
| Sticky checkbox | <?= ($v ?? false) == "on" ? "checked" : "" ?> |
| Comment styles | //, #, /* */ |