PHP 表单处理
这是 PHP 真正的杀手锏。处理 HTTP 表单是 PHP 设计的初衷之一——内置 $_GET、$_POST、$_REQUEST 超全局变量,无需任何框架就能接收、验证、处理用户提交的数据。这一章是 PHP Web 开发的入门关键。
1. HTTP 请求方法:GET 与 POST
提交表单主要有两种 HTTP 方法,选哪种取决于数据的性质和用途:
GET vs POST:
GET
- 数据在 URL:?name=xiaoming&age=25
- 长度限制(浏览器约 2KB)
- 可收藏、可分享(URL 包含全部参数)
- 不安全(密码会出现在 URL 和日志里)
- 幂等:重复提交结果相同(适合查询)
- 典型:搜索、筛选、分页
POST
- 数据在请求体
- 无长度限制(理论上)
- 不可收藏(URL 不含数据)
- 相对安全(不会出现在 URL)
- 非幂等:重复提交会产生多次结果
- 典型:登录、注册、提交订单、上传文件2. 接收数据:$_GET / $_POST
PHP 把表单数据自动放到超全局数组里,直接读就行:
<?php
// GET:数据放在 URL 查询字符串里
// 访问 page.php?name=xiaoming&age=25
echo $_GET["name"]; // xiaoming
echo $_GET["age"]; // 25
// POST:数据放在 HTTP 请求体里(不在 URL 显示)
echo $_POST["username"];
echo $_POST["password"];
// $_REQUEST = $_GET + $_POST + $_COOKIE(少用,有歧义)
// 用 ?? 提供默认值,避免未定义警告
$name = $_GET["name"] ?? "匿名";
// REQUEST_METHOD 判断请求类型
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// 处理表单提交
$name = trim($_POST["name"] ?? "");
}几个关键点:
- form 的 method 属性决定数组:
method="get"→$_GET,method="post"→$_POST。 - input 的 name 属性是数组键:
<input name="email">→$_POST["email"]。 - 键不存在会警告:用
?? ""提供默认值(null 合并)。 - $_REQUEST 是 GET+POST+COOKIE 的合集,有歧义风险,少用。
3. 完整表单处理流程
一个完整的表单处理包含 取值 → 验证 → 显示错误 → 粘性 四步。下面是注册表单的完整示例:
<?php
// 处理注册表单(完整示例)
$errors = [];
$name = $email = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// 1. 取值 + trim + 默认值
$name = trim($_POST["name"] ?? "");
$email = trim($_POST["email"] ?? "");
// 2. 验证
if ($name === "") {
$errors[] = "姓名不能为空";
} elseif (mb_strlen($name) > 50) {
$errors[] = "姓名不能超过 50 字";
}
if ($email === "") {
$errors[] = "邮箱不能为空";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "邮箱格式不正确";
}
// 3. 通过验证则处理
if (empty($errors)) {
// 写入数据库、发邮件、跳转...
header("Location: /success.php");
exit;
}
}
?>
<form method="post">
<p>
姓名: <input type="text" name="name"
value="<?= htmlspecialchars($name) ?>">
</p>
<p>
邮箱: <input type="email" name="email"
value="<?= htmlspecialchars($email) ?>">
</p>
<?php foreach ($errors as $e): ?>
<p style="color:red"><?= htmlspecialchars($e) ?></p>
<?php endforeach; ?>
<button type="submit">注册</button>
</form>注意几个防御性写法:
trim()去掉首尾空格——用户常误输入。?? ""提供默认值,避免未定义警告。filter_var(..., FILTER_VALIDATE_EMAIL)验证邮箱格式。- 所有输出都 htmlspecialchars:防 XSS(下面会讲)。
- 表单字段
value用 PHP 输出已提交的值,失败时不丢用户输入(粘性表单)。 - 成功后
header("Location: ...")跳转,避免用户刷新重复提交(PRG 模式)。
4. filter_var / filter_input:内置过滤器
PHP 内置了强大的过滤器,比手写正则更可靠:
<?php
// filter_input:从指定来源取并过滤
// 来源:INPUT_GET / INPUT_POST / INPUT_COOKIE / INPUT_SERVER
$name = filter_input(INPUT_POST, "name", FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$age = filter_input(INPUT_POST, "age", FILTER_VALIDATE_INT);
// filter_var:过滤已有变量
$email = "test@example.com";
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "邮箱合法";
}
// 常用过滤器
filter_var($url, FILTER_VALIDATE_URL); // 验证 URL
filter_var($ip, FILTER_VALIDATE_IP); // 验证 IP
filter_var($s, FILTER_SANITIZE_FULL_SPECIAL_CHARS); // 转义特殊字符
filter_var($i, FILTER_VALIDATE_INT, ["min_range" => 0, "max_range" => 150]);
// 验证整数范围
$age = filter_input(INPUT_POST, "age", FILTER_VALIDATE_INT, [
"options" => ["min_range" => 0, "max_range" => 150]
]);优先用 filter_var,不要手写正则。原因:正则容易写漏、性能差;filter_var 用 C 实现,经过充分测试,而且语义清晰。
5. 粘性表单(Sticky Form)
"粘性"指表单提交失败后,用户已填的数据保留。这是好的用户体验,新手常忽略:
<?php
// "粘性表单":提交失败时保留用户已输入的数据
// 提升用户体验,避免重新填写所有字段
$selected = $_POST["city"] ?? "";
?>
<select name="city">
<option value="bj" <?= $selected === "bj" ? "selected" : "" ?>>北京</option>
<option value="sh" <?= $selected === "sh" ? "selected" : "" ?>>上海</option>
<option value="gz" <?= $selected === "gz" ? "selected" : "" ?>>广州</option>
</select>
<?php
// 多选 checkbox
$hobbies = $_POST["hobbies"] ?? [];
?>
<label>
<input type="checkbox" name="hobbies[]" value="read"
<?= in_array("read", $hobbies) ? "checked" : "" ?>> 阅读
</label>几个常用技巧:
- input / textarea:在
value属性输出 PHP 值。 - checkbox / radio:用
in_array或字符串比较,匹配则输出checked。 - select option:匹配则输出
selected。 - 所有输出都要 htmlspecialchars——攻击者可能在输入里塞 HTML。
6. 安全:三道防线
"永远不要相信用户输入"——这是 Web 安全的第一定律。下面是 PHP 表单的三大防御:
<?php
// 永远不要相信用户输入!三大防御:
// 1. htmlspecialchars:输出到 HTML 时转义,防 XSS
echo htmlspecialchars($userInput);
// <script> 变 <script>,浏览器不再执行
// 2. strip_tags:去掉 HTML 标签
$plain = strip_tags($dirtyHtml);
// 3. 数据库用 PDO 预处理(第 14 章),永远不要拼接 SQL
// 4. CSRF 防护(表单加 token,第 12/16 章会讲)
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION["csrf_token"] = $token;
// 表单里:<input type="hidden" name="csrf" value="<?= $token">- htmlspecialchars:输出到 HTML 时必须调用,把
<>&"'转义,从根本上防 XSS。详见第 16 章安全。 - strip_tags:简单去掉 HTML 标签,适合需要纯文本的场景(评论、备注)。
- PDO 预处理:防 SQL 注入,详见第 14 章。永远不要拼接 SQL!
- CSRF Token:防跨站请求伪造,详见第 12/16 章。
7. 文件上传(简述)
上传文件用 enctype="multipart/form-data",数据放在 $_FILES(不是 $_POST)。这部分内容较多,放在第 15 章单独讲。
8. 实战要点清单
- POST 优先:涉及修改数据(创建/更新/删除)用 POST,绝不用 GET。
- PRG 模式:POST 处理完跳转到 GET 页面,避免刷新重复提交。
- 服务端验证是必须的:前端 HTML5 验证(
required)只是体验,绕过很简单。 - 错误信息不要泄露系统细节:不要直接显示数据库错误,给用户友好提示。
- 密码不用
<input type="text">:必须type="password"。
小结
这一章你掌握了 PHP 表单处理的完整流程:GET/POST 区别、$_GET/$_POST、验证、粘性、过滤、XSS 防御。下一篇会话管理——让用户"登录一次就记住"。
← 上一篇 PHP 字符串
下一篇 PHP 会话管理 →