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
📖 参照専用
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]
]));
?>
▶ サンプル: Validating an Entire Form with filter_var
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';
}
?>
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: <script>alert("Hacked!")</script>
// 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
?>
▶ サンプル: A Complete Input Sanitization Function
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'] ?? '');
?>
5. Putting It All Together
▶ サンプル: Full Registration Form with Validation
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>
❓ よくある質問
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.📖 まとめ
- Validation sequence: required check → type check → range check → format check
filter_var($val, FILTER_VALIDATE_EMAIL/URL/INT)is the cleanest validation shortcutpreg_match('/pattern/', $val)handles complex format requirementshtmlspecialchars()prevents XSS — escape before outputting to HTMLstrip_tags()removes all HTML;trim()removes surrounding whitespace- Front-end validation is for experience; PHP validation is for security
📝 練習問題
- Build a profile editing form with nickname, age, email, and personal website URL. Validate each field with the appropriate rule.
- 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.
- Test an XSS vulnerability: create a guestbook form that deliberately omits
htmlspecialchars. Try posting<script>alert(1)</script>and observe. Then addhtmlspecialcharsand test again.