本文目录导读:

我来详细介绍PHP项目中个人资料修改功能的开发流程和最佳实践。
数据库设计
用户表结构
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL,
`phone` varchar(20) DEFAULT NULL,
`avatar` varchar(255) DEFAULT NULL,
`bio` text DEFAULT NULL,
`gender` enum('male','female','other') DEFAULT NULL,
`birthday` date DEFAULT NULL,
`website` varchar(255) DEFAULT NULL,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
前端HTML表单
<!-- profile/edit.php -->
<form id="profileForm" action="update_profile.php" method="POST" enctype="multipart/form-data">
<div class="form-group">
<label>头像</label>
<div class="avatar-upload">
<img src="<?php echo $user['avatar']; ?>" id="avatarPreview">
<input type="file" name="avatar" accept="image/*" onchange="previewAvatar(this)">
</div>
</div>
<div class="form-group">
<label>用户名</label>
<input type="text" name="username" value="<?php echo htmlspecialchars($user['username']); ?>" required>
</div>
<div class="form-group">
<label>邮箱</label>
<input type="email" name="email" value="<?php echo htmlspecialchars($user['email']); ?>" required>
</div>
<div class="form-group">
<label>手机号</label>
<input type="tel" name="phone" value="<?php echo htmlspecialchars($user['phone']); ?>">
</div>
<div class="form-group">
<label>个人简介</label>
<textarea name="bio" rows="4"><?php echo htmlspecialchars($user['bio']); ?></textarea>
</div>
<div class="form-group">
<label>性别</label>
<select name="gender">
<option value="male" <?php echo $user['gender'] == 'male' ? 'selected' : ''; ?>>男</option>
<option value="female" <?php echo $user['gender'] == 'female' ? 'selected' : ''; ?>>女</option>
<option value="other" <?php echo $user['gender'] == 'other' ? 'selected' : ''; ?>>其他</option>
</select>
</div>
<div class="form-group">
<label>生日</label>
<input type="date" name="birthday" value="<?php echo $user['birthday']; ?>">
</div>
<div class="form-group">
<label>个人网站</label>
<input type="url" name="website" value="<?php echo htmlspecialchars($user['website']); ?>">
</div>
<button type="submit">保存修改</button>
</form>
后端PHP处理
获取用户当前信息
// get_profile.php
session_start();
require_once 'db_connection.php';
$user_id = $_SESSION['user_id'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
return $user;
更新个人资料处理
// update_profile.php
<?php
session_start();
require_once 'db_connection.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$user_id = $_SESSION['user_id'];
$errors = [];
// 验证输入
$username = trim($_POST['username'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$bio = trim($_POST['bio'] ?? '');
$gender = $_POST['gender'] ?? '';
$birthday = $_POST['birthday'] ?? '';
$website = trim($_POST['website'] ?? '');
// 验证用户名
if (empty($username)) {
$errors[] = '用户名不能为空';
}
// 验证邮箱
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = '邮箱格式不正确';
}
// 验证手机号(中国手机号)
if (!empty($phone) && !preg_match('/^1[3-9]\d{9}$/', $phone)) {
$errors[] = '手机号格式不正确';
}
// 验证网站URL
if (!empty($website) && !filter_var($website, FILTER_VALIDATE_URL)) {
$errors[] = '网站URL格式不正确';
}
// 检查用户名是否已存在
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ? AND id != ?");
$stmt->execute([$username, $user_id]);
if ($stmt->fetch()) {
$errors[] = '用户名已被使用';
}
// 检查邮箱是否已存在
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ? AND id != ?");
$stmt->execute([$email, $user_id]);
if ($stmt->fetch()) {
$errors[] = '邮箱已被使用';
}
if (!empty($errors)) {
echo json_encode(['success' => false, 'errors' => $errors]);
exit;
}
try {
// 处理头像上传
$avatar_path = null;
if (isset($_FILES['avatar']) && $_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
$max_size = 2 * 1024 * 1024; // 2MB
$file_info = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($file_info, $_FILES['avatar']['tmp_name']);
finfo_close($file_info);
if (!in_array($mime_type, $allowed_types)) {
echo json_encode(['success' => false, 'errors' => ['只支持JPG、PNG、GIF格式的图片']]);
exit;
}
if ($_FILES['avatar']['size'] > $max_size) {
echo json_encode(['success' => false, 'errors' => ['图片大小不能超过2MB']]);
exit;
}
// 生成唯一文件名
$extension = pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION);
$filename = uniqid() . '_' . time() . '.' . $extension;
$upload_dir = 'uploads/avatars/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
$upload_path = $upload_dir . $filename;
if (move_uploaded_file($_FILES['avatar']['tmp_name'], $upload_path)) {
$avatar_path = $upload_path;
// 删除旧头像
$stmt = $pdo->prepare("SELECT avatar FROM users WHERE id = ?");
$stmt->execute([$user_id]);
$old_avatar = $stmt->fetchColumn();
if ($old_avatar && file_exists($old_avatar)) {
unlink($old_avatar);
}
}
}
// 构建更新SQL
$sql = "UPDATE users SET username = ?, email = ?, phone = ?, bio = ?,
gender = ?, birthday = ?, website = ?";
$params = [$username, $email, $phone, $bio, $gender, $birthday, $website];
if ($avatar_path) {
$sql .= ", avatar = ?";
$params[] = $avatar_path;
}
$sql .= ", updated_at = NOW() WHERE id = ?";
$params[] = $user_id;
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
echo json_encode([
'success' => true,
'message' => '个人资料更新成功',
'avatar' => $avatar_path ?? null
]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'errors' => ['更新失败,请稍后重试']]);
}
}
?>
JavaScript前端验证和AJAX提交
// profile.js
document.getElementById('profileForm').addEventListener('submit', function(e) {
e.preventDefault();
// 客户端验证
const username = document.querySelector('[name="username"]').value;
const email = document.querySelector('[name="email"]').value;
const phone = document.querySelector('[name="phone"]').value;
if (username.length < 2 || username.length > 20) {
showError('用户名长度应在2-20个字符之间');
return;
}
if (!isValidEmail(email)) {
showError('请输入有效的邮箱地址');
return;
}
if (phone && !isValidPhone(phone)) {
showError('请输入有效的手机号');
return;
}
// 显示加载状态
const submitBtn = document.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.textContent = '保存中...';
// 使用FormData处理文件上传
const formData = new FormData(this);
fetch('update_profile.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showSuccess('个人资料更新成功!');
if (data.avatar) {
document.getElementById('avatarPreview').src = data.avatar;
}
} else {
showErrors(data.errors);
}
})
.catch(error => {
showError('网络错误,请稍后重试');
})
.finally(() => {
submitBtn.disabled = false;
submitBtn.textContent = '保存修改';
});
});
// 头像预览
function previewAvatar(input) {
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('avatarPreview').src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
}
}
安全最佳实践
防止XSS攻击
// 输出时转义HTML
function escapeHtml($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
CSRF保护
// 生成CSRF Token
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// 在表单中添加
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
// 验证CSRF Token
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('CSRF token validation failed');
}
防止SQL注入
- 使用PDO预处理语句(已实现)
- 避免直接拼接SQL语句
优化建议
- 限流处理:限制用户频繁修改资料
- 操作日志:记录重要的资料修改操作
- 缓存更新:更新用户信息时清除相关缓存
- 异步处理:对头像等大文件进行异步处理
这是一个完整的个人资料修改功能实现,包含了前后端验证、文件处理、安全防护等关键功能,可以根据实际需求进行调整和扩展。