Codeskill

Learn to code, step by step

Forms, validation, and flash messages as a system

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.

PreviousBuilding a simple router (front controller)