【问题标题】:How do I convert a dynamically constructed ext/mysql query to a PDO prepared statement?如何将动态构造的 ext/mysql 查询转换为 PDO 准备语句?
【发布时间】:2012-09-01 21:12:39
【问题描述】:

我正在将我的一些使用 ext/mysql(mysql_*() 函数)的代码转换为 PDO 和准备好的语句。以前当我动态构造查询时,我只是通过mysql_real_escape_string() 传递我的字符串并将它们直接放入我的查询中,但现在我发现我需要在执行查询时将值作为数组传递,或者在执行之前绑定变量.

如何转换旧代码以使用新的数据库驱动程序?

【问题讨论】:

  • 很高兴看到出色而详细的答案。
  • @Fluffeh 还没完,但我要睡觉了。明天再来讨论。
  • 鉴于你已经拥有的,这肯定会成为我的去看看这个问题...在提出建议或回答问题时的列表!

标签: php mysql pdo


【解决方案1】:

将您的查询从 ext/mysql 迁移到 PDO 准备语句需要在多个方面采用新方法。在这里,我将介绍一些经常需要执行的常见任务。这绝不是详尽无遗地匹配所有可能的情况,它只是为了演示在动态生成查询时可以使用的一些技术。

在我们开始之前,有几件事要记住 - 如果有问题,请在提问前检查此列表!

  • 如果您没有明确禁用模拟准备,您的查询并不比使用 mysql_real_escape_string() 更安全See this 以获得完整的解释。
  • 不能在单个查询中混合命名占位符和问号占位符。在开始构建查询之前,您必须决定使用其中一个,不能中途切换。
  • 预准备语句中的占位符只能用于值,不能用于对象名称。换句话说,您不能使用占位符动态指定数据库、表、列或函数名称,或任何 SQL 关键字。一般来说,如果您发现需要这样做,说明您的应用程序设计有误,您需要重新检查。
  • 用于指定数据库/表/列标识符的任何变量都不应直接来自用户输入。换句话说,不要使用$_POST$_GET$_COOKIE 或任何其他来自外部源的数据来指定您的列名。您应该在使用此数据构建动态查询之前对其进行预处理。
  • PDO 命名占位符在查询中指定为:name。传入数据以执行时,相应的数组键可以选择包含前导:,但这不是必需的。占位符名称应仅包含字母数字字符。
  • 命名占位符不能在查询中多次使用。要多次使用相同的值,您必须使用多个不同的名称。如果您的查询包含许多重复值,请考虑使用问号占位符。
  • 使用问号占位符时,传递值的顺序很重要。还需要注意的是,占位符位置是 1 索引的,而不是 0 索引的。

以下所有示例代码均假定已建立数据库连接,并且相关 PDO 实例存储在变量 $db 中。


使用关联数组作为列/值列表

最简单的方法是使用命名占位符。

使用 ext/mysql 可以在构造查询时转义值并将转义值直接放入查询中。在构造 PDO 准备语句时,我们使用数组键来指定占位符名称,因此我们可以将数组直接传递给 PDOStatement::execute()

对于这个例子,我们有一个包含三个键/值对的数组,其中键代表列名,值代表列的值。我们要选择任何列匹配的所有行(数据具有OR 关系)。

// The array you want to use for your field list
$data = array (
  'field1' => 'value1',
  'field2' => 'value2',
  'field3' => 'value3'
);

// A temporary array to hold the fields in an intermediate state
$whereClause = array();

// Iterate over the data and convert to individual clause elements
foreach ($data as $key => $value) {
    $whereClause[] = "`$key` = :$key";
}

// Construct the query
$query = '
  SELECT *
  FROM `table_name`
  WHERE '.implode(' OR ', $whereClause).'
';

// Prepare the query
$stmt = $db->prepare($query);

// Execute the query
$stmt->execute($data);

使用数组构造IN (<value list>)子句的值列表

实现这一点的最简单方法是使用问号占位符。

这里我们有一个包含 5 个字符串的数组,我们想要匹配给定的列名,并返回列值与 5 个数组值中的至少一个匹配的所有行。

// The array of values
$data = array (
  'value1',
  'value2',
  'value3',
  'value4',
  'value5'
);

// Construct an array of question marks of equal length to the value array
$placeHolders = array_fill(0, count($data), '?');

// Normalise the array so it is 1-indexed
array_unshift($data, '');
unset($data[0]);

// Construct the query
$query = '
  SELECT *
  FROM `table_name`
  WHERE `field` IN ('.implode(', ', $placeHolders).')
';

// Prepare the query
$stmt = $db->prepare($query);

// Execute the query
$stmt->execute($data);

如果您已经确定要使用带有命名占位符的查询,则该技术会稍微复杂一些,但并不复杂。您只需遍历数组即可将其转换为关联数组并构造命名占位符。

// The array of values
$data = array (
  'value1',
  'value2',
  'value3',
  'value4',
  'value5'
);

// Temporary arrays to hold the data
$placeHolders = $valueList = array();

// Loop the array and construct the named format
for ($i = 0, $count = count($data); $i < $count; $i++) {
  $placeHolders[] = ":list$i";
  $valueList["list$i"] = $data[$i];
}

// Construct the query
$query = '
  SELECT *
  FROM `table_name`
  WHERE `field` IN ('.implode(', ', $placeHolders).')
';

// Prepare the query
$stmt = $db->prepare($query);

// Execute the query
$stmt->execute($valueList);

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2010-12-19
  • 2018-03-11
  • 1970-01-01
  • 2011-08-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多