php - PDO 绑定(bind)问题

标签 php mysql sql

对于给定的代码,我收到以下 SQL 错误。

You have an error in your SQL syntax; check the manual that coresponds to your MySQL server version for the right syntax to use near '' at line 1

代码:

$set_query = "";

foreach ($passed_columns as $c)
{
    $set_query .= $c . " = " . ':' . $c . ',';
}

$p = strlen($set_query);
$set_query[$p-1] = "";

$SQL = 'UPDATE users SET ' . $set_query . ' WHERE user_id IN (' . implode(",", $_POST['user_id']) . ')';

$stmt = $dbh->prepare($SQL);

foreach($_POST['cols'] as $key => $val)
{
    $stmt->bindValue(':' . $key, $val);
}

if (!$stmt->execute()) {
    die(print_r($stmt->errorInfo()));
}

$_POST['cols'] 包含(column_name => 新列值)的键值数组。 $passed_columns 仅包含与 $_POST['cols'] 中的键匹配的列名数组

我认为这个问题与值的绑定(bind)方式有关。如果我回显 $SQL 变量,则输出是有效的 SQL(具有我正在测试的值)。

但奇怪的是,如果我手动将 $SQL 设置为它刚刚输出的有效 SQL(“UPDATE users SET role = :role WHERE user_id IN (100)”),脚本就可以运行。

最佳答案

反馈:

  • 使用 “字符串”。 $var 很快就变得不可读了。 PHP 可以直接在字符串中嵌入变量:"string $var" 如果你需要做数组表达式,你可以使用大括号,比如 "string {$arr['key']}".

  • 我建议用反引号分隔列名。

  • 从设置列表中删除最后一个逗号是笨拙的。最好将集合列表构建为数组并使用逗号内爆。

  • 您的 IN 列表容易受到 SQL 注入(inject)攻击。使用 (int) 映射 user_id 值以删除可能的恶意内容。如果 user_id 值不是整数,则使用查询参数(但不要在单个语句中混合使用 ? 位置参数和命名参数——这会混淆 PDO)。

  • bind_param() 是不必要的。只需将参数值传递给 execute()。在现代版本的 PHP 中,键值中的前导冒号是不必要的,这使得直接从键/值数组传递参数变得更加简单。

  • 从您的示例中不清楚 $passed_columns 是从用户输入中获取的内容,还是硬编码在您的应用中。小心以这种方式引入 SQL 注入(inject)。我假设 $passed_columns 仅包含您控制的值。

  • prepare() 在出错时返回 false,因此您应该始终检查其返回值并适本地响应错误。

  • print_r()实际上打印到输出,而不是返回一个字符串,除非您传递可选的第二个参数 true。

  • 令大多数 PHP 开发人员感到惊讶的是,双引号字符串实际上比单引号字符串快一点。不管怎样,差异都非常小,但是双引号字符串允许您将变量直接放在字符串中,为什么不呢?

下面是我将如何编写代码:

$set_terms = array();
foreach ($passed_columns as $c)
{
    $set_terms[] = "`$c` = :$c";
}
$set_clause = implode(",", $set_terms);

$user_id_list = implode(",", array_map(function($id) { return (int) $id; },
  $_POST["user_id"]);

$SQL = "UPDATE users SET {$set_clause} WHERE user_id IN ({$user_id_list})";

if (!($stmt = $dbh->prepare($SQL)) {
    die(print_r($dbh->errorInfo(), true));
}

if (!$stmt->execute($_POST["cols"])) {
    die(print_r($stmt->errorInfo(), true));
}

PS:如果出现错误,只使用 die() 可能不是最好的做法。专业的 Web 界面会为开发人员记录错误,然后向用户呈现更好的屏幕。

关于php - PDO 绑定(bind)问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16616254/

相关文章:

android - MYSQL查询,合并不相关表的记录以进行自动完成

php - MySQL 仅针对特定日期的无效日期时间格式

php - 让 HABTM 关系在 CakePHP 中独一无二

php - 无法执行菜单项(内部错误)...尝试访问 httpd.conf 和 httpd-vhosts.conf 时找不到文件

mysql - 选择字段中没有特定值的事件

MySQL Group By 和 Distinct

sql 组合 2 个不同顺序的查询

mysql - 子查询中存在的情况

php - 获取在现有 Symfony 3.4 应用程序中运行的 API 平台文档

mysql - 从 MYSQL 查询中计算列的平均值