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

hec6srdp  于 2023-02-03  发布在  PHP
关注(0)|答案(6)|浏览(429)

我需要绑定一个数组的值到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,但似乎无法获得正确的语法。

pes8fvy9

pes8fvy91#

从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 prepared statement with multiple values for IN clause中得到解释:

  • 首先,我们需要创建一个字符串,其?标记数与数组中的元素数相同。为此,我们将使用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

尽管这段代码相当大,但它比本主题迄今为止提供的任何其他可行的解决方案都要小得多。

rggaifut

rggaifut2#

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

对于绑定的每个变量,都需要一个 * 问号 *。
"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的解。

  • 一个月一次 *
pb3skfrl

pb3skfrl3#

从PHP 8.1版开始,binding is no longer required .与从5.0版开始的PDO一样,现在可以将参数作为数组直接传递给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);

另外值得一提的是now defaults库在出错时抛出异常,在8.1版本之前不是这样的。

vptzau2j

vptzau2j4#

像这样使用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;
xqkwcwgp

xqkwcwgp5#

我在这方面也遇到了麻烦,在发现大多数人都在使用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);
}
voase2hg

voase2hg6#

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

$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
    ))
;

然后使用"..."标记/省略号(文档)来绑定数组。

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

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

我知道这有点违背了绑定的目的,因为它是为了逃逸(但至少它可以很好地处理整数列表,即ID)。

相关问题