将 PHP 变量安全地拼接到 SQL 查询语句中。

下面我将分步骤、从简单到复杂地进行说明,并重点强调安全防范。
第一步:获取参数
在 DedeCMS 中,传递参数通常有两种方式:
- URL GET 方式:通过 URL 的查询字符串传递,
https://www.example.com/plus/list.php?tid=1&pagesize=10。 - 表单 POST 方式:通过表单提交传递,例如搜索功能。
在 PHP 中,我们可以使用 $_GET 和 $_POST 超全局变量来接收这些参数。
示例:
假设 URL 是 https://www.example.com/yourpage.php?typeid=5&order=hot
在 yourpage.php 文件中,我们可以这样获取参数:

// 获取 typeid 参数,如果不存在则默认为 0 $typeid = isset($_GET['typeid']) ? (int)$_GET['typeid'] : 0; // 获取排序参数,如果不存在则默认为 'new' $order = isset($_GET['order']) ? trim($_GET['order']) : 'new'; // 打印一下看看结果 echo "typeid: " . $typeid . "<br>"; echo "order: " . $order;
重要提示:对获取的参数进行初步的类型转换和过滤(如 (int) 和 trim())是一个好习惯,可以避免很多不必要的麻烦。
第二步:编写 SQL 查询语句
获取到参数后,我们就可以在 SQL 语句中使用它们,最直接的方式是使用字符串拼接。
示例:
我们要查询 dede_archives 表中 typeid 为我们获取到的 $typeid 值,并且按 $order 指定的字段排序的文章。
// 1. 获取参数
$typeid = isset($_GET['typeid']) ? (int)$_GET['typeid'] : 0;
$order = isset($_GET['order']) ? trim($_GET['order']) : 'new';
// 2. 拼接 SQL 语句
$sql = "SELECT * FROM dede_archives WHERE typeid = '$typeid' ORDER BY ";
if ($order == 'hot') {
$sql .= "click DESC"; // 按点击量降序
} else {
$sql .= "id DESC"; // 按ID降序 (默认)
}
// 打印 SQL 语句进行检查
echo "执行的 SQL 语句是: " . $sql;
这段代码的潜在问题:
上面的拼接方式存在巨大的SQL 注入风险。$typeid 是一个恶意的字符串,1 OR 1=1,SQL 语句就变成了 ... WHERE typeid = 1 OR 1=1,这会导致查询出所有数据,甚至更严重的数据泄露或破坏。

第三步:安全执行 SQL 查询 (核心)
为了安全地执行 SQL,DedeCMS 提供了专门的数据库操作类 DedeSql(或 dedesql),我们应该使用它提供的方法,而不是直接执行 mysql_query。
使用 SetQuery + ExecuteQuery (推荐)
这是最安全、最推荐的方式。SetQuery 方法会自动处理参数,防止 SQL 注入。
语法:
$dsql->SetQuery("SQL语句 [WHERE ...]");
$dsql->Execute('别名');
关键点:
- 在 SQL 语句中,使用 作为占位符。
- 将 PHP 变量作为第二个参数传递给
ExecuteQuery方法,它会自动替换 。
安全改造后的代码:
// 1. 获取并过滤参数
$typeid = isset($_GET['typeid']) ? (int)$_GET['typeid'] : 0;
$order = isset($_GET['order']) ? trim($_GET['order']) : 'new';
// 2. 实例化 DedeSql 数据库操作类
$dsql = new DedeSql();
// 3. 编写带占位符的 SQL 语句
$sql = "SELECT * FROM dede_archives WHERE typeid = ##typeid## ORDER BY ";
if ($order == 'hot') {
$sql .= "click DESC";
} else {
$sql .= "id DESC";
}
// 4. 设置查询并执行
// 将 $typeid 变量作为数组传递给 ExecuteQuery
$dsql->SetQuery($sql);
$dsql->ExecuteQuery('list', $typeid); // 'list' 是查询结果的别名,可以自定义
// 5. 遍历查询结果
while ($row = $dsql->GetArray('list')) {
echo "<h3>" . $row['title'] . "</h3>";
echo "<p>" . $row['description'] . "</p>";
echo "<hr>";
}
// 6. 释放资源
$dsql->Close();
工作原理:
ExecuteQuery 方法内部会检测到 ##typeid## 占位符,然后将它替换为传递进来的 $typeid 变量的值,并且会进行必要的转义处理,从而杜绝了 SQL 注入。
使用 Select 快捷方法 (更简洁)
对于简单的 SELECT 查询,DedeSql 类还提供了一个非常方便的 Select 方法。
语法:
$dsql->Select("表名", "字段", "WHERE条件", "排序限制");
示例:
// 1. 获取并过滤参数
$typeid = isset($_GET['typeid']) ? (int)$_GET['typeid'] : 0;
$order = isset($_GET['order']) ? trim($_GET['order']) : 'new';
// 2. 实例化 DedeSql
$dsql = new DedeSql();
// 3. 使用 Select 方法
// 注意:这里的条件部分同样使用 ## 作为占位符
$dsql->Select("dede_archives", "*", "typeid=##typeid##", "id DESC", $typeid);
// 4. 遍历结果
while ($row = $dsql->GetArray()) {
echo "<h3>" . $row['title'] . "</h3>";
echo "<p>" . $row['description'] . "</p>";
echo "<hr>";
}
// 5. 释放资源
$dsql->Close();
这种方式更紧凑,但功能上不如 SetQuery + ExecuteQuery 灵活。
第四步:综合实例 - 自定义列表页
这是一个完整的、可以在 DedeCMS 模板文件中使用的例子,假设我们要做一个“热门文章排行榜”页面,可以通过 URL 指定分类 ID。
文件路径:/templets/default/custom_hotlist.htm
<!DOCTYPE html>
<html>
<head>热门文章列表</title>
</head>
<body>
<h1>热门文章排行榜</h1>
<?php
// 1. 包含并初始化 DedeSql
// (通常在 DedeCMS 的 PHP 文件中,$dsql 全局变量已经存在,但在独立页面需要自己创建)
if (!defined('DEDEINC')) exit('dedecms error!');
require_once(DEDEINC.'/dedesql.class.php');
$dsql = new DedeSql();
// 2. 获取 URL 参数
$typeid = isset($_GET['tid']) ? (int)$_GET['tid'] : 0;
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10; // 可选:限制条数
// 3. 构建 SQL 语句并执行
if ($typeid > 0) {
// 如果指定了分类ID,则查询该分类下的热门文章
$sql = "SELECT id, title, click, litpic FROM dede_archives WHERE typeid = ##typeid## AND arcrank > -1 ORDER BY click DESC LIMIT 0, ##limit##";
$dsql->SetQuery($sql);
// 传递多个参数
$dsql->ExecuteQuery('hotlist', array('typeid' => $typeid, 'limit' => $limit));
} else {
// 如果没有指定分类ID,则查询全站热门文章
$sql = "SELECT id, title, click, litpic FROM dede_archives WHERE arcrank > -1 ORDER BY click DESC LIMIT 0, ##limit##";
$dsql->SetQuery($sql);
$dsql->ExecuteQuery('hotlist', array('limit' => $limit));
}
// 4. 在模板中循环输出
while ($row = $dsql->GetArray('hotlist')) {
$arcUrl = GetOneArchive($row['id']); // 使用系统函数获取文章链接
echo "<div class='hot-item'>";
echo "<a href='" . $arcUrl['arcurl'] . "'>" . $row['title'] . "</a>";
echo "<span>点击: " . $row['click'] . "</span>";
echo "</div>";
}
// 5. 释放资源
$dsql->Close();
?>
</body>
</html>
如何访问:
访问 https://www.yoursite.com/templets/default/custom_hotlist.htm?tid=5&limit=20 即可查看 ID 为 5 的分类下的前 20 篇热门文章。
总结与最佳实践
- 永远不要相信外部输入:对所有通过
$_GET,$_POST等获取的参数进行过滤和验证。 - 优先使用 DedeSql 的占位符:始终使用
##变量名##作为占位符,并通过ExecuteQuery的第二个参数传递变量,这是防止 SQL 注入的黄金法则。 - 限制查询范围:在
WHERE条件中,尽量加上arcrank > -1(表示审核通过)等条件,避免调用到未发布或被删除的内容。 - 使用系统函数:获取文章链接、栏目链接等,尽量使用 DedeCMS 自带的函数(如
GetOneArchive,GetTypeLink),而不是自己硬编码 URL。 - 及时释放资源:查询结束后,调用
$dsql->Close()关闭数据库连接,释放内存。
遵循以上原则,你就可以在 DedeCMS 中安全、灵活地使用 SQL 语句并传递参数了。
