PHP 表单处理
本章将介绍 PHP 如何接收、验证和处理表单数据。
表单基础
HTML 表单
<form action="process.php" method="post">
<label>用户名:<input type="text" name="username"></label>
<label>邮箱:<input type="email" name="email"></label>
<label>密码:<input type="password" name="password"></label>
<button type="submit">提交</button>
</form>
接收表单数据
<?php
// GET 方式
$username = $_GET['username'];
// POST 方式
$username = $_POST['username'];
// GET 或 POST
$username = $_REQUEST['username'];
// 检查请求方法
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
}
?>
数据过滤
基本过滤
<?php
// 去除两端空白
$username = trim($_POST['username']);
// 去除 HTML 标签
$comment = strip_tags($_POST['comment']);
// 转义 HTML 实体
$comment = htmlspecialchars($_POST['comment']);
// 去除斜杠(如果 magic_quotes 开启)
$text = stripslashes($_POST['text']);
?>
filter_input 函数
<?php
// 过滤单个输入
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
// 过滤并清理
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
// 过滤多个输入
$filters = [
'username' => FILTER_SANITIZE_STRING,
'email' => FILTER_VALIDATE_EMAIL,
'age' => [
'filter' => FILTER_VALIDATE_INT,
'options' => ['min_range' => 1, 'max_range' => 120]
]
];
$data = filter_input_array(INPUT_POST, $filters);
// 验证结果
if ($data['email'] === false) {
echo "邮箱格式无效";
}
?>
常用过滤器
<?php
// 验证过滤器
FILTER_VALIDATE_EMAIL // 验证邮箱
FILTER_VALIDATE_URL // 验证 URL
FILTER_VALIDATE_INT // 验证整数
FILTER_VALIDATE_FLOAT // 验证浮点数
FILTER_VALIDATE_BOOLEAN // 验证布尔值
FILTER_VALIDATE_IP // 验证 IP 地址
FILTER_VALIDATE_MAC // 验证 MAC 地址
FILTER_VALIDATE_REGEXP // 正则验证
// 清理过滤器
FILTER_SANITIZE_EMAIL // 清理邮箱
FILTER_SANITIZE_URL // 清理 URL
FILTER_SANITIZE_STRING // 清理字符串(已弃用,PHP 8.1+)
FILTER_SANITIZE_SPECIAL_CHARS // 转义特殊字符
FILTER_SANITIZE_NUMBER_INT // 只保留数字和 +-
FILTER_SANITIZE_NUMBER_FLOAT // 只保留数字和 +-.,eE
?>
表单验证
基本验证
<?php
$errors = [];
// 必填验证
if (empty($_POST['username'])) {
$errors['username'] = '用户名不能为空';
}
// 长度验证
if (strlen($_POST['username']) < 3 || strlen($_POST['username']) > 20) {
$errors['username'] = '用户名长度必须在 3-20 个字符之间';
}
// 邮箱验证
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = '邮箱格式无效';
}
// 数字范围验证
$age = filter_var($_POST['age'], FILTER_VALIDATE_INT, [
'options' => ['min_range' => 1, 'max_range' => 120]
]);
if ($age === false) {
$errors['age'] = '年龄必须在 1-120 之间';
}
// 正则验证
if (!preg_match('/^[a-zA-Z][a-zA-Z0-9_]{2,19}$/', $_POST['username'])) {
$errors['username'] = '用户名必须以字母开头,只能包含字母、数字和下划线';
}
// 密码确认
if ($_POST['password'] !== $_POST['password_confirm']) {
$errors['password'] = '两次密码输入不一致';
}
?>
完整验证示例
<?php
class FormValidator {
private $data;
private $errors = [];
public function __construct(array $data) {
$this->data = $data;
}
public function validate() {
$this->validateUsername();
$this->validateEmail();
$this->validatePassword();
$this->validateAge();
return empty($this->errors);
}
private function validateUsername() {
$username = trim($this->data['username'] ?? '');
if (empty($username)) {
$this->errors['username'] = '用户名不能为空';
return;
}
if (strlen($username) < 3 || strlen($username) > 20) {
$this->errors['username'] = '用户名长度必须在 3-20 个字符之间';
return;
}
if (!preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $username)) {
$this->errors['username'] = '用户名必须以字母开头,只能包含字母、数字和下划线';
}
}
private function validateEmail() {
$email = trim($this->data['email'] ?? '');
if (empty($email)) {
$this->errors['email'] = '邮箱不能为空';
return;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->errors['email'] = '邮箱格式无效';
}
}
private function validatePassword() {
$password = $this->data['password'] ?? '';
$confirm = $this->data['password_confirm'] ?? '';
if (strlen($password) < 8) {
$this->errors['password'] = '密码至少 8 个字符';
return;
}
if (!preg_match('/[A-Z]/', $password)) {
$this->errors['password'] = '密码必须包含大写字母';
return;
}
if (!preg_match('/[a-z]/', $password)) {
$this->errors['password'] = '密码必须包含小写字母';
return;
}
if (!preg_match('/[0-9]/', $password)) {
$this->errors['password'] = '密码必须包含数字';
return;
}
if ($password !== $confirm) {
$this->errors['password'] = '两次密码输入不一致';
}
}
private function validateAge() {
$age = $this->data['age'] ?? '';
if (!is_numeric($age) || $age < 1 || $age > 120) {
$this->errors['age'] = '年龄必须在 1-120 之间';
}
}
public function getErrors() {
return $this->errors;
}
public function getError($field) {
return $this->errors[$field] ?? null;
}
}
// 使用
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$validator = new FormValidator($_POST);
if ($validator->validate()) {
// 验证通过,处理数据
$username = trim($_POST['username']);
$email = trim($_POST['email']);
// ...
} else {
// 显示错误
foreach ($validator->getErrors() as $field => $error) {
echo "$field: $error<br>";
}
}
}
?>
表单安全
CSRF 防护
<?php
session_start();
// 生成 CSRF Token
function generateCsrfToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// 验证 CSRF Token
function verifyCsrfToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
?>
<!-- 表单中添加 Token -->
<form method="post">
<input type="hidden" name="csrf_token" value="<?php echo generateCsrfToken(); ?>">
<!-- 其他字段 -->
</form>
<?php
// 验证 Token
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verifyCsrfToken($_POST['csrf_token'] ?? '')) {
die('CSRF 验证失败');
}
// 处理表单...
}
?>
XSS 防护
<?php
// 输出时转义
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
// 辅助函数
function e($string) {
return htmlspecialchars($string ?? '', ENT_QUOTES, 'UTF-8');
}
?>
<!-- 在 HTML 中使用 -->
<p>用户名:<?php echo e($username); ?></p>
<input type="text" value="<?php echo e($value); ?>">
SQL 注入防护
<?php
// 使用 PDO 预处理语句
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?');
$stmt->execute([$_POST['username']]);
// 命名参数
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $_POST['email']]);
?>
文件上传表单
<form method="post" enctype="multipart/form-data">
<input type="file" name="avatar" accept="image/*">
<button type="submit">上传</button>
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$file = $_FILES['avatar'];
// 验证错误码
if ($file['error'] !== UPLOAD_ERR_OK) {
die('上传失败');
}
// 验证文件类型
$allowed = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($file['type'], $allowed)) {
die('不支持的文件类型');
}
// 验证文件大小
$maxSize = 2 * 1024 * 1024; // 2MB
if ($file['size'] > $maxSize) {
die('文件太大');
}
// 验证文件内容(更安全)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!in_array($mime, $allowed)) {
die('文件类型验证失败');
}
// 移动文件
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$newName = uniqid() . '.' . $ext;
move_uploaded_file($file['tmp_name'], 'uploads/' . $newName);
}
?>
表单重填
<?php
session_start();
// 保存表单数据
function old($field, $default = '') {
return $_SESSION['old'][$field] ?? $default;
}
// 清除旧数据
function clearOld() {
unset($_SESSION['old']);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['old'] = $_POST;
$validator = new FormValidator($_POST);
if (!$validator->validate()) {
$_SESSION['errors'] = $validator->getErrors();
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
clearOld();
// 处理数据...
}
?>
<!-- 表单中显示旧值和错误 -->
<input type="text" name="username" value="<?php echo e(old('username')); ?>">
<?php if ($error = $_SESSION['errors']['username'] ?? null): ?>
<span class="error"><?php echo e($error); ?></span>
<?php endif; ?>
小结
本章我们学习了:
- 表单数据接收:_POST、$_REQUEST
- 数据过滤:trim、strip_tags、htmlspecialchars、filter_input
- 表单验证:必填、长度、格式、范围验证
- 表单安全:CSRF、XSS、SQL 注入防护
- 文件上传表单处理
- 表单重填功能
下一章我们将学习会话管理。