如何使用mysqli API制作一个完全动态的预备语句?

问题描述 投票:1回答:1

我需要把这个查询改成使用预备语句。这可能吗?

这个查询。

$sql = "SELECT id, title, content, priority, date, delivery FROM tasks " . $op . " " . $title . " " . $content . " " . $priority . " " . $date . " " . $delivery . " ORDER BY " . $orderField . " " . $order . " " . $pagination . "";

在查询之前,有代码检查POST变量,并在查询中改变变量的内容。

//For $op makes an INNER JOIN with or without IN clause depending on the content of a $_POST variable
$op = "INNER JOIN ... WHERE opID  IN ('"$.opID."')";
//Or
$op = "INNER JOIN ... ";

//For $title (depends of $op):
$title = "WHERE title LIKE'%".$_POST["title"]."%'";
//Or
$title = "AND title LIKE'%".$_POST["title"]."%'";

//For $content:
$content = "AND content LIKE '%".$_POST["content"]."%'";

//For $priority just a switch:
$priority = "AND priority = DEPENDING_CASE";

//For $date and $delivery another switch 
$d = date("Y-m-d", strtotime($_POST["date"]));
$date = "AND date >= '$d' 00:00:00 AND date <= '$d' 23:59:59";
//Or $date = "AND date >= '$d' 00:00:00";
//Or $date = "AND date <= '$d' 23:59:59";

//For $orderField
$orderField = $_POST["column"];

//For $order
$order= $_POST["order"];

//For $pagination 
$pagination = "LIMIT ".$offset.",". $recordsPerPage;

我怎么能用准备好的语句来做这个查询呢?

  • 这个查询可以更静态化,但这意味着要做不同的准备语句,并根据$_POST的检查来执行。
  • 它取决于许多变量,因为这个查询在一个包含搜索字段和列的表中显示结果。

一个完整的查询例子是这样的(取决于$_POST检查)。

SELECT id, title, content, priority, date, delivery FROM tasks INNER JOIN op ON task.op = op.opId WHERE op IN (4851,8965,78562) AND title LIKE '%PHT%' AND content LIKE '%%' AND priority = '2' ORDER BY date DESC LIMIT 0, 10 
php mysqli prepared-statement
1个回答
2
投票

一个很好的问题。并感谢你转到准备好的声明。看来,经过这么多年的奋斗,这个想法终于开始被人接受了。

声明:将有链接到我自己的网站,因为我正在帮助人们与PHP 20多年,并得到了一个痴迷于写文章最常见的问题。

是的,这是完全可能的。看看我的文章。如何为mysqli创建一个搜索过滤器 为全功能的例子。

对于 WHERE 部分,你所需要的是创建两个独立的数组--一个包含有占位符的查询条件,一个包含这些占位符的实际值,也就是

WHERE 即:

$conditions = [];
$parameters = [];

if (!empty($_POST["content"])) {
    $conditions[] = 'content LIKE ?';
    $parameters[] = '%'.$_POST['content ']."%";
}

以此类推,针对所有的搜索条件。

然后你可以 implode 所有条件都用 AND 弦为胶,得到一流的。WHERE 子句。

if ($conditions)
{
    $where .= " WHERE ".implode(" AND ", $conditions);
}

这个程序对所有的搜索条件都是一样的,但对 IN() 条款。

IN() 条款

是有点不同的,因为你需要更多的占位符和更多的值来添加。

if (!empty($_POST["opID"])) {
    $in  = str_repeat('?,', count($array) - 1) . '?';
    $conditions[] = "opID IN ($in)";
    $parameters = array_merge($parameters, $_POST["opID"]);
}

这段代码将添加尽可能多的 ? 占位符至 IN() 子句中的元素一样多。$_POST["opID"] 并将所有这些值添加到 $parameters 数组。解释可以在我的网站同一栏目的相邻文章中找到。

当你完成了 WHERE 子句,你可以移动到你的查询的其余部分。

ORDER BY 条款

你不能用子句来参数化顺序,因为字段名和SQL关键字不能用占位符来表示。为了解决这个问题,我恳请你使用一个叫做 白名单功能 我就是为了这个目的而写的。有了它,你可以让你的ORDER BY子句100%安全但又非常灵活。你所需要的只是预先定义一个数组,其中的字段名允许在子句中使用。

$sortColumns = ["title","content","priority"]; // add your own

然后使用这个方便的函数获得安全值。

$orderField = white_list($_POST["column"], $sortColumns, "Invalid column name");
$order = white_list($_POST["order"], ["ASC","DESC"], "Invalid ORDER BY direction");

这是个聪明的函数,它涵盖了三种不同的情况

  • 如果没有提供值(即$_POST["column"]为空),将使用白名单中的第一个值,因此它作为默认值。
  • 如果提供了一个正确的值,它将被用于查询。
  • 如果提供了一个错误的值,那么将抛出一个错误。

LIMIT 条款

LIMIT 值是完美的参数化,所以你可以直接将它们添加到 $parameters 数组。

$limit = "LIMIT ?, ?";
$parameters[] = $offset;
$parameters[] = $recordsPerPage;

最后的组装

最后,你的查询将是这样的内容。

$sql = "SELECT id, title, content, priority, date, delivery 
        FROM tasks INNER JOIN ... $where ORDER BY `$orderField` $order $limit"; 

它可以使用以下代码来执行

$stmt = $mysqli->prepare($sql);
$stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
$stmt->execute();
$data = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);

哪儿 $data 是一个传统的数组,包含了查询返回的所有记录。

© www.soinside.com 2019 - 2024. All rights reserved.