此代码有效,但由于将 GET 参数连接到查询中,因此看起来不安全。我进行串联是因为我需要 WHERE 子句中参数的动态数量,这些参数可以是不同类型(IN
,正常比较条件)。
如何根据不同类型的 WHERE 条件的动态数字准备安全语句?
class myclass
{
public function index($where_clause = 1)
{
// db connection (using pdo)
$stm = $this->dbh->query("SELECT COUNT(amount) paid_qs FROM qanda $where_clause");
$ret = $stm->fetch(PDO::FETCH_ASSOC);
// do stuff
}
public function gen_where_clause()
{
$where_clause = '';
if (isset($_GET['c']) || isset($_GET['t']))
{
$where_clause = 'WHERE ';
if (isset($_GET['c']))
{
$where_clause .= 'cat = ' . $_GET['c'];
}
if (isset($_GET['t']))
{
if (isset($_GET['c']))
{
$where_clause .= $where_clause . ' AND '
}
$where_clause .= 'tag IN(' . $_GET['t'] . ')';
}
}
return $this->index($where_clause);
}
}
最佳答案
我将从三个方面解决这个问题:代码的实际正确性、解决方案和更好的做法。
代码
这段代码实际上不有效,因为mentioned有非常基本的语法错误,甚至根本无法实际运行。我假设这是一个简化错误,但即使连接也是错误的:语句每次都是重复的(.= 和字符串本身。其中任何一个都可以工作,都将破坏查询)
$where_clause .= $where_clause . ' AND '
参数的动态数量
具有动态数量参数的问题很有趣,并且根据需要可能相当复杂,但是在这种情况下,相当简单的参数连接将允许您实现动态数量的参数,如suggested by AbraCadaver .
更确切地说,当一个条件被添加到语句中时,分别将sql添加到语句中,并将值添加到一个数组中:
$sql .= 'cat = :cat';
$values[':cat'] = $_GET['c'];
然后您可以准备语句并使用正确的参数执行它。
$stmt = $pdo->prepare($sql);
$stmt->execute($values);
更好的做法
如前所述,此问题中提供的代码可能根本不起作用,因此让我强调一些可以显着增强此代码段的基本 OOP 原则。
- 依赖注入(inject)
数据库连接应该通过构造函数注入(inject),而不是每次执行查询时都重新创建(如果您在 index
方法中连接,它就会这样做)。请注意,$pdo
是一个私有(private)属性。它不应该是公共(public)的,不能被其他对象访问。如果这些对象需要数据库连接,也可以在它们的构造函数中注入(inject)相同的 pdo 实例。
class myclass
{
private $pdo;
public function __construct(PDO $pdo) { $this->pdo = $pdo; }
}
- 流程
其中一个方法应该是私有(private)的,由另一个(公共(public)方法)调用,后者将在参数中接收运行函数所需的一切。在这种情况下,似乎没有涉及任何参数,一切都来自 $_GET
。
我们可以调整索引,使其接受 sql 和查询值,但这三行可以很容易地转移到其他方法。
private function index($sql, $values)
{
$stmt = $this->pdo->prepare($sql);
$stmt->execute($values);
return $stmt->fetchAll();
}
然后是可以安全使用的公共(public) gen_where_clause(我相信它的命名是错误的......它实际上生成的是值,而不是子句),它将生成动态数量的参数,保护你免受 sql 注入(inject)。
public function gen_where_clause()
{
$sql = "SELECT COUNT(amount) AS paid_qs FROM qanda ";
$values = [];
if (isset($_GET['c']) || isset($_GET['t']))
{
$sql .= ' WHERE ';
if (isset($_GET['c']))
{
$sql .= ' cat = :cat ';
$values[':cat'] = $_GET['c'];
}
// etc.
}
return $this->index($sql, $values);
}
- 过滤输入
对于 sql 注入(inject)保护,即使用参数化查询时,不需要转义值。但是,清理您的输入始终是一个正确的想法。在函数外部对其进行清理,然后将其作为参数传递给“搜索”函数,将函数与超全局 $_GET
解耦。定义过滤参数超出了本文的范围,请参阅 documentation .
// global code
// create $pdo normally
$instance = new myclass($pdo);
$inputs = filter_input_array(INPUT_GET, $args);
$results = $instance->gen_search_clause($inputs);
关于php - 如何准备具有动态数量参数的语句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44527918/