Forms & User Input
In real apps, forms are everywhere: login, search, product create/edit, contact us, uploads. This page explains GET vs POST, validation, sticky forms, CSRF protection, the Post-Redirect-Get (PRG) pattern, and file uploads — with working code.
1) GET vs POST (When to use which?)
| Use GET when… | Use POST when… |
|---|---|
| Reading data; search filters; pagination; sharable URLs; no sensitive data. | Changing data (create/update/delete); sensitive data; file uploads; complex forms. |
Mini GET example
<?php
// URL: /search.php?q=php
$q = filter_input(INPUT_GET, 'q', FILTER_SANITIZE_SPECIAL_CHARS) ?? '';
echo "Results for: " . $q;
?>
2) Anatomy of a Robust Form
Browser submits form → PHP validates → On error: re-render with messages (sticky values)
↘ On success: do action, set flash, PRG redirect
| Concept | Why it matters | How |
|---|---|---|
| Sticky fields | Don’t force users to retype on error | Keep an $old array; echo values back |
| Error array | Show field-specific messages | $errors['email'] = 'message' |
| Server-side validation | Cannot be bypassed | Use filter_var, regex, custom rules |
| CSRF token | Blocks cross-site form submits | Token in session + hidden input + hash_equals |
| PRG | Prevents duplicate submit on refresh | header('Location: ...') after success |
3) Validation Patterns (Copy-paste)
3.1 Required text (min length)
<?php
$name = trim($_POST['name'] ?? '');
if ($name === '' || mb_strlen($name) < 2) {
$errors['name'] = 'Name must be at least 2 characters.';
}
?>
3.2 Email
<?php
$email = trim($_POST['email'] ?? '');
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors['email'] = 'Enter a valid email address.';
}
?>
3.3 Custom regex (Indian mobile)
<?php
$mobile = trim($_POST['mobile'] ?? '');
if ($mobile !== '' && !preg_match('/^[6-9]\d{9}$/', $mobile)) {
$errors['mobile'] = 'Enter a valid 10-digit mobile number.';
}
?>
3.4 PRG (Post-Redirect-Get) skeleton
<?php
if ($method === 'POST' && empty($errors)) {
// save or send email here
$_SESSION['flash_success'] = 'Saved successfully.';
header('Location: form.php');
exit;
}
?>
4) CSRF Protection (Explained)
CSRF = another site makes the user’s browser submit your form. Defense: a random token stored in the session and verified on POST.
<?php
// Once per session
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(32));
}
?>
<!-- in form -->
<input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
<?php
$token = $_POST['csrf'] ?? '';
if (!hash_equals($_SESSION['csrf'], $token)) {
die('CSRF failed');
}
?>
5) Working Demo: Contact Form (Sticky + Errors + CSRF + PRG)
6) File Uploads (Secure Pattern)
Always check: (1) upload error code, (2) file size, (3) real MIME using finfo,
(4) generate a safe random filename, and (5) store outside webroot if possible.
6.1 HTML
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
<input type="file" name="avatar" accept="image/png, image/jpeg">
<button type="submit">Upload</button>
</form>
6.2 PHP Handler
<?php
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST' && isset($_FILES['avatar'])) {
$f = $_FILES['avatar'];
if ($f['error'] !== UPLOAD_ERR_OK) { die('Upload error'); }
if ($f['size'] > 2*1024*1024) { die('Too large (max 2MB)'); }
$fi = new finfo(FILEINFO_MIME_TYPE);
$mime = $fi->file($f['tmp_name']);
if (!in_array($mime, ['image/jpeg', 'image/png'], true)) {
die('Invalid type (only jpg/png)');
}
$safe = bin2hex(random_bytes(8)) . ($mime==='image/png' ? '.png' : '.jpg');
$target = __DIR__ . "/uploads/" . $safe;
move_uploaded_file($f['tmp_name'], $target);
}
?>
7) Best Practices & Common Mistakes
| Issue | Problem | Fix |
|---|---|---|
| Printing raw input | XSS risk | Wrap with htmlspecialchars(..., ENT_QUOTES, 'UTF-8') |
| Relying only on JS validation | Can be bypassed | Always validate again in PHP |
| No CSRF protection | Cross-site requests succeed | Session token + verify on POST |
Using $_REQUEST | Mixes GET/POST/COOKIE | Use $_GET or $_POST explicitly |
| Building SQL with string concat | SQL injection | Use PDO prepared statements |
8) Practice Tasks
- Create a registration form (name, email, password, confirm). Validate ≥ 8 chars and matching passwords. Use CSRF + PRG.
- Make a product form (title, price int, URL). Show per-field errors and sticky values.
- Add an avatar upload input (PNG/JPG, ≤ 1MB). Save with random filename to
/uploads. - Refactor validation rules into a reusable function that returns
[$clean, $errors].
Next: Continue with File Handling — reading & writing files, CSV/JSON, locks, safe paths, and directory ops.