PHP: 表单验证

上节课你学会了接收表单数据。这节课是更关键的一步——验证。永远不要信任用户的输入。用户可能手滑、可能是恶意攻击、可能压根没填。表单验证是你的第一道防火墙。

1. 验证架构

所有验证在服务器端做(PHP),前端验证(HTML5/JS)只是锦上添花:

TEXT 📖 仅展示
用户浏览器(JS 验证,可被绕过)
      ↓
PHP 服务器(必须验证!不可跳过)
      ↓
存入数据库
PHP
<?php
$errors = [];

// 1. 获取并清理输入
$name  = trim($_POST['name'] ?? '');
$age   = $_POST['age'] ?? '';
$email = trim($_POST['email'] ?? '');

// 2. 验证规则
if ($name === '') {
    $errors['name'] = '姓名不能为空';
} elseif (strlen($name) > 50) {
    $errors['name'] = '姓名不能超过50个字符';
}

if ($age === '') {
    $errors['age'] = '年龄不能为空';
} elseif (!is_numeric($age)) {
    $errors['age'] = '年龄必须是数字';
} elseif ((int)$age < 1 || (int)$age > 150) {
    $errors['age'] = '年龄必须在1-150之间';
}

if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors['email'] = '邮箱格式不正确';
}  // email 不是必填项,但填了就要正确

// 3. 判断结果
if (empty($errors)) {
    echo "验证通过!";
} else {
    foreach ($errors as $field => $msg) {
        echo "<p style='color:red'>{$msg}</p>";
    }
}
?>
💡 提示: 用关联数组 $errors 来收集错误信息。$errors['field_name'] 的结构便于在对应字段旁边显示错误提示。


2. filter_var() 验证

PHP 内置的 filter_var() 是最简洁的验证方式:

PHP
<?php
// 邮箱验证
$email = "test@example.com";
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
// "test@example.com"(验证通过返回原值)

$badEmail = "not-an-email";
var_dump(filter_var($badEmail, FILTER_VALIDATE_EMAIL));
// bool(false)(验证失败)

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

// IP 验证
$ip = "192.168.1.1";
var_dump(filter_var($ip, FILTER_VALIDATE_IP));

// 整数范围验证
$age = 25;
var_dump(filter_var($age, FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 1, "max_range" => 150]
]));
?>

▶ 示例:用 filter_var 验证整个表单

PHP
<?php
$errors = [];

// 验证邮箱
$email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
if ($email === false) {
    $errors['email'] = '请输入有效的邮箱地址';
}

// 验证 URL
$website = filter_var($_POST['website'] ?? '', FILTER_VALIDATE_URL);
if ($website === false && ($_POST['website'] ?? '') !== '') {
    $errors['website'] = '请输入有效的网址';
}

// 验证整数范围
$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 1, "max_range" => 120]
]);
if ($age === false) {
    $errors['age'] = '年龄必须在1-120之间';
}
?>
▶ 试一试

3. 正则验证(preg_match)

当标准验证不够用时,正则表达式上场:

PHP
<?php
// 手机号验证(中国11位)
$phone = "13800138000";
if (preg_match('/^1[3-9]\d{9}$/', $phone)) {
    echo "手机号格式正确";
}

// 用户名验证(字母数字下划线,3-20位)
$username = "user_2024";
if (preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
    echo "用户名格式正确";
}

// 强密码验证(至少8位,含大小写字母和数字)
$password = "MyPass123";
if (preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/', $password)) {
    echo "密码强度合格";
}
?>

4. 安全防护

(1) htmlspecialchars() — 防 XSS 攻击

用户输入中的 <script> 标签如果不处理,会直接在浏览器执行:

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

// ❌ 危险:直接输出
echo $userInput;  // 脚本会执行!

// ✅ 安全:转义特殊字符
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// 输出:&lt;script&gt;alert(&quot;被黑了!&quot;)&lt;/script&gt;
// 浏览器显示原文,但不执行脚本
?>

(2) strip_tags() — 去除 HTML 标签

PHP
<?php
$content = "<p>这是<b>重要</b>内容</p>";
echo strip_tags($content);  // 这是重要内容

// 允许保留某些标签(第二个参数白名单)
echo strip_tags($content, '<b><i>');  // 这是<b>重要</b>内容
?>

(3) trim() — 去除头尾空白

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

▶ 示例:完整的输入清理函数

PHP
<?php
/**
 * 清理用户文本输入
 */
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. 完整验证实战

▶ 示例:注册表单完整验证

PHP 📖 仅展示
<?php
$errors = [];
$username = $email = $password = '';

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

    // 用户名验证
    if ($username === '') {
        $errors['username'] = '用户名不能为空';
    } elseif (strlen($username) < 3 || strlen($username) > 20) {
        $errors['username'] = '用户名需要3-20个字符';
    } elseif (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
        $errors['username'] = '用户名只能包含字母、数字、下划线';
    }

    // 邮箱验证
    $emailClean = filter_var($email, FILTER_VALIDATE_EMAIL);
    if ($email === '') {
        $errors['email'] = '邮箱不能为空';
    } elseif ($emailClean === false) {
        $errors['email'] = '邮箱格式不正确';
    }

    // 密码验证
    if ($password === '') {
        $errors['password'] = '密码不能为空';
    } elseif (strlen($password) < 6) {
        $errors['password'] = '密码至少需要6个字符';
    }

    if (empty($errors)) {
        $usernameSafe = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
        echo "<h3 style='color:green'>注册成功!欢迎,{$usernameSafe}!</h3>";
        $username = $email = $password = '';
    }
}
?>

<form method="POST" action="">
    <div>
        <label>用户名:</label>
        <input type="text" name="username"
               value="<?= htmlspecialchars($username) ?>">
        <span style="color:red"><?= $errors['username'] ?? '' ?></span>
    </div>
    <div>
        <label>邮箱:</label>
        <input type="email" name="email"
               value="<?= htmlspecialchars($email) ?>">
        <span style="color:red"><?= $errors['email'] ?? '' ?></span>
    </div>
    <div>
        <label>密码:</label>
        <input type="password" name="password">
        <span style="color:red"><?= $errors['password'] ?? '' ?></span>
    </div>
    <button type="submit">注册</button>
</form>
逻辑代码 52 行(超过 40 行限制,仅展示)

❓ 常见问题

Q 前端已经有 required 和 pattern 验证了,还需要 PHP 验证吗?
A 必须需要。前端验证可以被轻易绕过(禁用 JS、用 curl 模拟请求)。PHP 端验证是不可跳过的最后防线。
Q htmlspecialcharsstrip_tags 什么时候用哪个?
A 保留显示用户输入的内容用 htmlspecialchars(转义但不删除 HTML 标签,保留原外观)。完全去除 HTML 标签用 strip_tags。大多数场景用 htmlspecialchars 就够。
Q 正则表达式看起来很恐怖,我要全记住吗?
A 不用。常用正则(邮箱、手机号、URL)有现成的,记住 preg_match() 的用法和几个基础符号(^开头、$结尾、\d数字、[] 字符集、{}数量)就够了。

📖 小节

📝 作业

  1. 写一个用户资料编辑表单,包含昵称、年龄、邮箱、个人主页URL,对每个字段做对应的验证。
  2. 写一个密码修改表单,要求:原密码不为空、新密码至少8位且含数字和字母、确认密码与新密码一致。
  3. 测试 XSS 漏洞:创建一个留言板,故意不写 htmlspecialchars,尝试在留言中输入 <script>alert(1)</script>,观察结果。然后加上 htmlspecialchars 再测试。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

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

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