如何使用准备好的 PDO 语句设置 ORDER BY 参数?

Posted

技术标签:

【中文标题】如何使用准备好的 PDO 语句设置 ORDER BY 参数?【英文标题】:How do I set ORDER BY params using prepared PDO statement? 【发布时间】:2011-02-02 07:11:39 【问题描述】:

我在使用 SQL 的 ORDER BY 部分中的参数时遇到问题。它不会发出任何警告,但不会打印任何内容。

$order = 'columnName';
$direction = 'ASC';

$stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY :order :direction");
$stmt->bindParam(':my_param', $is_live, PDO::PARAM_STR);
$stmt->bindParam(':order', $order, PDO::PARAM_STR);
$stmt->bindParam(':direction', $direction, PDO::PARAM_STR);
$stmt->execute();

:my_param 有效,但 :order:direction 无效。它没有在内部正确转义吗?我是否将其直接插入 SQL 中?像这样:

$order = 'columnName';
$direction = 'ASC';

$stmt = $db->prepare("SELECT * from table WHERE column = :my_param ORDER BY $order $direction");

是否有 PDO::PARAM_COLUMN_NAME 常量或等价物?

谢谢!

【问题讨论】:

另见Can php PDO Statements accept the table name as parameter? 【参考方案1】:

是的,您无法将其直接插入 SQL 中。当然,有一些预防措施。 每个运算符/标识符都必须在您的脚本中进行硬编码,如下所示:

$orders=array("name","price","qty");
$key=array_search($_GET['sort'],$orders);
$order=$orders[$key];
$query="SELECT * from table WHERE is_live = :is_live ORDER BY $order";

方向相同。

我写了一个whitelisting helper function用于这种情况,它大大减少了需要编写的代码量:

$order = white_list($order, ["name","price","qty"], "Invalid field name");
$direction = white_list($direction, ["ASC","DESC"], "Invalid ORDER BY direction");

$sql = "SELECT field from table WHERE column = ? ORDER BY $order $direction";
$stmt = $db->prepare($sql);
$stmt->execute([$is_live]);

这里的想法是检查值并在不正确的情况下引发错误。

【讨论】:

如果你有一个有效的字符串数组并重新输入你所拥有的 $input = (string)$input;(string) 你可以简单地检查 if( in_array($input, $valid_orderbys_array) ) 它基本上应该做一样的。【参考方案2】:

我认为你不能:

order by 子句中使用占位符 绑定列名:您只能绑定值或变量,并将它们的值注入到准备好的语句中。

【讨论】:

老实说 - IS 可以在 ORDER BY 子句中绑定 (不是标识符) 这是部分正确的。您只能使用绑定占位符来提供 values永远可以使用绑定占位符来提供 标识符(如表名、列名)、SQL 关键字value 以外的 SQL 语法。 (基本上,绑定占位符可以代替字符串文字、日期文字或数字文字。句号。但正如 zerkms 指出的那样,由于可以在 ORDER BY 子句中使用文字作为表达式的一部分,所以它事实上可以使用绑定占位符作为ORDER BY子句中表达式的一部分。【参考方案3】:

有可能ORDER BY 子句中使用准备好的语句,不幸的是,您需要传递名称的列顺序,并且需要设置PDO_PARAM_INT 和类型。

mysql 中,您可以使用此查询获取列的顺序:

SELECT column_name, ordinal_position FROM information_schema.columns 
WHERE table_name = 'table' and table_schema = 'database'

PHP 代码:

$order = 2;

$stmt = $db->prepare("SELECT field from table WHERE column = :param ORDER BY :order DESC");
$stmt->bindParam(':param', $is_live, PDO::PARAM_STR);
$stmt->bindParam(':order', $order, PDO::PARAM_INT);
$stmt->execute();

【讨论】:

聪明的解决方法。不过就个人而言,我认为首先必须弄清楚列号是太多了。 $direction 仍然存在问题,您刚刚将其硬编码为 DESC【参考方案4】:

我认为您不能将 ASC/DESC 作为准备好的语句的一部分,但是如果您在 sql 查询中将它们全部列出,则可以像这样:

// Validate between 2 possible values:
$sortDir = isset($_GET['sortDir']) && $_GET['sortDir'] === 'ASC' ? 'ASC' : 'DESC';
$sql = "
...
     order 
        by 
           case :orderByCol
               when 'email' then email
               when 'age' then age
               else surname
           end
           $sortDir
";
$stmt = $db->prepare($sql);
$stmt->bindParam(':orderByCol', $someColumn);
$stmt->execute();

由于 ASC/DESC 只有两个可能的值,您可以使用 php 代码轻松验证并在它们之间选择硬编码值。

您也可以为此使用 ELT(FIELD(,,,,,),,,,,) 函数,但是即使列是数字数据类型,排序也将始终作为字符串完成应该使用数字语义/排序规则进行排序。

【讨论】:

【参考方案5】:

不幸的是,我想您无法使用准备好的语句来实现它。 这将使其不可缓存,因为不同的列可能具有可以使用特殊排序策略进行排序的值。

使用标准转义创建查询并直接执行。

【讨论】:

【参考方案6】:

可能。您可以在“order by”子句中使用数字而不是字段名称。这是一个从 1 开始的数字,按照查询中的字段名称顺序排列。您可以为 ASC 或 DESC 连接一个字符串。例如 “从 tab1 顺序中选择 col1,col2,col3?” + strDesc + “限制 10,5”。 strDesc="ASC" / "DESC"。

【讨论】:

【参考方案7】:

如果我没记错的话,帕斯卡是对的。 PDO 中唯一可能的绑定是值的绑定,就像您对 ':my_param' 参数所做的那样。 但是,这并没有造成任何伤害:

$stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY ".$order ." ".$direction);
$stmt->bindParam(':my_param', $is_live, PDO::PARAM_STR);
$stmt->execute();

唯一需要注意的是 $order$direction 的正确转义,但由于您手动设置它们并且没有通过用户输入设置它们,我想您已经设置好了。

【讨论】:

如果他不打算更改 orderby 列和方向,那么我认为让它们成为变量根本没有任何意义。

以上是关于如何使用准备好的 PDO 语句设置 ORDER BY 参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 pdo 的准备好的语句用于 order by 和 limit 子句?

如何使用带有 BIT(1) 列的 PDO 准备语句?

如何使用准备好的语句将表单数据插入 PDO?

如何查看准备好的 PDO SQL 语句 [重复]

在 PHP 中到处使用准备好的语句? (PDO)

PDO 准备好的语句如何帮助防止 SQL 易受攻击的语句?