Treating forms, validation, and flash messages as a system. The intro series showed basic $_POST handling. Here we validate properly, repopulate fields on error, and show one-time messages after redirect.
Post/Redirect/Get
After a successful POST, redirect to a GET URL. That stops the browser warning about resubmitting the form when the user refreshes.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$errors = validateNote($_POST);
if ($errors) {
$_SESSION['errors'] = $errors;
$_SESSION['old'] = $_POST;
header('Location: /notes/create');
exit;
}
saveNote($_POST);
$_SESSION['flash'] = 'Note saved.';
header('Location: /notes');
exit;
}
Validation helper
Keep rules in one place. Return an array of field => message errors:
function validateNote(array $input): array
{
$errors = [];
$title = trim($input['title'] ?? '');
if ($title === '') {
$errors['title'] = 'Title is required.';
} elseif (mb_strlen($title) > 120) {
$errors['title'] = 'Title must be 120 characters or fewer.';
}
$body = trim($input['body'] ?? '');
if ($body === '') {
$errors['body'] = 'Body is required.';
}
return $errors;
}
Flash messages
Store a message in the session, show it once on the next page, then remove it:
function flash(string $key = 'flash'): ?string
{
if (empty($_SESSION[$key])) {
return null;
}
$message = $_SESSION[$key];
unset($_SESSION[$key]);
return $message;
}
Repopulate old input
function old(string $field, string $default = ''): string
{
$value = $_SESSION['old'][$field] ?? $default;
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
// In template after failed validation:
// <input name="title" value="<?= old('title') ?>">
Clear $_SESSION['old'] after the form renders so stale data does not linger.
CSRF token (basics)
Every state-changing form should include a token stored in the session and checked on POST. We go deeper in advanced security; the habit starts here:
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
// In form:
// <input type="hidden" name="csrf" value="<?= $_SESSION['csrf'] ?>">
// On POST:
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
http_response_code(403);
exit('Invalid token');
}
Validation is not optional polish. Bad data in the database is harder to fix than catching it at the door.

