Forms, Validation, and Safe Input Handling
A reliable form workflow preserves user input, reports useful errors, prevents duplicate submissions, and treats every client-supplied value as untrusted.
The POST/Redirect/GET Pattern
- Display the form with GET.
- Validate and process the submission with POST.
- Store a flash message or errors.
- Redirect to a GET page after success.
This prevents a browser refresh from repeating the last state-changing POST.
A Complete Validation Example
<?php
declare(strict_types=1);
session_start();
$values = [
'name' => trim($_POST['name'] ?? ''),
'email' => trim($_POST['email'] ?? ''),
];
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($values['name'] === '' || mb_strlen($values['name']) > 100) {
$errors['name'] = 'Enter a name between 1 and 100 characters.';
}
if (filter_var($values['email'], FILTER_VALIDATE_EMAIL) === false) {
$errors['email'] = 'Enter a valid email address.';
}
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf_token'] ?? '')) {
$errors['form'] = 'The form expired. Reload the page and try again.';
}
if ($errors === []) {
$_SESSION['flash'] = 'Contact saved successfully.';
header('Location: /contacts');
exit;
}
}
$_SESSION['csrf_token'] ??= bin2hex(random_bytes(32));
?>
<form method="post" action="" novalidate>
<input type="hidden" name="csrf_token"
value="<?= htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
<label for="name">Name</label>
<input id="name" name="name" maxlength="100" required
value="<?= htmlspecialchars($values['name'], ENT_QUOTES, 'UTF-8') ?>">
<?php if (isset($errors['name'])): ?>
<p role="alert"><?= htmlspecialchars($errors['name'], ENT_QUOTES, 'UTF-8') ?></p>
<?php endif; ?>
<label for="email">Email</label>
<input id="email" name="email" type="email" required
value="<?= htmlspecialchars($values['email'], ENT_QUOTES, 'UTF-8') ?>">
<?php if (isset($errors['email'])): ?>
<p role="alert"><?= htmlspecialchars($errors['email'], ENT_QUOTES, 'UTF-8') ?></p>
<?php endif; ?>
<button type="submit">Save contact</button>
</form>
Validation Versus Sanitization
| Action | Purpose | Example |
|---|---|---|
| Normalize | Convert equivalent inputs to a consistent form | Trim whitespace or normalize a phone number |
| Validate | Decide whether input meets the rules | Require a valid date and a positive seat count |
| Escape | Safely render data in a specific output context | Use htmlspecialchars() for HTML text |
File Upload Rules
- Check the upload error code.
- Enforce a server-side size limit.
- Inspect MIME type using
finfo; do not trust the filename extension. - Generate a new storage filename.
- Store uploads outside the public document root or serve through a controlled endpoint.
- Never execute uploaded content.