LEARN TO CODE | PHP | 02 PAGES AND FORMS
02.3 Validating input and escaping output
Decide whether submitted data is acceptable, report errors clearly and display values without treating them as HTML markup.
01 | TWO DIFFERENT QUESTIONS
Continue using the PHP development server from 01.1. Create the files below inside one practice folder, preserving their filenames and subfolders. In VS Code open that folder, then start
php -S 127.0.0.1:8000
in its terminal and open the named page through HTTP.
These are complete local examples for PHP 8. Text inputs and output are treated deliberately, but the examples are not a login system, a production deployment or a saved registration service. Where a page previews a submission only, nothing is sent or stored.
Validation asks whether a value meets the application’s rules. Output escaping asks how to represent a value safely in a particular output context.
A name containing an apostrophe may be perfectly acceptable. It still needs appropriate encoding when placed into HTML. Conversely, an escaped year of 500 is still an invalid year for a form allowing 7 to 13.
02 | DEFINE THE RULES FIRST
| Field | This exercise’s rule | What it does not establish |
|---|---|---|
| Name | Trimmed, 1–100 bytes | A person’s real identity |
| Passes FILTER_VALIDATE_EMAIL | Ownership or deliverability | |
| Year | Integer value, 7–13 inclusive | That the person actually attends that year |
| Club | One known option key | That a registration was saved |
The name limit uses strlen, which counts bytes, not user-perceived characters. We state that explicitly rather than promise a 100-character multilingual policy. A real product can choose an appropriate Unicode-aware length rule.
03 | CHECK THE INPUT SHAPE FIRST
Start with known expected keys. For each, reject a non-string submitted value rather than passing an array into trim or a string function.
Defaulting a missing value to '' lets the same field rules handle omissions. Trimming outer whitespace is a deliberate normalisation step; it does not prove the remaining content is trustworthy.
A fixed dropdown still needs an allow-list check. Someone can submit an option the HTML never offered.
04 | COMPLETE VALIDATED FORM
Use this shared helper wherever the guide requests helpers.php. It escapes strings for HTML text and quoted ordinary HTML attribute values; it is not a universal encoder for every context.
helpers.php
<?php
function escapeHtml(string $text): string
{
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}
signup.php
<?php
require_once __DIR__ . '/helpers.php';
$values = ['name' => '', 'email' => '', 'year' => '', 'club' => ''];
$clubs = ['robotics' => 'Robotics', 'art' => 'Art & Design'];
$errors = [];
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
foreach ($values as $key => $unused) {
$raw = $_POST[$key] ?? '';
if (!is_string($raw)) {
$errors[] = 'Each field must contain a single text value.';
} else {
$values[$key] = trim($raw);
}
}
if ($values['name'] === '' || strlen($values['name']) > 100) {
$errors[] = 'Enter a name of 1 to 100 bytes.';
}
if (filter_var($values['email'], FILTER_VALIDATE_EMAIL) === false) {
$errors[] = 'Enter a valid email address format.';
}
$year = filter_var($values['year'], FILTER_VALIDATE_INT,
['options' => ['min_range' => 7, 'max_range' => 13]]);
if ($year === false) {
$errors[] = 'Enter a whole-number year level from 7 to 13.';
}
if (!array_key_exists($values['club'], $clubs)) {
$errors[] = 'Choose a listed club.';
}
$success = !$errors;
}
?>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Validate a club request</title></head>
<body>
<h1>Club request preview</h1>
<p>This exercise previews a request. It does not save or send it.</p>
<?php if ($errors): ?>
<ul role="alert">
<?php foreach ($errors as $error): ?><li><?= escapeHtml($error) ?></li><?php endforeach; ?>
</ul>
<?php endif; ?>
<form action="signup.php" method="post">
<p><label for="name">Name : </label>
<input id="name" name="name" required value="<?= escapeHtml($values['name']) ?>"></p>
<p><label for="email">Email : </label>
<input id="email" name="email" type="email" required value="<?= escapeHtml($values['email']) ?>"></p>
<p><label for="year">Year level : </label>
<input id="year" name="year" type="number" min="7" max="13" required value="<?= escapeHtml($values['year']) ?>"></p>
<p><label for="club">Club : </label>
<select id="club" name="club" required>
<option value="">Choose a club</option>
<?php foreach ($clubs as $key => $label): ?>
<option value="<?= escapeHtml($key) ?>" <?= $values['club'] === $key ? 'selected' : '' ?>><?= escapeHtml($label) ?></option>
<?php endforeach; ?>
</select></p>
<button type="submit">Preview request</button>
</form>
<?php if ($success): ?>
<h2>Validated preview</h2>
<p><?= escapeHtml($values['name']) ?> requested <?= escapeHtml($clubs[$values['club']]) ?>.</p>
<?php endif; ?>
</body>
</html>
05 | VALIDATE WITHOUT DESTROYING THE INPUT
The processor stores the current field values, builds an errors list and declares success only when no errors remain. The HTML then displays errors and reuses the entered values.
filter_var with FILTER_VALIDATE_INT returns the integer or false for this usage. Compare with === false so a legitimate zero is not accidentally confused with failure in a different numeric range.
Our permitted year range excludes zero anyway, but the strict comparison expresses the correct general distinction.
06 | ALLOW-LISTS LIMIT THE MEANING
The club field sends a key such as robotics. PHP checks that it exists in the server’s known $clubs array, then uses the corresponding label.
This prevents an unexpected key from being treated as a valid club. It does not grant permission or check available places; those are separate requirements.
Use a fixed mapping for known choices instead of trusting submitted display labels or deciding validity solely from what the browser showed.
07 | ESCAPE AT THE OUTPUT POINT
Keep ordinary values as data, then escape when placing them into HTML. The helper transforms special characters into HTML representations so the browser treats them as text.
The example escapes both paragraph content and values in quoted input attributes. It also escapes option values and labels. The selected attribute is a fixed string chosen by a server-side comparison, not raw submitted markup.
Do not store a pre-escaped HTML version as the only copy of every value. A different destination may need different handling, and repeated escaping can produce confusing display.
08 | CONTEXT STILL MATTERS
htmlspecialchars is appropriate for the ordinary HTML text and quoted attribute contexts shown here. It does not by itself validate a URL scheme or make text safe inside a JavaScript program, CSS expression or SQL statement.
For database values, use parameterised queries; for allowed URL destinations, apply appropriate validation and URL construction. Learn each boundary when you introduce that feature.
Escaping is also different from stripping tags. Data can contain meaningful angle brackets that should be displayed as text rather than silently deleted.
09 | TEST THE RULE, NOT JUST THE BUTTON
| Test | Expected |
|---|---|
| Valid fields and year 7 or 13 | Validated preview |
| Year 6 or 14 | Range error |
| Year 10.5 | Integer error |
| Unknown club key | Choice error |
| Whitespace-only name | Name error |
| Array submitted as name | Shape error, no crash |
| Name containing angle brackets | Displays as text if other rules pass |
| Email-like text that fails validation | Email-format error |
Use fictitious example data. The page does not mail or save a request. Testing the preview is not evidence of a completed registration workflow.
10 | INDEPENDENT PRACTICE
TRY IT YOURSELF
Add an optional short comment field. Choose a byte-length limit, preserve its value after errors and display it in the preview as ordinary text.
Reveal guidance
Add comment to the values map, include a textarea with escaped content, validate the chosen length using the same clearly stated byte rule, and escape it again at display. Do not reject all punctuation merely to avoid understanding output encoding.
Next, we will remember a small choice across requests using server-side session state.
KEEP LEARNING
Previous page 02.4 Sessions and cookies
Official references: Includes and control structures · POST values · HTML escaping · Sessions · Cookies