如何使用mysqli预准备语句绑定字符串数组?

2022-08-30 17:26:08

我需要将一个值数组绑定到子句。我该怎么做?WHERE IN(?)

这有效:

$mysqli = new mysqli("localhost", "root", "root", "db");
if(!$mysqli || $mysqli->connect_errno)
{
    return;
}
$query_str= "SELECT name FROM table WHERE city IN ('Nashville','Knoxville')";
$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str))
{       
    $query_prepared->execute();

但是,我无法与这样的bind_param一起工作:

$query_str= "SELECT name FROM table WHERE city IN (?)";
$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str))
{       
    $cities= explode(",", $_GET['cities']);
    $str_get_cities=  "'".implode("','", $get_cities)."'"; // This equals 'Nashville','Knoxville'

    $query_prepared->bind_param("s", $cities);
    $query_prepared->execute();

我做错了什么?

我也尝试过,但似乎无法获得正确的语法。call_user_func_array


答案 1

从 PHP 8.1 开始,您可以直接传递数组来执行:

$sql = "INSERT INTO users (email, password) VALUES (?,?)"; // sql
$stmt = $mysqli->prepare($sql); // prepare
$stmt->execute([$email, $password]); // execute with data! 

对于早期版本,该任务有点复杂,但可行。对于一个简单的情况,当您已经有一个带有占位符的查询时,代码将是

$sql = "INSERT INTO users (email, password) VALUES (?,?)"; // sql
$data = [$email, $password]; // put your data into array
$stmt = $mysqli->prepare($sql); // prepare
$stmt->bind_param(str_repeat('s', count($data)), ...$data); // bind array at once
$stmt->execute();

虽然,就像在你的例子中一样,我们有任意数量的占位符,但我们将不得不添加更多的代码。我将从我的文章Mysqli准备语句中获取解释,其中包含IN子句的多个值

  • 首先,我们需要创建一个字符串,其中包含数组中尽可能多的元素。为此,我们将使用非常方便的功能。?str_repeat()
  • 然后,必须将此带有逗号分隔问号的字符串添加到查询中。虽然它是一个变量,但在这种情况下它是安全的,因为它只包含常量值
  • 那么这个查询必须像任何其他查询一样准备
  • 那么我们需要创建一个字符串,其中包含要与bind_param()一起使用的类型。请注意,通常没有理由对绑定变量使用不同类型的变量 - mysql会很乐意将它们全部接受为字符串。有边缘情况,但极为罕见。对于日常使用,您可以随时保持简单,并使用“s”表示所有内容。 再次出手相救。str_repeat()
  • 然后我们需要将数组值绑定到语句。不幸的是,你不能只是把它写成一个变量,就像这样,只允许标量变量。幸运的是,有一个参数解包运算符可以完全满足我们的需求 - 将一个值数组发送到函数中,就好像它是一组不同的变量一样!$stmt->bind_param("s", $array)bind_param()
  • 其余的像往常一样 - 执行查询,获取结果并获取数据!

因此,正确的示例代码将是

$array = ['Nashville','Knoxville']; // our array
$in    = str_repeat('?,', count($array) - 1) . '?'; // placeholders
$sql   = "SELECT name FROM table WHERE city IN ($in)"; // sql
$stmt  = $mysqli->prepare($sql); // prepare
$types = str_repeat('s', count($array)); //types
$stmt->bind_param($types, ...$array); // bind array at once
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$data = $result->fetch_all(MYSQLI_ASSOC); // fetch the data   

尽管此代码相当大,但它比本主题中提供的任何其他合理的解决方案都要小得多。


答案 2

你不能用一个问号绑定两个变量!

对于绑定的每个变量,您都需要一个question mark

“bind_param”检查每个变量是否与要求匹配。之后,字符串值放在引号之间。

这将不起作用。

"SELECT name FROM table WHERE city IN (?)"; ( becomes too )
$q_prepared->bind_param("s", $cities);
"SELECT name FROM table WHERE city IN ('city1,city2,city3,city4')";

一定是。

"SELECT name FROM table WHERE city IN (?,?,?,?)"; ( becomes too )
$q_prepared->bind_param("ssss", $city1,$city2,$city3,$city4);
"SELECT name FROM table WHERE city IN ('city1','city2','city3','city4')";

$query_prepared->bind_param逐个引用字符串参数。
变量的数量和字符串类型的长度必须与语句中的参数匹配。

$query_str= "SELECT name FROM table WHERE city IN ('Nashville','Knoxville')";

将成为

$query_str= "SELECT name FROM table WHERE city IN (?,?)";

现在一定是bind_param

bind_param("ss",$arg1,$arg2)

有了这个

$query_str= "SELECT name FROM table WHERE city IN (?)";

bind_param

bind_param("s",$cities)

你得到

$query_str= "SELECT name FROM table WHERE city IN ('Nashville,Knoxville')";

这就是为什么数组不起作用的原因。
这个事实的唯一解决方案是call_user_func_array

如果您初始化语句,则不需要以下操作

$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str)) {

这是正确的

$query_prepared = $mysqli->stmt_init();
if($query_prepared->prepare($query_str)) {

如果您不想使用
并且只有少量参数,
则可以使用以下代码进行操作。call_user_func_array

[...]
$cities= explode(",", $_GET['cities']);
if (count($cities)>3) { echo "too many arguments"; }
else
{ 
$count = count($cities); 
$SetIn = "(";
  for($i = 0; $i < $count; ++$i) {    
      $code.='s';
      if ($i>0) {$SetIn.=",?";} else {$SetIn.="?";}
  }
$SetIn.=")";
$query_str= "SELECT name FROM table WHERE city IN ".$SetIn;
// with 2 arguments $query_str will look like
// SELECT name FROM table WHERE city IN (?,?)
$query_prepared = $mysqli->stmt_init();
if($query_prepared->prepare($query_str))
  {       
    if ($count==1) { $query_prepared->bind_param($code, $cities[0]);}
    if ($count==2) { $query_prepared->bind_param($code, $cities[0],$cities[1]);}
    if ($count==3) { $query_prepared->bind_param($code, $cities[0],$cities[1],$cities[2]);
    // with 2 arguments $query_prepared->bind_param() will look like
    // $query_prepared->bind_param("ss",$cities[0],$cities[1])      
  }    

    $query_prepared->execute();
  } 
 [...]
 }

我建议你尝试一下。call_user_func_array

寻找
mysqli-stmt.bind-param 的解决方案nick9v


推荐