php pdo 准备重复变量
在编写 pdo 语句时,是否可以重复变量的值?我的意思是:
$query = "UPDATE users SET firstname = :name WHERE firstname = :name";
$stmt = $dbh -> prepare($query);
$stmt -> execute(array(":name" => "Jackie"));
请注意,我重复“:name”名称持有者,而我只提供一次值。我怎样才能做到这一点?
在编写 pdo 语句时,是否可以重复变量的值?我的意思是:
$query = "UPDATE users SET firstname = :name WHERE firstname = :name";
$stmt = $dbh -> prepare($query);
$stmt -> execute(array(":name" => "Jackie"));
请注意,我重复“:name”名称持有者,而我只提供一次值。我怎样才能做到这一点?
简单的答案是:你不能。PDO 对预准备的语句使用抽象,这有一些限制。不幸的是,这是一个,你必须使用类似的东西来解决
$query = "UPDATE users SET firstname = :name1 WHERE firstname = :name2";
$stmt = $dbh -> prepare($query);
$stmt -> execute(array(":name1" => "Jackie", ":name2" => "Jackie"));
在某些情况下,例如使用某些版本的PDO / MySQL驱动程序模拟预准备语句,支持重复的命名参数;但是,这不应该被依赖,因为它是脆弱的(例如,它可以使升级需要更多的工作)。
如果要支持命名参数的多次出现,则始终可以扩展 PDO 和 PDOStatement(通过经典继承或组合),或者仅扩展 PDOStatement,并通过设置属性将类设置为语句类。扩展的PDOStatement(或)可以提取命名参数,查找重复并自动生成替换。它还将记录这些重复项。绑定和执行方法在传递命名参数时,将测试参数是否重复,并将值绑定到每个替换参数。PDO::ATTR_STATEMENT_CLASS
PDO::prepare
注意:以下示例未经测试,可能存在错误(代码注释中记录了一些与语句解析相关的错误)。
class PDO_multiNamed extends PDO {
function prepare($stmt) {
$params = array_count_values($this->_extractNamedParams());
# get just named parameters that are repeated
$repeated = array_filter($params, function ($count) { return $count > 1; });
# start suffixes at 0
$suffixes = array_map(function ($x) {return 0;}, $repeated);
/* Replace repeated named parameters. Doesn't properly parse statement,
* so may replacement portions of the string that it shouldn't. Proper
* implementation left as an exercise for the reader.
*
* $param only contains identifier characters, so no need to escape it
*/
$stmt = preg_replace_callback(
'/(?:' . implode('|', array_keys($repeated)) . ')(?=\W)/',
function ($matches) use (&$suffixes) {
return $matches[0] . '_' . $suffixes[$matches[0]]++;
}, $stmt);
$this->prepare($stmt,
array(
PDO::ATTR_STATEMENT_CLASS => array('PDOStatement_multiNamed', array($repeated)))
);
}
protected function _extractNamedParams() {
/* Not actually sufficient to parse named parameters, but it's a start.
* Proper implementation left as an exercise.
*/
preg_match_all('/:\w+/', $stmt, $params);
return $params[0];
}
}
class PDOStatement_multiNamed extends PDOStatement {
protected $_namedRepeats;
function __construct($repeated) {
# PDOStatement::__construct doesn't like to be called.
//parent::__construct();
$this->_namedRepeats = $repeated;
}
/* 0 may not be an appropriate default for $length, but an examination of
* ext/pdo/pdo_stmt.c suggests it should work. Alternatively, leave off the
* last two arguments and rely on PHP's implicit variadic function feature.
*/
function bindParam($param, &$var, $data_type=PDO::PARAM_STR, $length=0, $driver_options=array()) {
return $this->_bind(__FUNCTION__, $param, func_get_args());
}
function bindValue($param, $var, $data_type=PDO::PARAM_STR) {
return $this->_bind(__FUNCTION__, $param, func_get_args());
}
function execute($input_parameters=NULL) {
if ($input_parameters) {
$params = array();
# could be replaced by array_map_concat, if it existed
foreach ($input_parameters as $name => $val) {
if (isset($this->_namedRepeats[$param])) {
for ($i=0; $i < $this->_namedRepeats[$param], ++$i) {
$params["{$name}_{$i}"] = $val;
}
} else {
$params[$name] = $val;
}
}
return parent::execute($params);
} else {
return parent::execute();
}
}
protected function _bind($method, $param, $args) {
if (isset($this->_namedRepeats[$param])) {
$result = TRUE;
for ($i=0; $i < $this->_namedRepeats[$param], ++$i) {
$args[0] = "{$param}_{$i}";
# should this return early if the call fails?
$result &= call_user_func_array("parent::$method", $args);
}
return $result;
} else {
return call_user_func_array("parent::$method", $args);
}
}
}
在我的情况下,当我从dblib freedts切换到sqlsrv PDO驱动程序时,会出现此错误。Dblib 驱动程序处理重复的参数名称,没有错误。我有相当复杂的动态查询,有很多联合和很多重复的参数,所以我使用以下帮助程序作为解决方法:
function prepareMsSqlQueryParams($query, $params): array
{
$paramsCount = [];
$newParams = [];
$pattern = '/(:' . implode('|:', array_keys($params)) . ')/';
$query = preg_replace_callback($pattern, function ($matches) use ($params, &$newParams, &$paramsCount) {
$key = ltrim($matches[0], ':');
if (isset($paramsCount[$key])) {
$paramsCount[$key]++;
$newParams[$key . $paramsCount[$key]] = $params[$key];
return $matches[0] . $paramsCount[$key];
} else {
$newParams[$key] = $params[$key];
$paramsCount[$key] = 0;
return $matches[0];
}
}, $query);
return [$query, $newParams];
}
然后你可以这样使用它:
$query = "UPDATE users SET firstname = :name WHERE firstname = :name";
$params = [":name" => "Jackie"];
// It will return "UPDATE users SET firstname = :name WHERE firstname = :name1"; with appropriate parameters array
list($query, $params) = prepareMsSqlQueryParams($query, $params);
$stmt = $dbh->prepare($query);
$stmt->execute(params);