Form Validation
Learn how to validate submitted form data with empty(), filter_var(), and trim(), and safely display errors back to the user.
Introduction
In the last lesson, you built a working form that collected a name and an email address — but it trusted whatever the visitor typed without question. An empty name, a malformed email, or even malicious script tags would all be accepted silently.
This lesson covers form validation: making sure required fields are actually filled in, that values match the format you expect, and that anything you echo back to the page is safely escaped so it cannot be used to attack your site's other visitors.
- How to check that required fields were actually filled in using empty().
- How to validate formats like email addresses with filter_var().
- How to clean up stray whitespace using trim().
- How to display field-specific error messages back to the user.
- Why any user input echoed back to the page must be escaped with htmlspecialchars().
Checking Required Fields
The empty() function checks whether a variable is considered "empty" — an empty string, 0, null, or an unset key all count. It is the standard way to check that a required form field was actually filled in.
<?php
$name = $_POST["name"] ?? "";
if (empty($name)) {
echo "Name is required.\n";
} else {
echo "Name received: $name\n";
}
?>Name is required.The null coalescing operator (??) returns "" if $_POST["name"] does not exist, instead of triggering an undefined-key warning. This is a safe way to read a possibly-missing superglobal value before validating it.
Trimming Whitespace
Visitors often accidentally add leading or trailing spaces when typing into a form field. The trim() function removes whitespace from both ends of a string, which should typically happen before you validate or store a value.
<?php
$name = $_POST["name"] ?? "";
$name = trim($name);
if (empty($name)) {
echo "Name is required.\n";
} else {
echo "Name received: '$name'\n";
}
?>Name received: 'Alice'Trimming before checking with empty() also catches a sneaky edge case: a field containing only spaces, which would otherwise pass a naive check but still be functionally blank.
Validating Formats with filter_var()
Some fields need more than just a "not empty" check — an email field, for example, should actually look like an email address. PHP's filter_var() function validates a value against a built-in filter, such as FILTER_VALIDATE_EMAIL.
<?php
$email = trim($_POST["email"] ?? "");
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "Valid email: $email\n";
} else {
echo "Please enter a valid email address.\n";
}
?>Please enter a valid email address.Valid email: alice@example.comfilter_var() returns the validated (and in some cases sanitized) value when the input passes, or false when it does not — making it easy to use directly inside an if condition.
Displaying Field-Specific Errors
Rather than a single generic error message, well-designed forms tell the visitor exactly which field needs attention. A common approach is to collect errors into an array, keyed by field name, and check it when rendering each field.
<?php
$errors = [];
$name = trim($_POST["name"] ?? "");
$email = trim($_POST["email"] ?? "");
if (empty($name)) {
$errors["name"] = "Name is required.";
}
if (empty($email)) {
$errors["email"] = "Email is required.";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors["email"] = "Please enter a valid email address.";
}
if (empty($errors)) {
echo "All fields are valid!\n";
} else {
foreach ($errors as $field => $message) {
echo "$field: $message\n";
}
}
?>name: Name is required.
email: Please enter a valid email address.Full Worked Example
Putting everything together, here is a self-submitting sign-up form that validates both fields, re-displays field-specific errors, and safely echoes back whatever the visitor typed.
<?php
$errors = [];
$name = "";
$email = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$name = trim($_POST["name"] ?? "");
$email = trim($_POST["email"] ?? "");
if (empty($name)) {
$errors["name"] = "Name is required.";
}
if (empty($email)) {
$errors["email"] = "Email is required.";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors["email"] = "Please enter a valid email address.";
}
if (empty($errors)) {
echo "<p>Thanks, " . htmlspecialchars($name) . "! Signed up with " . htmlspecialchars($email) . ".</p>";
}
}
?>
<!DOCTYPE html>
<html>
<body>
<form method="POST" action="signup-validated.php">
<label for="name">Name:</label>
<input type="text" id="name" name="name" value="<?php echo htmlspecialchars($name); ?>">
<?php if (isset($errors["name"])): ?>
<span class="error"><?php echo htmlspecialchars($errors["name"]); ?></span>
<?php endif; ?>
<label for="email">Email:</label>
<input type="email" id="email" name="email" value="<?php echo htmlspecialchars($email); ?>">
<?php if (isset($errors["email"])): ?>
<span class="error"><?php echo htmlspecialchars($errors["email"]); ?></span>
<?php endif; ?>
<button type="submit">Sign Up</button>
</form>
</body>
</html>name field shows error: "Name is required."
email field passes validation, no error shown
Form re-displayed with the entered email pre-filledEscaping Output with htmlspecialchars()
Notice that every single place a submitted value is echoed back into the page in the example above goes through htmlspecialchars(). This function converts special HTML characters like <, >, and " into safe entities, so text a visitor typed cannot be interpreted as HTML or JavaScript.
<?php
$comment = $_POST["comment"] ?? "";
// Dangerous: if $comment contains <script>...</script>, it would execute in every visitor's browser
echo "<p>" . $comment . "</p>";
// Safe: the tags are converted to harmless text instead of being rendered
echo "<p>" . htmlspecialchars($comment) . "</p>";
?>Unsafe version: script executes in the browser
Safe version: <script>alert("hi")</script> displayed as plain textThis kind of attack is called cross-site scripting (XSS). Any value that originated from user input — even one that passed all your other validation — must be escaped with htmlspecialchars() before it is echoed into HTML.
Common Mistakes
- Checking empty() before trimming whitespace, letting a field of only spaces slip through as "not empty."
- Validating a field's format but forgetting to check that it was filled in at all.
- Showing one generic error message instead of pointing the visitor to the specific field that failed.
- Echoing submitted values back into HTML without htmlspecialchars(), opening the door to XSS attacks.
Best Practices
- Always trim() input before checking it with empty() or storing it.
- Use filter_var() with the appropriate filter constant for structured fields like email addresses.
- Collect validation errors in an associative array keyed by field name for clear, targeted feedback.
- Escape every piece of user-supplied data with htmlspecialchars() at the moment you echo it, with no exceptions.
Frequently Asked Questions
Is client-side validation (like HTML's required attribute) enough on its own?
No. Client-side validation improves the user experience, but it can be bypassed entirely, so server-side validation in PHP is always required for real security and data integrity.
What does filter_var() return when validation fails?
It returns false, which is why it is commonly used directly inside an if or elseif condition.
Why escape output instead of just rejecting input with HTML tags in it?
Rejecting is one option for some fields, but many legitimate inputs (like a name containing an apostrophe) are safe to accept and simply need to be escaped correctly when displayed.
Should I use htmlspecialchars() on data that already passed filter_var()?
Yes. Format validation and output escaping solve different problems — validation checks the value is well-formed, escaping protects how it is later displayed.
Are there other filter_var() filters besides FILTER_VALIDATE_EMAIL?
Yes, PHP provides several, including filters for URLs, integers, and IP addresses, all used the same way.
Key Takeaways
- empty() checks whether a required field was actually filled in.
- trim() should be applied before validation to strip stray leading/trailing whitespace.
- filter_var() with constants like FILTER_VALIDATE_EMAIL checks that a value matches an expected format.
- Field-specific error messages, stored in an array keyed by field name, give clearer feedback than one generic message.
- Any user input echoed back to the page must be escaped with htmlspecialchars() to prevent XSS attacks.
Summary
Form validation is what turns a form that merely accepts input into one that can be trusted. By checking required fields, trimming whitespace, validating formats with filter_var(), and always escaping output with htmlspecialchars(), you protect both your data and your visitors.
In this lesson, you validated a real sign-up form end to end and learned why escaping output is non-negotiable. Next, you will take a closer look at the GET and POST methods themselves and when to reach for each one.
- You can check required fields using empty().
- You can validate formats like email addresses using filter_var().
- You can clean up input with trim() before validating it.
- You can display field-specific error messages.
- You always escape user input with htmlspecialchars() before echoing it back to the page.