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

How can I bind an array of strings with a mysqli prepared statement?

提问人:Mark 提问时间:6/21/2013 最后编辑:Peter MortensenMark 更新时间:3/16/2023 访问量:54855

问:

我需要将值数组绑定到子句。我该怎么做?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

php mysqli bindparam

评论

0赞 moskito-x 7/10/2013
$str_get_cities= "'".implode("','", $get_cities)."'";.不要使用引号!!这是用“s”选项完成的!bind_param
0赞 mbuster 1/23/2017
正如其他人建议的那样,使用call_user_func_array函数将所需的参数绑定到参数化查询。只是为了强调它接受通过引用传递的参数。我只能找到如何执行参数化查询的代码片段,其中包含要绑定的动态参数数量,因此我最终执行了自己的函数(请参阅帖子)。它接受任何参数化的 SELECT、UPDATE、INSERT 和 DELETE 查询,并且在我的 PHP 代码中动态执行任何 MySQL 数据库交互对我有很大帮助。
0赞 mickmackusa 4/3/2022
更早的、清晰、完整的问题问完全相同的事情:在 mysqli 预准备语句中使用数组:WHERE ..在(..)查询

答:

12赞 moskito-x 6/21/2013 #1

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

对于绑定的每个变量,都需要一个问号

“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 two 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 two arguments, $query_prepared->bind_param() will look like
        // $query_prepared->bind_param("ss", $cities[0], $cities[1])
        }
        $query_prepared->execute();
    }
    [...]
}

我建议你尝试一下。call_user_func_array

寻找 的解决方案。nick9v

mysqli_stmt::bind_param

评论

18赞 ggDeGreat 10/24/2015
你建议使用,但你从来没有在这里展示它如何做。=_='call_user_func_array
0赞 Mochi 4/11/2020
这是call_user_func_array的一个用例,整个问题解释 pontikis.net/blog/dynamically-bind_param-array-mysqli
0赞 I try so hard but I cry harder 7/4/2020
“将mysqli_stmt_bind_param()与call_user_func_array()结合使用时必须小心。请注意,mysqli_stmt_bind_param() 要求通过引用传递参数,而 call_user_func_array() 可以接受可以表示引用或值的变量列表作为参数。源码 -> php.net/manual/en/mysqli-stmt.bind-param.php
1赞 mickadoo 2/2/2014 #2

我也遇到了麻烦,在发现大多数人都在使用 call_user_func_array 之前,我就让它工作了:eval

$fields = array('model', 'title', 'price'); // Fields in WHERE clause
$values = array( // Type and value for each field
    array('s', 'ABCD-1001'),
    array('s', '[CD] Test Title'),
    array('d', '16.00')
);
$sql = "SELECT * FROM products_info WHERE "; // Start of query
foreach ($fields as $current) { // Build where clause from fields
    $sql .= '`' . $current . '` = ? AND ';
}
$sql = rtrim($sql, 'AND '); // Remove last AND
$stmt = $db->prepare($sql);
$types = ''; $vals = '';
foreach ($values as $index => $current_val) { // Build type string and parameters
    $types .= $current_val[0];
    $vals .= '$values[' . $index . '][1],';
}
$vals = rtrim($vals, ','); // Remove last comma
$sql_stmt = '$stmt->bind_param("' . $types . '",' . $vals . ');'; // Put bind_param line together
eval($sql_stmt); // Execute bind_param
$stmt->execute();
$stmt->bind_result($col1, $col2, $col3, $col4, $col5, $col6); // This could probably also be done dynamically in the same way
while ($stmt->fetch()) {
    printf("%s %s %s %s %s %s\n", $col1, $col2, $col3, $col4, $col5, $col6);
}

评论

0赞 Ethan Moore 7/20/2020
肯定需要一些玩弄,但我实际上真的很喜欢这个答案。它可能有点笨重,但老实说,它更容易阅读和弄清楚发生了什么。call_user_func_array
5赞 SingSong 5/9/2016 #3

像这样使用call_user_func_array

$stmt = $mysqli->prepare("INSERT INTO t_file_result VALUES(?,?,?,?)");

$id = '1111';
$type = 2;
$result = 1;
$path = '/root';

$param = array('siis', &$id, &$type, &$result, &$path);
call_user_func_array(array($stmt, 'bind_param'), $param);

$stmt->execute();

printf("%d row inserted. \n", $stmt->effected_rows);
$stmt->close;

评论

1赞 gonatee 5/28/2017
如果您在文档中添加更多解释和来源,那么这肯定会被接受!可爱的答案在这里!
4赞 Alexis Wilke 7/22/2017
这是行不通的,所有参数都必须是引用。因此,您需要将格式定义为 in ,然后在您使用的数组中定义。所有参数都必须是引用。$format = "siis";&$format
1赞 Peter Mortensen 2/2/2023
@Alexis Wilke:这可能是许多纯代码虚假答案的一个例子。
-1赞 WoodrowShigeru 6/26/2019 #4

我的做法是:准备带有所有单独问号以及类型字符串的查询。

$cities = array('Nashville', 'Knoxville');
$dibs = '';
$query = "SELECT name FROM table WHERE city IN (";
$marks = array();

foreach ($cities as $k => $city) {
    // i, s, b, d type based on the variables to bind.
    $dibs .= 's';
    array_push($marks, '?');
}

$query .= implode(',', $marks) . ')';

连接。

$mysql = new mysqli($host, $user, $pass, $dbname);
$statement =
    $mysql->prepare($query)
OR    die(sprintf(
        'Query error (%s) %s', $mysql->errno, $mysql->error
    ))
;

然后你使用“...”token / 省略号(文档)来绑定数组。

if ($statement) {
    $statement->bind_param($dibs, ...$cities);
    $statement->execute();

    $statement->close();
}
$mysql->close();

我知道它有点违背了为了逃避而绑定的目的(但至少它适用于整数列表,即 ID)。

67赞 Your Common Sense 10/12/2019 #5

在我的文章中,Mysqli 准备了 IN 子句的多个值的语句

PHP 8.2 方式。execute_query()

从 PHP 8.2 开始,您可以使用一个方便的函数 execute_query()

// INSERT example
$sql = "INSERT INTO users (email, password) VALUES (?,?)"; // sql
$mysqli->execute_query($sql,[$email, $password]); // in one go

如果您的数组具有可变长度,则需要动态创建占位符列表

// WHERE IN example
$array = ['Nashville','Knoxville']; // our array
$parameters = str_repeat('?,', count($array) - 1) . '?'; // placeholders
$sql = "SELECT name FROM table WHERE city IN ($parameters)"; // sql
$result = $mysqli->execute_query($sql, $array); // in one go
$data = $result->fetch_all(MYSQLI_ASSOC); // fetch the data   

PHP 8.1 方式。数组转换为 execute()

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

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

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

旧版本,准备/绑定/执行方式

对于早期版本,任务要复杂一些。

// INSERT example
$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 
$stmt->execute();

虽然,就像您的情况一样,我们有任意数量的占位符,但我们将不得不添加更多的代码。

  • 首先,我们需要创建一个字符串,其中包含与数组中元素数量一样多的标记。为此,我们将使用非常方便的功能。?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赞 AaA 4/17/2020
我不明白你回答的第一段。我不认为一个问题应该提供答案,他正在寻找你和其他人回答的答案。我寻找的不是我需要添加多少个 quesiton 标记,我已经拥有了。我找不到对bind_param的适当解释,因为似乎 php.net 文档并不关心它。我想知道我是否可以将数组作为参数传递给它。
1赞 Your Common Sense 4/18/2020
@AaA我的回答中还有另一段,其中一段提到了参数解包运算符。它回答了你的问题吗?
0赞 Marco Muciño 7/24/2020
谢谢,“参数解包运算符”是我的答案。这个答案解决了许多其他类似的问题,解决方案非常详细。
1赞 djack109 1/1/2021
这是行不通的。每次都给我$stmt->bind_param($paramstring, $params );Array to string conversion
4赞 miken32 9/25/2021 #6

从 PHP 版本 8.1 开始,不再需要绑定。与 PDO 5.0 版一样,您现在可以将参数作为数组直接传递给 execute 方法

$mysqli       = new mysqli("localhost", "root", "root", "db");
$params       = ['Nashville','Knoxville'];
$placeholders = str_repeat('?,', count($params) - 1) . '?'
$query        = "SELECT name FROM table WHERE city IN ($placeholders)";
$stmt         = $mysqli->prepare($query);

$stmt->execute($params);

另一个示例,如果您有一个关联数组,其键与列名匹配:

$mysqli       = new mysqli("localhost", "root", "root", "db");
$data         = ["bar" => 23, "baz" => "some data"];
$params       = array_values($data);
$placeholders = str_repeat('?,', count($params) - 1) . '?'
$columns      = implode("`,`", array_keys($data));
$query        = "INSERT INTO foo (`$columns`) VALUES ($placeholders)";
$stmt         = $mysqli->prepare($query);

$stmt->execute($params);

另外值得一提的是,该库现在默认在发生错误时抛出异常。在版本 8.1 之前,情况并非如此。

0赞 Puneet Sharma 2/14/2023 #7

PHP 8.2 用 execute_query 解决了所有问题

$this->execute_query($query, $parms);

喜欢

$query = "SELECT * FROM users WHERE `email` IN (?) LIMIT 1";
$parms = ["[email protected]"];
$this->execute_query($query, $parms);

记得?是占位符和数字 ?(占位符) 与 count($parms) 是相同的数字,可以通过多种方式完成。

所以在你的情况下,它应该是

$query = "SELECT name FROM table WHERE city IN (? , ?)";
$parms = ['Nashville','Knoxville'];
$this->execute_query($query, $parms);

https://www.php.net/manual/en/mysqli.execute-query

如果要先创建动态$query,那么

$query = "SELECT name FROM table WHERE city IN (".implode(",",array_map(fn()=>"?",$parms)).")";

评论

0赞 Your Common Sense 2/14/2023
如果 $parms = [“[email protected]”,“[email protected]”];怎么办?
0赞 Puneet Sharma 2/14/2023
那么在查询中,我们应该有两个吗?(占位符)在$query中,因为它将数组值放入查询中
0赞 Your Common Sense 2/14/2023
如果你事先不知道这个数组中有多少个元素怎么办?它是在你写下答案的问题中被问到的
0赞 Puneet Sharma 2/14/2023
新的更新现在应该没有问题