PHP Form Validation

The Forms & User Input page already covers sticky forms, CSRF, and PRG. This page goes deeper into validation itself: using filter_var() with specific filters, checking numeric ranges, writing a reusable validation function, and showing field-specific errors.

1) filter_var() With Specific Filters

filter_var() validates a value against a built-in filter and returns the cleaned value, or false if it fails.

<?php
  filter_var('a@b.com', FILTER_VALIDATE_EMAIL);  // 'a@b.com'
  filter_var('42', FILTER_VALIDATE_INT);          // 42
  filter_var('not-a-number', FILTER_VALIDATE_INT);  // false
  filter_var('https://example.com', FILTER_VALIDATE_URL);  // the URL string
?>

2) Validating a Numeric Range

Pass an options array with min_range/max_range to FILTER_VALIDATE_INT to enforce a range in one call.

<?php
  $age = filter_var($_POST['age'], FILTER_VALIDATE_INT, [
    'options' => ['min_range' => 1, 'max_range' => 120]
  ]);
  if ($age === false) {
    $errors['age'] = 'Age must be between 1 and 120.';
  }
?>

3) Required-Field Checking With empty()

Always check a field is present and non-empty before running a format check on it, so you can show a clearer "required" message instead of a confusing format error.

<?php
  $coupon = trim($_POST['coupon'] ?? '');
  if (empty($coupon)) {
    $errors['coupon'] = 'Discount code is required.';
  }
?>

4) A Regex Pattern: Exactly 6 Uppercase Letters/Digits

Some fields need a custom shape that no built-in filter covers — that's where preg_match() comes in.

<?php
  if (!preg_match('/^[A-Z0-9]{6}$/', $coupon)) {
    $errors['coupon'] = 'Code must be exactly 6 uppercase letters/digits.';
  }
?>

5) A Reusable Validation Function

Instead of scattering validation checks through the page, group them into one function that returns [$clean, $errors] — easy to reuse and to unit-test.

<?php
  function validate_demo_form(array $post): array {
    $errors = [];
    $clean  = ['age' => '', 'website' => '', 'coupon' => ''];
    // ...validate each field, fill $clean and $errors...
    return [$clean, $errors];
  }
?>
Field-specific error display, sticky values, CSRF tokens, and the Post-Redirect-Get pattern are already explained in detail on the Forms & User Input page — this page reuses the same patterns, just with different validation rules below.

6) Working Demo: Age, Website & Discount Code

Whole number, 1–120.
Exactly 6 uppercase letters/digits.

7) Best Practices

IssueProblemFix
Trusting client-side validationCan be bypassed with dev tools/curlAlways re-validate on the server
One generic error messageUser doesn't know which field is wrongKeep an $errors array keyed by field
Loose == comparisons on validated valuesType-juggling surprisesUse ===/!== after filtering
Re-checking format before requiredConfusing "invalid format" on an empty fieldCheck empty() first