PHP: Form Validation in PHP

In the last lesson you learned how to receive form data. This lesson covers the more critical step — validation. Never trust user input. Users make typos. Users are malicious. Users leave fields blank. Form validation is your first line of defense.

1. The Validation Architecture

All validation happens on the server side (PHP). Client-side validation (HTML5 / JavaScript) is just a nice-to-have convenience layer:

TEXT 📖 Display only
Browser (JS validation, easily bypassed)
      ↓
PHP server (MUST validate — never skip this step)
      ↓
Store in database
PHP
<?php
$errors = [];

// 1. Collect and clean input
$name  = trim($_POST['name'] ?? '');
$age   = $_POST['age'] ?? '';
$email = trim($_POST['email'] ?? '');

// 2. Apply validation rules
if ($name === '') {
    $errors['name'] = 'Name is required';
} elseif (strlen($name) > 50) {
    $errors['name'] = 'Name must not exceed 50 characters';
}

if ($age === '') {
    $errors['age'] = 'Age is required';
} elseif (!is_numeric($age)) {
    $errors['age'] = 'Age must be a number';
} elseif ((int)$age < 1 || (int)$age > 150) {
    $errors['age'] = 'Age must be between 1 and 150';
}

if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors['email'] = 'Invalid email format';
}  // Email is optional, but if provided it must be valid

// 3. Determine the outcome
if (empty($errors)) {
    echo "Validation passed!";
} else {
    foreach ($errors as $field => $msg) {
        echo "<p style='color:red'>{$msg}</p>";
    }
}
?>
💡 Tip: Use an associative array $errors to collect error messages. The $errors['field_name'] structure makes it easy to display errors next to their corresponding fields.


2. Validating with filter_var()

PHP's built-in filter_var() is the cleanest way to validate common data types:

PHP
<?php
// Email validation
$email = "test@example.com";
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
// "test@example.com" (returns the original value on success)

$badEmail = "not-an-email";
var_dump(filter_var($badEmail, FILTER_VALIDATE_EMAIL));
// bool(false) (validation failed)

// URL validation
$url = "https://www.example.com";
var_dump(filter_var($url, FILTER_VALIDATE_URL));

// IP address validation
$ip = "192.168.1.1";
var_dump(filter_var($ip, FILTER_VALIDATE_IP));

// Integer range validation
$age = 25;
var_dump(filter_var($age, FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 1, "max_range" => 150]
]));
?>

▶ Example: Validating an Entire Form with filter_var

Output:

TEXT 📖 Display only
(no visible output)
PHP
<?php
$errors = [];

// Validate email
$email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
if ($email === false) {
    $errors['email'] = 'Please enter a valid email address';
}

// Validate URL
$website = filter_var($_POST['website'] ?? '', FILTER_VALIDATE_URL);
if ($website === false && ($_POST['website'] ?? '') !== '') {
    $errors['website'] = 'Please enter a valid URL';
}

// Validate integer range
$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';
}
?>

Output:

TEXT 📖 Display only
Output displayed

3. Regex Validation with preg_match()

When built-in validators aren't enough, regular expressions step in:

PHP
<?php
// Phone number (10-digit North American)
$phone = "5551234567";
if (preg_match('/^\d{10}$/', $phone)) {
    echo "Phone number format is valid";
}

// Username (alphanumeric + underscore, 3-20 characters)
$username = "user_2024";
if (preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
    echo "Username format is valid";
}

// Strong password (min 8 chars, at least one lowercase, one uppercase, one digit)
$password = "MyPass123";
if (preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/', $password)) {
    echo "Password meets strength requirements";
}
?>

4. Security Hardening

(1) htmlspecialchars() — Preventing XSS Attacks

If user input containing <script> tags is echoed directly, the browser will execute it:

PHP
<?php
$userInput = '<script>alert("Hacked!")</script>';

// ❌ Dangerous — direct output
echo $userInput;  // The script executes!

// ✅ Safe — escape special characters
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// Renders as: &lt;script&gt;alert(&quot;Hacked!&quot;)&lt;/script&gt;
// The browser displays the text rather than executing it
?>

(2) strip_tags() — Stripping HTML Tags

PHP
<?php
$content = "<p>This is <b>important</b> content</p>";
echo strip_tags($content);  // This is important content

// Optionally allow specific tags (whitelist as second argument)
echo strip_tags($content, '<b><i>');  // This is <b>important</b> content
?>

(3) trim() — Removing Leading and Trailing Whitespace

PHP
<?php
$input = "   John   ";
echo trim($input);  // John
?>

▶ Example: A Complete Input Sanitization Function

Output:

TEXT 📖 Display only
(no visible output - function defined)
PHP
<?php
/**
 * Clean user-supplied text input
 */
function cleanInput(string $data): string {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
    return $data;
}

$name  = cleanInput($_POST['name'] ?? '');
$comment = cleanInput($_POST['comment'] ?? '');
?>

Output:

TEXT 📖 Display only
Output displayed

5. Putting It All Together

▶ Example: Full Registration Form with Validation

Output:

TEXT 📖 Display only
<h3 style='color:green'>Registration successful! Welcome, {Alice}!</h3>
PHP
<?php
$errors = [];
$username = $email = $password = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $email    = trim($_POST['email'] ?? '');
    $password = $_POST['password'] ?? '';

    // Username validation
    if ($username === '') {
        $errors['username'] = 'Username is required';
    } elseif (strlen($username) < 3 || strlen($username) > 20) {
        $errors['username'] = 'Username must be 3–20 characters';
    } elseif (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
        $errors['username'] = 'Username may only contain letters, digits, and underscores';
    }

    // Email validation
    $emailClean = filter_var($email, FILTER_VALIDATE_EMAIL);
    if ($email === '') {
        $errors['email'] = 'Email is required';
    } elseif ($emailClean === false) {
        $errors['email'] = 'Invalid email format';
    }

    // Password validation
    if ($password === '') {
        $errors['password'] = 'Password is required';
    } elseif (strlen($password) < 6) {
        $errors['password'] = 'Password must be at least 6 characters';
    }

    if (empty($errors)) {
        $usernameSafe = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
        echo "<h3 style='color:green'>Registration successful! Welcome, {$usernameSafe}!</h3>";
        $username = $email = $password = '';
    }
}
?>

<form method="POST" action="">
    <div>
        <label>Username:</label>
        <input type="text" name="username"
               value="<?= htmlspecialchars($username) ?>">
        <span style="color:red"><?= $errors['username'] ?? '' ?></span>
    </div>
    <div>
        <label>Email:</label>
        <input type="email" name="email"
               value="<?= htmlspecialchars($email) ?>">
        <span style="color:red"><?= $errors['email'] ?? '' ?></span>
    </div>
    <div>
        <label>Password:</label>
        <input type="password" name="password">
        <span style="color:red"><?= $errors['password'] ?? '' ?></span>
    </div>
    <button type="submit">Register</button>
</form>

Output:

TEXT 📖 Display only
Registration successful! Welcome, John!

❓ FAQ

Q I already have required and pattern attributes on the front-end. Do I still need PHP validation?
A Absolutely. Front-end validation is trivially bypassed — disable JavaScript, or simulate a request with curl. PHP-side validation is the last line of defense that cannot be skipped.
Q When should I use htmlspecialchars and when strip_tags?
A Use htmlspecialchars when you want to display user input as-is but safely (it escapes HTML without removing it). Use strip_tags when you want to completely remove all HTML. In most cases, htmlspecialchars is sufficient.
Q Regular expressions look intimidating. Do I need to memorize them?
A No. Common patterns (email, phone, URL) have well-known recipes. Just remember preg_match() and a few basic symbols — ^ (start), $ (end), \d (digit), [] (character set), {} (quantity) — and you'll be fine.

📖 Summary

📝 Exercises

  1. Build a profile editing form with nickname, age, email, and personal website URL. Validate each field with the appropriate rule.
  2. Build a password change form: require the current password, enforce at least 8 characters with a mix of letters and digits for the new password, and confirm it matches.
  3. Test an XSS vulnerability: create a guestbook form that deliberately omits htmlspecialchars. Try posting <script>alert(1)</script> and observe. Then add htmlspecialchars and test again.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏