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
ConceptWhy it mattersHow
Sticky fieldsDon’t force users to retype on errorKeep an $old array; echo values back
Error arrayShow field-specific messages$errors['email'] = 'message'
Server-side validationCannot be bypassedUse filter_var, regex, custom rules
CSRF tokenBlocks cross-site form submitsToken in session + hidden input + hash_equals
PRGPrevents duplicate submit on refreshheader('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)

We’ll never share your email.

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

IssueProblemFix
Printing raw inputXSS riskWrap with htmlspecialchars(..., ENT_QUOTES, 'UTF-8')
Relying only on JS validationCan be bypassedAlways validate again in PHP
No CSRF protectionCross-site requests succeedSession token + verify on POST
Using $_REQUESTMixes GET/POST/COOKIEUse $_GET or $_POST explicitly
Building SQL with string concatSQL injectionUse PDO prepared statements

8) Practice Tasks

  1. Create a registration form (name, email, password, confirm). Validate ≥ 8 chars and matching passwords. Use CSRF + PRG.
  2. Make a product form (title, price int, URL). Show per-field errors and sticky values.
  3. Add an avatar upload input (PNG/JPG, ≤ 1MB). Save with random filename to /uploads.
  4. 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.