PHP 完整表单实例
PHP 完整表单实例中文讲解
以下是一个完整的 PHP 表单处理示例,涵盖了用户输入的收集、验证、清理、安全处理以及数据存储,适用于用户注册场景。示例包括 HTML 表单、服务器端验证(包括必需字段、邮箱和 URL 验证)、错误提示、数据回显、CSRF 保护和数据库交互。这个实例旨在展示 PHP 表单处理的全面流程,结合最佳实践和安全性。
功能概述
- 表单字段:姓名(必需)、邮箱(必需,格式验证)、网站 URL(必需,格式验证)、密码(必需,最小长度 6 位)。
- 验证:检查必需字段、验证邮箱和 URL 格式、清理输入数据。
- 安全性:防止 XSS(跨站脚本攻击)、SQL 注入和 CSRF(跨站请求伪造)。
- 数据存储:将验证通过的数据保存到 MySQL 数据库。
- 用户体验:回显用户输入、显示友好错误提示。
完整代码示例
1. 数据库准备
假设使用 MySQL 数据库,创建名为 test
的数据库和 users
表:
CREATE DATABASE test;
USE test;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
website VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
2. HTML 表单 (register.html
)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
<style>
.error { color: red; }
label { display: inline-block; width: 100px; }
input[type="text"], input[type="email"], input[type="url"], input[type="password"] {
width: 200px; margin-bottom: 10px;
}
</style>
</head>
<body>
<h2>用户注册</h2>
<form action="register.php" method="post">
<?php
// 启动会话以生成 CSRF 令牌
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<label for="name">姓名*:</label>
<input type="text" id="name" name="name" value="<?php echo isset($_POST['name']) ? htmlspecialchars($_POST['name']) : ''; ?>">
<br>
<label for="email">邮箱*:</label>
<input type="email" id="email" name="email" value="<?php echo isset($_POST['email']) ? htmlspecialchars($_POST['email']) : ''; ?>">
<br>
<label for="website">网站 URL*:</label>
<input type="url" id="website" name="website" value="<?php echo isset($_POST['website']) ? htmlspecialchars($_POST['website']) : ''; ?>">
<br>
<label for="password">密码*:</label>
<input type="password" id="password" name="password">
<br>
<input type="submit" value="注册">
<p>* 表示必需字段</p>
</form>
</body>
</html>
3. PHP 处理脚本 (register.php
)
<?php
// 启动会话以验证 CSRF 令牌
session_start();
// 初始化错误和数据数组
$errors = [];
$data = [];
// 检查请求方法
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 验证 CSRF 令牌
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
$errors[] = "CSRF 验证失败";
} else {
// 获取并清理输入
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$website = trim($_POST['website'] ?? '');
$password = $_POST['password'] ?? '';
// 清理输入数据
$name = filter_var($name, FILTER_SANITIZE_STRING);
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
$website = filter_var($website, FILTER_SANITIZE_URL);
// 验证必需字段
if (empty($name)) {
$errors[] = "姓名是必需字段";
}
if (empty($email)) {
$errors[] = "邮箱是必需字段";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "无效的邮箱格式";
}
if (empty($website)) {
$errors[] = "网站 URL 是必需字段";
} elseif (!filter_var($website, FILTER_VALIDATE_URL)) {
$errors[] = "无效的 URL 格式";
} elseif (!preg_match("/^https?:\/\//", $website)) {
$errors[] = "URL 必须以 http:// 或 https:// 开头";
}
if (empty($password)) {
$errors[] = "密码是必需字段";
} elseif (strlen($password) < 6) {
$errors[] = "密码长度至少为 6 位";
}
// 如果没有错误,处理数据
if (empty($errors)) {
try {
// 连接数据库
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 检查邮箱是否已存在
$stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE email = :email");
$stmt->execute(['email' => $email]);
if ($stmt->fetchColumn() > 0) {
$errors[] = "邮箱已被注册";
} else {
// 插入数据
$stmt = $pdo->prepare("INSERT INTO users (name, email, website, password) VALUES (:name, :email, :website, :password)");
$stmt->execute([
'name' => $name,
'email' => $email,
'website' => $website,
'password' => password_hash($password, PASSWORD_DEFAULT) // 加密密码
]);
echo "注册成功!";
// 重置 CSRF 令牌
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
exit;
}
} catch (PDOException $e) {
$errors[] = "数据库错误: " . $e->getMessage();
}
}
}
}
// 显示错误并回显表单
if (!empty($errors)) {
echo "<div class='error'><ul>";
foreach ($errors as $error) {
echo "<li>$error</li>";
}
echo "</ul></div>";
include 'register.html';
} else {
// 非 POST 请求,显示表单
include 'register.html';
}
?>
代码解析
- HTML 表单:
- 使用
method="post"
提交数据,enctype
未设置(无文件上传)。 - 包含 CSRF 令牌的隐藏字段
<input type="hidden" name="csrf_token">
。 - 使用
htmlspecialchars()
回显用户输入,防止 XSS 攻击。 - 添加简单的 CSS 样式,增强用户体验。
- PHP 处理逻辑:
- CSRF 保护:验证 CSRF 令牌,确保请求合法。
- 输入清理:使用
trim()
移除空格,filter_var()
清理邮箱和 URL。 - 必需字段验证:检查
name
、email
、website
和password
是否为空。 - 格式验证:
- 邮箱:
filter_var($email, FILTER_VALIDATE_EMAIL)
。 - URL:
filter_var($website, FILTER_VALIDATE_URL)
和正则表达式确保协议为http://
或https://
。 - 密码:长度至少 6 位。
- 邮箱:
- 唯一性检查:查询数据库,防止重复邮箱。
- 数据库操作:使用 PDO 预处理语句,防止 SQL 注入;密码使用
password_hash()
加密。 - 错误处理:集中存储错误,统一显示,并回显表单。
- 用户体验:
- 回显用户输入(如
name
和email
),避免重复填写。 - 错误提示清晰(如“无效的邮箱格式”),显示在表单上方。
- 成功提交后显示确认消息并重置 CSRF 令牌。
配置说明
- 数据库连接:替换
register.php
中的user
和pass
为实际的 MySQL 用户名和密码,数据库名为test
。 - 文件结构:
project/
├── register.html
├── register.php
- 服务器环境:确保 PHP 环境支持 PDO 和 MySQL 扩展,运行在支持会话的 Web 服务器(如 Apache 或 Nginx)上。
安全性措施
- XSS 防护:使用
htmlspecialchars()
转义输出。 - SQL 注入防护:使用 PDO 预处理语句。
- CSRF 防护:生成和验证 CSRF 令牌。
- 密码安全:使用
password_hash()
存储加密密码。
扩展功能
- 客户端验证:
在 HTML 中添加required
和类型属性:
<input type="email" name="email" required>
<input type="url" name="website" required>
结合 JavaScript 增强客户端验证,但服务器端验证不可省略。
- 文件上传:
添加文件上传字段:
<input type="file" name="avatar">
PHP 验证:
if ($_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
$allowed = ['jpg', 'png'];
$ext = pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION);
if (!in_array($ext, $allowed)) {
$errors[] = "只允许 JPG 或 PNG 文件";
}
if ($_FILES['avatar']['size'] > 2 * 1024 * 1024) {
$errors[] = "文件大小不能超过 2MB";
}
} else {
$errors[] = "必须上传头像";
}
- 重定向:
成功注册后重定向到成功页面:
if (empty($errors)) {
// 数据库操作后
header("Location: success.php");
exit;
}
最佳实践
- 验证优先:先验证必需字段,再验证格式,减少不必要开销。
- 错误管理:使用数组集中存储错误,统一显示。
- 清理输入:使用
filter_var()
和trim()
确保数据干净。 - 安全优先:始终防范 XSS、SQL 注入和 CSRF。
- 用户友好:提供具体错误提示,回显用户输入。
参考资源
- PHP 官方手册 – PDO:数据库操作。
- 菜鸟教程 – PHP 表单处理:基础示例。
- OWASP 安全指南:Web 安全最佳实践。
总结
这个完整表单实例展示了 PHP 表单处理的全部流程,从用户输入收集、验证到数据存储,涵盖了必需字段、邮箱和 URL 验证、安全性措施和用户体验优化。开发者可以基于此示例扩展功能,如添加更多字段、集成前端验证或实现复杂业务逻辑,适用于用户注册、资料提交等场景。