PHP 表单处理

表单是 Web 应用与用户交互的核心方式。用户登录、注册、搜索、提交数据……都依赖表单。PHP 通过超全局变量 $_GET$_POST$_FILES 接收表单数据,处理起来非常直观。

安全是表单处理的第一原则。永远不要信任用户输入——所有来自用户的数据都可能包含恶意内容,必须经过验证和过滤才能使用。

基本表单接收

PHP 实例
// HTML 表单
// <form action="process.php" method="POST">
//   <input type="text" name="username">
//   <input type="email" name="email">
//   <button type="submit">提交</button>
// </form>

// process.php $username = $_POST['username'] ?? ''; $email = $_POST['email'] ?? '';

echo "用户名:$username"; echo "邮箱:$email"; ``

使用 ?? 空合并运算符提供默认值,避免在字段不存在时产生 Notice 警告。

GET vs POST

两种提交方式各有适用场景,选择正确的方式很重要:

特性GETPOST
数据位置URL 查询字符串请求体(不可见)
数据大小受 URL 长度限制(约 2KB)无限制(受服务器配置影响)
安全性低(数据暴露在 URL 中)较高(不在 URL 中)
可收藏/分享
浏览器缓存会缓存不缓存
适用场景搜索、筛选、分页登录、注册、修改数据
幂等性是(多次请求结果相同)
`php // GET 示例:搜索 // URL: /search.php?keyword=PHP&page=2 $keyword = $_GET['keyword'] ?? ''; $page = (int)($_GET['page'] ?? 1);

// POST 示例:登录 if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = $_POST['username'] ?? ''; $password = $_POST['password'] ?? ''; } `

数据验证与过滤

验证确保数据符合预期格式,过滤清除潜在危险内容:

`php $errors = [];

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

// 验证用户名 if (empty($username)) { $errors[] = "用户名不能为空"; } elseif (strlen($username) < 3 || strlen($username) > 20) { $errors[] = "用户名长度须在 3-20 个字符之间"; } elseif (!preg_match('/^[a-zA-Z0-9_\x{4e00}-\x{9fa5}]+$/u', $username)) { $errors[] = "用户名只能包含字母、数字、下划线和中文"; }

// 验证邮箱(使用 PHP 内置过滤器) if (empty($email)) { $errors[] = "邮箱不能为空"; } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = "邮箱格式不正确"; }

// 验证年龄 $age = filter_var($age, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 120]]); if ($age === false) { $errors[] = "年龄必须是 1-120 之间的整数"; }

// 验证 URL if (!empty($website) && !filter_var($website, FILTER_VALIDATE_URL)) { $errors[] = "网址格式不正确"; }

if (empty($errors)) { echo "验证通过,处理数据..."; } else { foreach ($errors as $error) { echo "<p class='error'>$error</p>"; } } `

防止 XSS 攻击

XSS(跨站脚本攻击)是最常见的 Web 安全漏洞之一,攻击者通过注入恶意脚本窃取用户数据:

`php // 危险:直接输出用户输入 echo $_POST['comment']; // 如果包含 <script>alert('XSS')</script> 会执行!

// 安全:转义 HTML 特殊字符 $comment = htmlspecialchars($_POST['comment'] ?? '', ENT_QUOTES, 'UTF-8'); echo $comment; // <script> 被转义为 &lt;script&gt;,不会执行

// 封装为函数方便使用 function e(string $str): string { return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); }

echo e($_POST['username'] ?? ''); `

规则:所有输出到 HTML 的用户数据都必须经过 htmlspecialchars() 处理。

防止 CSRF 攻击

CSRF(跨站请求伪造)攻击让用户在不知情的情况下执行操作。防御方式是使用 Token:

`php session_start();

// 生成 CSRF Token(在表单页面) if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }

// 在表单中输出 Token // <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">

// 验证 Token(在处理页面) function verifyCsrfToken(): bool { $token = $_POST['csrf_token'] ?? ''; return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token); }

if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!verifyCsrfToken()) { http_response_code(403); die("CSRF 验证失败"); } // 处理表单... } `

文件上传

`php // HTML: <form enctype="multipart/form-data"> // <input type="file" name="avatar" accept="image/*">

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['avatar'])) { $file = $_FILES['avatar']; $maxSize = 2 1024 1024; // 2MB $allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

// 检查上传错误 if ($file['error'] !== UPLOAD_ERR_OK) { $errors = [ UPLOAD_ERR_INI_SIZE => '文件超过 php.ini 限制', UPLOAD_ERR_FORM_SIZE => '文件超过表单限制', UPLOAD_ERR_PARTIAL => '文件只上传了一部分', UPLOAD_ERR_NO_FILE => '没有选择文件', ]; die($errors[$file['error']] ?? '上传失败'); }

// 验证文件大小 if ($file['size'] > $maxSize) { die("文件过大,最大允许 2MB"); }

// 验证 MIME 类型(不能只信任扩展名) $finfo = new finfo(FILEINFO_MIME_TYPE); $mimeType = $finfo->file($file['tmp_name']); if (!in_array($mimeType, $allowed)) { die("只允许上传 JPG/PNG/GIF/WebP 图片"); }

// 生成安全的文件名(不使用用户提供的文件名) $ext = pathinfo($file['name'], PATHINFO_EXTENSION); $filename = uniqid('img_', true) . '.' . strtolower($ext); $dest = __DIR__ . '/uploads/' . $filename;

if (move_uploaded_file($file['tmp_name'], $dest)) { echo "上传成功:/uploads/$filename"; } }

常见问题

Q:如何防止表单重复提交? A:使用 PRG(Post/Redirect/Get)模式:POST 处理完成后,重定向到 GET 页面,避免用户刷新时重复提交。同时可以在 Session 中存储一次性 Token,提交后立即销毁。

Q:filter_var 和手写正则验证哪个更好? A:filter_var 使用 PHP 内置的验证规则,对邮箱、URL、IP 等常见格式验证更可靠,推荐优先使用。对于自定义格式(如手机号、身份证号)则需要手写正则。

Q:上传文件时如何防止恶意文件? A:不能只检查文件扩展名(可以伪造),必须检查 MIME 类型(使用 finfo);不使用用户提供的文件名;上传目录禁止执行权限;图片文件可以用 getimagesize() 二次验证。

Q:$_GET 和 $_POST 数据需要 stripslashes 处理吗? A:不需要。magic_quotes_gpc 在 PHP 5.4 中已被移除,现代 PHP 不会自动添加反斜杠。只需要根据使用场景做 htmlspecialchars`(输出到HTML)或预处理语句(存入数据库)即可。