从“能跑”到“跑对”:一个 Xiuno BBS 帖子查询函数的错误写法与正确改造
很多刚接触 Xiuno BBS 二次开发的朋友,写查询时习惯直接拼 SQL,觉得“能查出数据就行”。但论坛这种读多写少的场景,一个不规范的查询函数可能让帖子列表页在几百条数据时就开始变慢,甚至在高并发下拖垮数据库。下面用一个真实改造案例,对比一段错误代码和它的正确写法。
场景:根据版块 ID 获取最新 N 条帖子
假设我们要写一个函数,接收版块 ID 和数量,返回帖子列表。先看一段常见的错误写法:
function get_threads_by_forum($fid, $limit) {
global $db;
$sql = "SELECT * FROM xn_thread WHERE fid = $fid ORDER BY dateline DESC LIMIT $limit";
$arr = $db->sql_fetchall($sql);
return $arr;
}
这段代码能跑,但问题不少。第一,$fid 和 $limit 直接拼进 SQL,如果调用方传了非整数,就是注入漏洞。第二,SELECT * 把帖子正文、附件信息等大字段全查出来,列表页只需要标题、作者、时间、回复数,白白浪费 IO。第三,没有考虑帖子状态,草稿、待审核、已删除的帖子也会被查出来。第四,LIMIT 没有做上限保护,调用方传个 10000,数据库直接压力拉满。
正确写法应该分四步改造:
第一步,参数强制转换。用 intval() 处理 $fid 和 $limit,并给 $limit 设一个合理上限,比如 100。第二步,只查需要的字段,用明确列名替代 *。第三步,加上状态条件,只取已审核通过的帖子。第四步,用 Xiuno 封装好的 db_find() 或 db_sql_find(),让框架处理转义和条件拼接。
function get_threads_by_forum($fid, $limit = 20) {
$fid = intval($fid);
$limit = min(intval($limit), 100);
if ($fid <= 0 || $limit <= 0) {
return array();
}
$cond = array('fid' => $fid, 'audit' => 1);
$fields = array('tid', 'fid', 'uid', 'subject', 'dateline', 'posts', 'views', 'lastpid');
$orderby = array('dateline' => -1);
return db_find('thread', $cond, $orderby, $limit, $fields);
}
改造后,函数的安全性、性能和可维护性都上了一个台阶。参数被约束,字段可控,状态过滤到位,而且 db_find() 内部会处理表前缀和条件转义,后续如果加缓存或分页也更容易扩展。
一个小提醒:如果你的 Xiuno 版本里 db_find() 的排序参数写法不同,可以直接用 db_sql_find() 配合手写 ORDER BY,但字段和条件仍然要按上面的思路来。别为了省事回到拼接老路,列表页的查询会被频繁调用,规范一点,后面排查问题时能少掉很多头发。