Skip to content

PHP 表单验证

本章节介绍了使用 PHP 验证用户提交的表单数据的基本技术。服务器端验证对于安全和数据完整性至关重要。

  • 安全性: 防范常见的 Web 漏洞,如跨站脚本攻击 (XSS)、SQL 注入和其他恶意输入。
  • 数据完整性: 确保接收到的数据格式正确,并满足应用程序的要求(例如,有效的电子邮件地址,必填字段已填写)。
  • 用户体验: 向用户提供清晰的错误反馈,帮助他们纠正错误。

| | 处理 PHP 表单时,请考虑安全性! 在服务器端对表单数据进行适当的验证和净化 (sanitization) 是不可或缺的。永远不要信任用户输入。客户端验证(使用 HTML5 属性或 JavaScript)可以改善用户体验,但很容易被绕过。 | | --- | --- |

我们将使用一个包含各种输入字段的表单:必填和可选文本字段、单选按钮和提交按钮。

PHP 表单验证示例

* 必填字段

Name: *

E-mail: *

Website:

Comment:

Gender: <input type=“radio” name=“gender” value=“female”>Female <input type=“radio” name=“gender” value=“male”>Male <input type=“radio” name=“gender” value=“other”>Other *

验证规则:

字段验证规则
Name必填。只能包含字母和空格。
E-mail必填。必须包含有效的电子邮件地址格式。
Website可选。如果存在,必须包含有效的 URL 格式。
Comment可选。多行输入字段。
Gender必填。必须选择一个选项。

1. 表单 action 属性与 htmlspecialchars() 函数

Section titled “1. 表单 action 属性与 htmlspecialchars() 函数”

表单的 action 属性指定提交表单数据时将数据发送到哪里。

  • $_SERVER['PHP_SELF'] 指的是当前脚本文件。提交表单会重新加载同一页面,允许页面内的 PHP 代码处理数据并显示错误。
  • htmlspecialchars() 在这里至关重要。它将特殊的 HTML 字符(如 < 和 >)转换为其对应的 HTML 实体(&lt; 和 &gt;)。这可以防止跨站脚本攻击 (XSS),即攻击者可能将恶意代码注入到 URL 中,如果在 action 属性未使用 htmlspecialchars() 的情况下操作该属性。

XSS 攻击示例(未使用 htmlspecialchars() 的情况):

如果 action 只是 <?php echo $_SERVER['PHP_SELF'];?>,攻击者可以构造一个如下所示的 URL:

http://example.com/form.php/"><script>alert('hacked')</script>

这可能会导致表单标签被渲染为:

<form method="post" action="form.php/"><script>alert('hacked')</script>">

注入的脚本随后会在用户的浏览器中执行。使用 htmlspecialchars() 通过编码恶意字符来阻止这种情况发生。

2. 输入净化 (Sanitization) 与验证 (Validation)

Section titled “2. 输入净化 (Sanitization) 与验证 (Validation)”
  • 净化 (Sanitization): 清理或过滤输入数据。示例:trim() 删除开头/结尾的空白字符;htmlspecialchars() 转义 HTML 字符以确保安全的输出。净化使得数据可以安全地处理或显示。
  • 验证 (Validation): 检查数据是否符合预期的规则。示例:电子邮件地址格式是否有效?必填字段是否非空?数字是否在特定范围内?

您通常在验证输入之前对其进行净化,并且在将输出显示在 HTML 中之前必须对其进行转义。

3. trim() 与 stripslashes()(历史说明)

Section titled “3. trim() 与 stripslashes()(历史说明)”
  • trim():用于移除用户输入中意外的开头/结尾空白字符。
  • stripslashes():用于移除旧的、已废弃的 magic_quotes_gpc 功能添加的反斜杠。在现代 PHP (PHP 5.4+) 中,您通常不需要 stripslashes(),因为 magic quotes 已被移除。 不必要地使用它可能会损坏包含反斜杠的有效数据。

让我们构建处理表单提交和验证的 PHP 代码。

<?php
// 定义变量并设置为初始空值
$nameErr = $emailErr = $genderErr = $websiteErr = "";
$name = $email = $gender = $comment = $website = "";
// 检查表单是否使用 POST 方法提交
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// --- 姓名验证 ---
if (empty($_POST["name"])) {
$nameErr = "姓名是必填项";
} else {
$name = sanitize_input($_POST["name"]);
// 检查姓名是否只包含字母、破折号、撇号和空格
if (!preg_match("/^[a-zA-Z-' ]*$/", $name)) {
$nameErr = "只允许字母、破折号、撇号和空格";
}
}
// --- 电子邮件验证 ---
if (empty($_POST["email"])) {
$emailErr = "电子邮件是必填项";
} else {
$email = sanitize_input($_POST["email"]);
// 使用 PHP 内置过滤器检查电子邮件地址格式是否良好
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$emailErr = "电子邮件格式无效";
}
}
// --- 网站验证 (可选) ---
if (empty($_POST["website"])) {
$website = "";
} else {
$website = sanitize_input($_POST["website"]);
// 检查 URL 语法是否有效 (允许 http, https 等方案)
// FILTER_VALIDATE_URL 默认需要包含方案 (scheme)
if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i", $website)) {
$websiteErr = "URL 格式无效";
} else if (!filter_var($website, FILTER_VALIDATE_URL)) {
// 使用 filter_var 作为通用结构的二次检查
// 根据需求,这个检查可能过于严格;正则 (regex) 通常更实用
// $websiteErr = "无效 URL";
}
}
// --- 评论净化 (可选) ---
if (empty($_POST["comment"])) {
$comment = "";
} else {
$comment = sanitize_input($_POST["comment"]);
// 这里没有特定的验证规则,只进行净化
}
// --- 性别验证 ---
if (empty($_POST["gender"])) {
$genderErr = "性别是必填项";
} else {
$gender = sanitize_input($_POST["gender"]);
// 可选:检查值是否为允许的选项之一
$allowed_genders = ['female', 'male', 'other'];
if (!in_array($gender, $allowed_genders)) {
$genderErr = "无效的性别选择";
$gender = ""; // 如果无效,重置值
}
}
// 如果所有验证都通过,则可以处理数据 (例如,保存到数据库)
if (empty($nameErr) && empty($emailErr) && empty($websiteErr) && empty($genderErr)) {
// 处理数据...
echo "<h2>表单提交成功:</h2>";
echo "姓名: " . htmlspecialchars($name) . "<br>";
echo "电子邮件: " . htmlspecialchars($email) . "<br>";
echo "网站: " . htmlspecialchars($website) . "<br>
echo "评论: " . nl2br(htmlspecialchars($comment)) . "<br>"; // 使用 nl2br 保留换行符
echo "性别: " . htmlspecialchars($gender) . "<br>";
// 如果需要,可以在成功提交后清除表单变量
// $name = $email = $gender = $comment = $website = "";
exit; // 如果重定向或已完成,停止脚本执行
}
}
// 基本净化函数 (去除空白字符)
// 注意:在现代 PHP 中不需要 stripslashes()
function sanitize_input($data) {
$data = trim($data);
// $data = stripslashes($data); // 已废弃 - 除非处理旧版 magic_quotes,否则不要使用
// htmlspecialchars 应该用于输出,而不是通常用于输入的存储
return $data;
}
?>
<!-- HTML 表单 (如上所示) 在这里 -->
<!-- 它使用 PHP 变量 $name, $email 等来重新填充字段 -->
<!-- 并使用 $nameErr, $emailErr 等来显示错误消息 -->
  1. 初始化: 用于存储表单数据 ($name, $email 等) 和相应错误消息 ($nameErr, $emailErr 等) 的变量被初始化为空字符串。
  2. 检查请求方法: if ($_SERVER["REQUEST_METHOD"] == "POST") 确保验证代码仅在表单通过 POST 方法提交时运行。
  3. 净化输入: sanitize_input() 函数(此处仅使用 trim())清理原始输入。
  4. 验证必填字段: empty() 检查必填字段(姓名、电子邮件、性别)是否已提交。
  5. 验证格式(姓名): preg_match() 检查姓名是否只包含允许的字符(字母、撇号、破折号、空格)。
  6. 验证格式(电子邮件): filter_var($email, FILTER_VALIDATE_EMAIL) 使用 PHP 的内置过滤器,这是检查电子邮件格式的可靠方法。
  7. 验证格式(网站): preg_match() 提供了更灵活的 URL 模式检查。filter_var($website, FILTER_VALIDATE_URL) 也可以使用,但可能更严格(例如,要求包含 http://)。选择最适合您需求的方法。
  8. 处理数据: 如果所有错误变量在验证后都保持为空,则数据被认为是有效的,可以进行处理(例如,保存到数据库,通过电子邮件发送)。记住在将这些数据显示回 HTML 时使用 htmlspecialchars() 以确保安全。
  9. 显示错误并重新填充表单: HTML 表单使用 PHP 错误变量 ($nameErr 等) 在相关字段旁边显示消息。它还使用数据变量 ($name, $email 等,为了安全起见用 htmlspecialchars() 包裹) 在 value 属性(或 textarea 内容)中重新填充表单,以便在验证失败时保留用户之前输入的内容。
  • 更强大的净化/验证: 对于更复杂的场景,使用像 laminas-filter、laminas-validator 或 Respect\Validation 这样的库。
  • CSRF 防护: 实现跨站请求伪造 (Cross-Site Request Forgery) Token 以防范 CSRF 攻击。
  • 集中的验证逻辑: 对于大型表单,考虑创建验证类或函数以实现更好的组织。