【问题标题】:How can I pass an array of PDO parameters yet still specify their types?如何传递一组 PDO 参数但仍指定它们的类型?
【发布时间】:2012-05-13 07:47:45
【问题描述】:
$sql = "SELECT * FROM table WHERE id LIKE CONCAT('%', :id, '%')
LIMIT :limit1, :limit2";

我还想像这样使用数组输入:

$stmt->execute($array);

否则我无法重复使用相同的方法来执行我的查询。

同时 :limit1 和 :limit2 不起作用,除非像这样放入:

$stmt->bindParam(':limit1', $limit1, PDO::PARAM_INT);

我都尝试了,但它没有使用 bindParams 执行:

$stmt->bindParam(':limit2', $limit2, PDO::PARAM_INT);
$stmt->execute($array);

解决方法是什么?

我以为我可以扩展 PDOStatement 并添加一个新方法“bindLimit”之类的,但我不知道 PDO 使用什么内部方法将参数绑定到变量。

【问题讨论】:

  • +1 表示扩展 PDOStatement 的想法(但我不确定这是否是解决方案)。
  • 你不能检查限制参数并显式绑定它们(如果存在),否则只使用执行?
  • “否则我无法重复使用相同的方法来执行我的查询。”你能详细解释一下这个方法吗?
  • @webbiedave 它只是一种方便的方法来准备、绑定和执行限制变量的可选参数。我可以将字符串 'LIMIT n,n' 附加到 $sql 的末尾,但是我必须使用正则表达式来清理输入字符串。

标签: php mysql pdo


【解决方案1】:

PDOStatement::execute 的文档中所述:

input_parameters

包含与正在执行的 SQL 语句中的绑定参数一样多的元素的值数组。所有值都被视为PDO::PARAM_STR

在大多数情况下,这完全没有被注意到,因为 MySQL 的 implicit type conversion 处理其余部分(但如果 MySQL 使用特别奇怪的连接字符集,它可能会导致一些不良行为,因为将数字字符串转换回它们的数值可能没有给出预期的结果)。

在您的情况下,MySQL 正在尝试执行具有字符串参数的 LIMIT 子句。虽然它可以尝试使用其隐式类型转换来解决这个问题,就像它在其他任何地方所做的那样,一个字符串出现在整数应该出现的位置,it simply doesn't bother,然后哭着跑了。

因此,您需要告诉 PDO 这些特定参数是整数。因为即使 PHP 也不知道它的变量是什么类型,我相信唯一的方法是直接将它们与 PDOStatement::bindParamPDOStatement::bindValue 绑定,就像你正在做的那样(虽然它不是 strictly 必须将 $data_type 参数指定为 $variable 的简单 (int) 转换,这使得 PHP 原本无能的类型系统足以使用)。

就 PDO 用于将参数绑定到变量的内部方法而言,请查看 really_register_bound_param()(不出所料未接触 PHP,因此您将在 C 中获得很多乐趣)。

祝你好运!

【讨论】:

    【解决方案2】:

    如果您关闭PDO::ATTR_EMULATE_PREPARES 的默认设置,那么它将起作用。我刚刚发现 mysql 默认情况下该设置处于启用状态,这意味着您实际上从未使用过准备好的语句,php 在内部为您创建动态 sql,为您引用值并替换占位符。是的,一个主要的wtf。

    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array(5)); //works!
    

    由于性能原因,默认情况下会模拟准备。

    另见PDO MySQL: Use PDO::ATTR_EMULATE_PREPARES or not?

    【讨论】:

    • 是的,哈哈。在禁用该设置之前和之后检查 mysql 查询日志,你会发现这些年来你从未真正使用过准备好的语句,哈哈。
    • 我不得不同意eggyal:WTF?这是我听过的最离奇的事情。
    【解决方案3】:

    当限制值不是用户输入时,为什么要绑定它们?

    $start = 0;
    $limit = 20;
    $sql = "SELECT * FROM table WHERE id LIKE CONCAT('%', :id, '%')
        LIMIT $start, $limit";
    

    即使$start$limit 是根据用户输入(例如$_GET)确定的,您也可以使用is_int() 测试该值。

    【讨论】:

    • @YourCommonSense 愿意解释一下自己吗?
    • @YourCommonSense 我很想知道你为什么拒绝我的帖子。你能解释一下吗?
    • @YourCommonSense 如何从整数中获得注入攻击?答:不能
    猜你喜欢
    • 2012-02-14
    • 2011-06-03
    • 1970-01-01
    • 2011-01-11
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    相关资源
    最近更新 更多