PHP 表单处理
表单是 Web 应用与用户交互的核心方式。用户登录、注册、搜索、提交数据……都依赖表单。PHP 通过超全局变量 $_GET、$_POST、$_FILES 接收表单数据,处理起来非常直观。
安全是表单处理的第一原则。永远不要信任用户输入——所有来自用户的数据都可能包含恶意内容,必须经过验证和过滤才能使用。
基本表单接收
// 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
两种提交方式各有适用场景,选择正确的方式很重要:
特性 GET POST 数据位置 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> 被转义为 <script>,不会执行
// 封装为函数方便使用 function e(string $str): string { return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); }
echo e($_POST['username'] ?? '');
htmlspecialchars()规则:所有输出到 HTML 的用户数据都必须经过
处理。`防止 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)或预处理语句(存入数据库)即可。