本章节将提供一个完整的PHP表单处理实例,涵盖表单验证、数据保持、错误处理、用户反馈等所有核心功能。通过这个实例,您可以学习如何构建一个健壮、安全、用户友好的PHP表单系统。
这个完整的实例包含以下功能:
完整的表单处理文件 (complete_form.php):
<?php
// ============================================
// 完整的PHP表单处理实例
// 功能:用户注册表单
// ============================================
// 开启会话(用于防重复提交和CSRF令牌)
session_start();
// 定义变量并初始化为空值
$name = $email = $phone = $website = $dob = $bio = $country = $newsletter = '';
$gender = $interests = [];
$nameErr = $emailErr = $phoneErr = $websiteErr = $dobErr = $genderErr = $countryErr = '';
// 定义验证规则
$validationRules = [
'name' => [
'required' => true,
'pattern' => '/^[a-zA-Z\x{4e00}-\x{9fa5}\s]{2,50}$/u',
'message' => '姓名应为2-50个字符,只能包含字母、中文和空格'
],
'email' => [
'required' => true,
'type' => 'email',
'message' => '请输入有效的邮箱地址'
],
'phone' => [
'required' => true,
'pattern' => '/^1[3-9]\d{9}$/',
'message' => '请输入有效的手机号码(11位数字)'
],
'website' => [
'required' => false,
'type' => 'url',
'message' => '请输入有效的网址'
],
'dob' => [
'required' => true,
'type' => 'date',
'message' => '请输入有效的出生日期'
],
'gender' => [
'required' => true,
'message' => '请选择性别'
],
'country' => [
'required' => true,
'message' => '请选择国家'
]
];
// 检查表单是否提交
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 检查表单令牌(防止重复提交)
if (!isset($_POST['form_token']) || $_POST['form_token'] !== ($_SESSION['form_token'] ?? '')) {
die("表单令牌无效或已过期");
}
// 验证姓名
if (empty($_POST["name"])) {
$nameErr = "姓名是必填的";
} else {
$name = clean_input($_POST["name"]);
if (!preg_match($validationRules['name']['pattern'], $name)) {
$nameErr = $validationRules['name']['message'];
}
}
// 验证邮箱
if (empty($_POST["email"])) {
$emailErr = "邮箱是必填的";
} else {
$email = clean_input($_POST["email"]);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$emailErr = $validationRules['email']['message'];
}
}
// 验证手机号码
if (empty($_POST["phone"])) {
$phoneErr = "手机号码是必填的";
} else {
$phone = clean_input($_POST["phone"]);
if (!preg_match($validationRules['phone']['pattern'], $phone)) {
$phoneErr = $validationRules['phone']['message'];
}
}
// 验证网址(可选)
if (!empty($_POST["website"])) {
$website = clean_input($_POST["website"]);
if (!filter_var($website, FILTER_VALIDATE_URL)) {
$websiteErr = $validationRules['website']['message'];
}
}
// 验证出生日期
if (empty($_POST["dob"])) {
$dobErr = "出生日期是必填的";
} else {
$dob = clean_input($_POST["dob"]);
// 检查是否为有效日期
$date_parts = explode('-', $dob);
if (count($date_parts) != 3 || !checkdate($date_parts[1], $date_parts[2], $date_parts[0])) {
$dobErr = "请输入有效的出生日期(YYYY-MM-DD格式)";
} else {
// 检查年龄是否大于18岁
$birth_date = new DateTime($dob);
$today = new DateTime();
$age = $birth_date->diff($today)->y;
if ($age < 18) {
$dobErr = "注册用户必须年满18岁";
}
}
}
// 验证性别
if (empty($_POST["gender"])) {
$genderErr = "请选择性别";
} else {
$gender = clean_input($_POST["gender"]);
$allowed_genders = ['male', 'female', 'other'];
if (!in_array($gender, $allowed_genders)) {
$genderErr = "无效的性别选择";
}
}
// 验证国家
if (empty($_POST["country"])) {
$countryErr = "请选择国家";
} else {
$country = clean_input($_POST["country"]);
$allowed_countries = ['china', 'usa', 'uk', 'japan', 'germany'];
if (!in_array($country, $allowed_countries)) {
$countryErr = "无效的国家选择";
}
}
// 处理兴趣爱好(可选,多选框)
$interests = isset($_POST['interests']) ? $_POST['interests'] : [];
$interests = array_map('clean_input', $interests);
// 处理个人简介(可选)
$bio = !empty($_POST["bio"]) ? clean_input($_POST["bio"]) : '';
// 处理订阅选项
$newsletter = isset($_POST["newsletter"]) ? 'yes' : 'no';
// 如果所有验证通过
if (empty($nameErr) && empty($emailErr) && empty($phoneErr) && empty($websiteErr) &&
empty($dobErr) && empty($genderErr) && empty($countryErr)) {
// 清除表单令牌(防止重复提交)
unset($_SESSION['form_token']);
// 在实际应用中,这里可以保存到数据库
// save_to_database($name, $email, $phone, $website, $dob, $gender, $interests, $bio, $country, $newsletter);
// 显示成功消息
display_success_message($name, $email, $phone, $website, $dob, $gender, $interests, $bio, $country, $newsletter);
// 不要显示表单
exit;
}
}
// 如果没有提交或验证失败,生成新的表单令牌
$_SESSION['form_token'] = bin2hex(random_bytes(32));
/**
* 数据清理函数
*/
function clean_input($data) {
if (empty($data)) return '';
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
return $data;
}
/**
* 显示成功消息
*/
function display_success_message($name, $email, $phone, $website, $dob, $gender, $interests, $bio, $country, $newsletter) {
$country_names = [
'china' => '中国',
'usa' => '美国',
'uk' => '英国',
'japan' => '日本',
'germany' => '德国'
];
$gender_names = [
'male' => '男',
'female' => '女',
'other' => '其他'
];
$interest_names = [
'coding' => '编程',
'reading' => '阅读',
'music' => '音乐',
'sports' => '运动',
'travel' => '旅行'
];
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>注册成功</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
.success-container {
background: #dff0d8;
border: 1px solid #d6e9c6;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.success-header {
color: #3c763d;
border-bottom: 2px solid #d6e9c6;
padding-bottom: 15px;
margin-bottom: 20px;
}
.user-info {
background: white;
padding: 20px;
border-radius: 5px;
margin-top: 20px;
}
.info-item { margin-bottom: 10px; }
.info-label { font-weight: bold; color: #555; }
.action-buttons { margin-top: 30px; }
.btn {
display: inline-block;
padding: 10px 20px;
margin-right: 10px;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
}
.btn-primary { background: #337ab7; color: white; }
.btn-secondary { background: #f0f0f0; color: #333; border: 1px solid #ccc; }
</style>
</head>
<body>
<div class="success-container">
<div class="success-header">
<h1>✓ 注册成功!</h1>
<p>感谢您注册我们的服务。您的信息已成功提交。</p>
</div>
<h3>您的注册信息:</h3>
<div class="user-info">
<div class="info-item">
<span class="info-label">姓名:</span> <?= htmlspecialchars($name) ?>
</div>
<div class="info-item">
<span class="info-label">邮箱:</span> <?= htmlspecialchars($email) ?>
</div>
<div class="info-item">
<span class="info-label">手机号码:</span> <?= htmlspecialchars($phone) ?>
</div>
<?php if (!empty($website)): ?>
<div class="info-item">
<span class="info-label">个人网站:</span>
<a href="<?= htmlspecialchars($website) ?>" target="_blank"><?= htmlspecialchars($website) ?></a>
</div>
<?php endif; ?>
<div class="info-item">
<span class="info-label">出生日期:</span> <?= htmlspecialchars($dob) . ' (' . (date('Y') - substr($dob, 0, 4)) . '岁)' ?>
</div>
<div class="info-item">
<span class="info-label">性别:</span> <?= $gender_names[$gender] ?? $gender ?>
</div>
<div class="info-item">
<span class="info-label">国家:</span> <?= $country_names[$country] ?? $country ?>
</div>
<?php if (!empty($interests)): ?>
<div class="info-item">
<span class="info-label">兴趣爱好:</span>
<?php
$interest_labels = [];
foreach ($interests as $interest) {
$interest_labels[] = $interest_names[$interest] ?? $interest;
}
echo implode(', ', $interest_labels);
?>
</div>
<?php endif; ?>
<?php if (!empty($bio)): ?>
<div class="info-item">
<span class="info-label">个人简介:</span>
<div style="margin-top: 5px;"><?= nl2br(htmlspecialchars($bio)) ?></div>
</div>
<?php endif; ?>
<div class="info-item">
<span class="info-label">订阅新闻:</span> <?= $newsletter == 'yes' ? '是' : '否' ?>
</div>
</div>
<div class="action-buttons">
<a href="complete_form.php" class="btn btn-primary">返回表单</a>
<a href="/" class="btn btn-secondary">返回首页</a>
</div>
</div>
</body>
</html>
<?php
}
// 显示表单
display_form();
exit;
/**
* 显示表单
*/
function display_form() {
global $name, $email, $phone, $website, $dob, $bio, $country, $newsletter, $gender, $interests;
global $nameErr, $emailErr, $phoneErr, $websiteErr, $dobErr, $genderErr, $countryErr;
$form_token = $_SESSION['form_token'] ?? '';
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户注册表单 - 完整实例</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.form-wrapper {
background: white;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
overflow: hidden;
margin-top: 30px;
}
.form-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.form-header h1 {
font-size: 28px;
margin-bottom: 10px;
}
.form-header p {
opacity: 0.9;
font-size: 16px;
}
.form-body {
padding: 40px;
}
.form-section {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
.form-section:last-child {
border-bottom: none;
}
.form-group {
margin-bottom: 25px;
}
.form-row {
display: flex;
flex-wrap: wrap;
margin: 0 -10px;
}
.form-col {
flex: 1;
min-width: 250px;
padding: 0 10px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #444;
}
.required::after {
content: " *";
color: #e74c3c;
}
input[type="text"],
input[type="email"],
input[type="tel"],
input[type="date"],
input[type="url"],
select,
textarea {
width: 100%;
padding: 12px 15px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 16px;
transition: all 0.3s ease;
background: #f8f9fa;
}
input:focus,
select:focus,
textarea:focus {
outline: none;
border-color: #667eea;
background: white;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.radio-group,
.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-top: 5px;
}
.radio-item,
.checkbox-item {
display: flex;
align-items: center;
}
.radio-item input[type="radio"],
.checkbox-item input[type="checkbox"] {
margin-right: 8px;
width: 18px;
height: 18px;
}
.error {
color: #e74c3c;
font-size: 14px;
margin-top: 5px;
display: block;
}
.form-actions {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.btn {
padding: 14px 32px;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 140px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #f8f9fa;
color: #444;
border: 2px solid #e1e5e9;
}
.btn-secondary:hover {
background: #e9ecef;
border-color: #d1d7e0;
}
.form-note {
text-align: center;
margin-top: 20px;
color: #666;
font-size: 14px;
}
@media (max-width: 768px) {
.form-col {
min-width: 100%;
margin-bottom: 15px;
}
.form-body {
padding: 20px;
}
.form-header {
padding: 20px;
}
.form-actions {
flex-direction: column;
}
.btn {
width: 100%;
}
}
</style>
</head>
<body>
<div class="container">
<div class="form-wrapper">
<div class="form-header">
<h1>用户注册</h1>
<p>请填写以下信息完成注册,带 <span style="color:#e74c3c">*</span> 的字段为必填项</p>
</div>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" class="form-body">
<input type="hidden" name="form_token" value="<?php echo $form_token; ?>">
<div class="form-section">
<h3 style="margin-bottom: 20px; color: #444;">基本信息</h3>
<div class="form-row">
<div class="form-col">
<div class="form-group">
<label for="name" class="required">姓名</label>
<input type="text" id="name" name="name"
value="<?php echo htmlspecialchars($name); ?>"
placeholder="请输入您的姓名">
<span class="error"><?php echo $nameErr; ?></span>
</div>
</div>
<div class="form-col">
<div class="form-group">
<label for="email" class="required">邮箱地址</label>
<input type="email" id="email" name="email"
value="<?php echo htmlspecialchars($email); ?>"
placeholder="example@domain.com">
<span class="error"><?php echo $emailErr; ?></span>
</div>
</div>
</div>
<div class="form-row">
<div class="form-col">
<div class="form-group">
<label for="phone" class="required">手机号码</label>
<input type="tel" id="phone" name="phone"
value="<?php echo htmlspecialchars($phone); ?>"
placeholder="13800138000">
<span class="error"><?php echo $phoneErr; ?></span>
</div>
</div>
<div class="form-col">
<div class="form-group">
<label for="dob" class="required">出生日期</label>
<input type="date" id="dob" name="dob"
value="<?php echo htmlspecialchars($dob); ?>"
max="<?php echo date('Y-m-d', strtotime('-18 years')); ?>">
<span class="error"><?php echo $dobErr; ?></span>
</div>
</div>
</div>
</div>
<div class="form-section">
<h3 style="margin-bottom: 20px; color: #444;">其他信息</h3>
<div class="form-group">
<label for="website">个人网站</label>
<input type="url" id="website" name="website"
value="<?php echo htmlspecialchars($website); ?>"
placeholder="https://example.com">
<span class="error"><?php echo $websiteErr; ?></span>
</div>
<div class="form-group">
<label class="required">性别</label>
<div class="radio-group">
<div class="radio-item">
<input type="radio" id="male" name="gender" value="male"
<?php if ($gender == "male") echo "checked"; ?>>
<label for="male">男</label>
</div>
<div class="radio-item">
<input type="radio" id="female" name="gender" value="female"
<?php if ($gender == "female") echo "checked"; ?>>
<label for="female">女</label>
</div>
<div class="radio-item">
<input type="radio" id="other" name="gender" value="other"
<?php if ($gender == "other") echo "checked"; ?>>
<label for="other">其他</label>
</div>
</div>
<span class="error"><?php echo $genderErr; ?></span>
</div>
<div class="form-group">
<label class="required">国家</label>
<select id="country" name="country">
<option value="">请选择国家</option>
<option value="china" <?php if ($country == "china") echo "selected"; ?>>中国</option>
<option value="usa" <?php if ($country == "usa") echo "selected"; ?>>美国</option>
<option value="uk" <?php if ($country == "uk") echo "selected"; ?>>英国</option>
<option value="japan" <?php if ($country == "japan") echo "selected"; ?>>日本</option>
<option value="germany" <?php if ($country == "germany") echo "selected"; ?>>德国</option>
</select>
<span class="error"><?php echo $countryErr; ?></span>
</div>
<div class="form-group">
<label>兴趣爱好</label>
<div class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" id="coding" name="interests[]" value="coding"
<?php if (in_array('coding', $interests)) echo "checked"; ?>>
<label for="coding">编程</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="reading" name="interests[]" value="reading"
<?php if (in_array('reading', $interests)) echo "checked"; ?>>
<label for="reading">阅读</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="music" name="interests[]" value="music"
<?php if (in_array('music', $interests)) echo "checked"; ?>>
<label for="music">音乐</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="sports" name="interests[]" value="sports"
<?php if (in_array('sports', $interests)) echo "checked"; ?>>
<label for="sports">运动</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="travel" name="interests[]" value="travel"
<?php if (in_array('travel', $interests)) echo "checked"; ?>>
<label for="travel">旅行</label>
</div>
</div>
</div>
<div class="form-group">
<label for="bio">个人简介</label>
<textarea id="bio" name="bio" rows="4"
placeholder="请简单介绍一下自己..."><?php echo htmlspecialchars($bio); ?></textarea>
</div>
<div class="form-group">
<div class="checkbox-item">
<input type="checkbox" id="newsletter" name="newsletter" value="1"
<?php if ($newsletter == 'yes') echo "checked"; ?>>
<label for="newsletter">订阅我们的新闻和更新</label>
</div>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
提交注册
</button>
<button type="reset" class="btn btn-secondary">
重置表单
</button>
</div>
<div class="form-note">
<p>我们承诺保护您的隐私,您的信息将仅用于服务目的。</p>
</div>
</form>
</div>
</div>
<script>
// 客户端验证增强
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form');
// 出生日期最大限制(18岁以上)
const dobInput = document.getElementById('dob');
const maxDate = new Date();
maxDate.setFullYear(maxDate.getFullYear() - 18);
dobInput.max = maxDate.toISOString().split('T')[0];
// 表单提交前验证
form.addEventListener('submit', function(e) {
let isValid = true;
// 清除之前的错误样式
document.querySelectorAll('.error-highlight').forEach(el => {
el.classList.remove('error-highlight');
});
// 验证必填字段
const requiredFields = form.querySelectorAll('[class*="required"]');
requiredFields.forEach(label => {
const input = label.closest('.form-group').querySelector('input, select, textarea');
if (!input || (input.type === 'checkbox' || input.type === 'radio')) {
// 对于单选按钮组,需要特殊处理
const radioGroup = label.closest('.form-group').querySelectorAll('input[type="radio"]');
if (radioGroup.length > 0) {
let radioChecked = false;
radioGroup.forEach(radio => {
if (radio.checked) radioChecked = true;
});
if (!radioChecked) {
isValid = false;
label.style.color = '#e74c3c';
}
}
} else if (!input.value.trim()) {
isValid = false;
input.classList.add('error-highlight');
input.style.borderColor = '#e74c3c';
}
});
if (!isValid) {
e.preventDefault();
alert('请填写所有必填字段!');
}
});
// 实时验证反馈
const validateEmail = (email) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
const validatePhone = (phone) => {
return /^1[3-9]\d{9}$/.test(phone);
};
// 邮箱实时验证
const emailInput = document.getElementById('email');
if (emailInput) {
emailInput.addEventListener('blur', function() {
if (this.value && !validateEmail(this.value)) {
this.style.borderColor = '#e74c3c';
} else {
this.style.borderColor = '#667eea';
}
});
}
// 手机号实时验证
const phoneInput = document.getElementById('phone');
if (phoneInput) {
phoneInput.addEventListener('blur', function() {
if (this.value && !validatePhone(this.value)) {
this.style.borderColor = '#e74c3c';
} else {
this.style.borderColor = '#667eea';
}
});
}
});
</script>
</body>
</html>
<?php
}
?>
| 功能模块 | 实现方法 | 说明 |
|---|---|---|
| 表单数据保持 | value="<?php echo htmlspecialchars($variable); ?>" |
提交失败时保留用户输入,避免重复填写 |
| 防重复提交 | 表单令牌(Form Token) | 使用session存储唯一令牌,防止表单重复提交 |
| 数据清理 | clean_input()函数 |
移除空白字符、转义特殊字符,防止XSS攻击 |
| 验证规则 | $validationRules数组 |
集中管理验证规则,便于维护 |
| 友好的错误提示 | <span class="error"> |
在对应字段下方显示具体错误信息 |
| 成功页面 | display_success_message() |
验证成功后显示用户提交的信息 |
| 响应式设计 | CSS媒体查询 | 适配不同屏幕尺寸的设备 |
| 客户端验证 | JavaScript增强 | 提供实时反馈,改善用户体验 |
complete_form.php文件display_success_message()函数中添加数据库保存逻辑$validationRules数组来调整验证规则您可以根据需要为这个实例添加以下扩展功能:
问题1:表单提交后数据没有保持
// 确保在HTML中正确输出变量值
<input type="text" name="name" value="<?php echo htmlspecialchars($name); ?>">
问题2:单选按钮和复选框没有保持选中状态
// 单选按钮
<input type="radio" name="gender" value="male" <?php if ($gender == "male") echo "checked"; ?>>
// 复选框(数组)
<input type="checkbox" name="interests[]" value="coding"
<?php if (in_array('coding', $interests)) echo "checked"; ?>>
问题3:表单令牌无效
// 确保session_start()在脚本开头调用
session_start();
// 确保表单令牌正确生成和验证
$_SESSION['form_token'] = bin2hex(random_bytes(32));
// 在表单中:<input type="hidden" name="form_token" value="<?php echo $_SESSION['form_token']; ?>">