如何将动态构造的ext/mysql查询转换为PDO预处理语句?

Dav*_*dom 9 php mysql pdo

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

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

Dav*_*dom 15

将查询从ext/mysql迁移到PDO预处理语句需要一种新方法来实现多个方面.在这里,我将介绍一些经常需要执行的常见任务.这绝不是穷举匹配每种可能的情况,它仅仅是为了演示动态生成查询时可以采用的一些技术.

在我们开始之前,要记住一些事情 - 如果某些事情无法正常工作,请在提问之前查看此列表!

  • 如果您未明确禁用模拟准备,则查询并不比使用更安全mysql_real_escape_string().有关完整说明,请参阅此处.
  • 无法在单个查询中混合使用命名占位符和问号占位符.在开始构建查询之前,您必须决定使用其中一个,而不能在中途切换.
  • 预准备语句中的占位符只能用于值,不能用于对象名称.换句话说,您不能使用占位符动态指定数据库,表,列或函数名称或任何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);
Run Code Online (Sandbox Code Playgroud)

使用数组构造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);
Run Code Online (Sandbox Code Playgroud)

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

// 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);
Run Code Online (Sandbox Code Playgroud)