
本文介绍如何从数据库查询用户经验值(userexp),依据预设等级规则计算并更新用户等级(userlvl),同时安全地写入 session,避免硬编码逻辑错误和 sql 注入风险。
本文介绍如何从数据库查询用户经验值(userexp),依据预设等级规则计算并更新用户等级(userlvl),同时安全地写入 session,避免硬编码逻辑错误和 sql 注入风险。
在 PHP Web 应用中,常见需求是根据数据库中存储的用户经验值(如 userexp 字段)实时计算并设置其等级(userlvl),再将结果存入 $_SESSION 供前端显示。但原代码存在多个关键问题:未实际执行数据库查询、等级判断逻辑在 $userexp = 0 之后才运行(导致恒为等级 1)、SQL 查询未绑定用户标识、且使用了不安全的字符串拼接方式。
以下是专业、健壮、可维护的实现方案:
✅ 正确流程:查询 → 计算 → 更新 → 存储
<?php session_start();
// 1. 数据库连接(请替换为你的实际配置)
$host = 'localhost';
$dbname = 'your_db';
$username = 'your_user';
$password = 'your_pass';
$conn = new mysqli($host, $username, $password, $dbname);
if ($conn->connect_error) {
die("数据库连接失败: " . $conn->connect_error);
}
$conn->set_charset('utf8mb4');
// 2. 安全获取当前用户 ID(必须已登录并存于 session)
if (!isset($_SESSION['userid'])) {
die("用户未登录");
}
$user_id = (int)$_SESSION['userid']; // 强制整型,防御注入
// 3. 查询用户最新 userexp(关键!原代码缺失此步)
$stmt = $conn->prepare("SELECT userexp FROM users WHERE id = ?");
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
die("用户不存在");
}
$user_data = $result->fetch_assoc();
$userexp = (int)$user_data['userexp'];
// 4. 根据经验值映射等级(推荐:使用有序数组 + 二分查找或逆序遍历)
$level_thresholds = [
[27586350412, 50],
[22069080330, 50], // 注意:此处原逻辑有重复(275... 和 220... 均对应 lvl 50),建议去重合并
[20434333639, 49],
[18920679295, 48],
[17519147496, 47],
[16221432867, 46],
[10814288578, 45],
// ...(其余阈值按降序排列,省略以保持简洁)
[5, 1]
];
$userlvl = 1; // 默认等级
foreach ($level_thresholds as $pair) {
if ($userexp >= $pair[0]) {
$userlvl = $pair[1];
break;
}
}
// 5. 同步更新数据库中的 userlevel 字段(可选,但推荐保持数据一致性)
$stmt = $conn->prepare("UPDATE users SET userlevel = ? WHERE id = ?");
$stmt->bind_param("ii", $userlvl, $user_id);
$stmt->execute();
// 6. 写入 Session(确保前端 echo 正确读取)
$_SESSION['userlvl'] = $userlvl;
$_SESSION['userexp'] = $userexp;
// 关闭连接
$stmt->close();
$conn->close();
?>
? 前端调用(安全显示)
<td> <b>LVL:</b> <?php echo htmlspecialchars($_SESSION['userlvl']); ?> </td>
✅ 使用 htmlspecialchars() 防止 XSS;✅ 等级值来自真实数据库查询,非静态假数据。
⚠️ 关键注意事项
- 绝不跳过数据库查询:原代码中 $userexp = 0 直接覆盖了数据库值,必须先 SELECT 再判断;
- 避免冗余条件:如 27586350412 和 220690803330 均设为等级 50,应合并为单条规则,提升可读性;
- 防御 SQL 注入:始终使用 mysqli_prepare() + bind_param(),禁用字符串拼接 SQL;
- 类型安全:对用户 ID、经验值等数值字段强制 (int) 转换;
- Session 前置检查:确保 $_SESSION['userid'] 已存在且合法,防止未授权访问;
- 性能优化:若等级规则固定,可将 $level_thresholds 提取为常量或缓存,避免重复定义。
✅ 推荐进阶实践
- 将等级计算逻辑封装为函数 calculateUserLevel(int $exp): int,便于单元测试;
- 使用 PDO 替代 MySQLi,支持更灵活的错误处理与事务;
- 对高频访问场景,考虑将 userlevel 缓存至 Redis,减少数据库压力。
通过以上重构,你的等级系统将真正“活”起来——每次页面加载都基于最新数据库值动态计算,安全、清晰、可持续维护。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











