在编写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,然后通过设置PDO::ATTR_STATEMENT_CLASS属性将类设置为语句类。扩展的PDOStatement(或PDO::prepare)可以提取命名的参数,查找重复项并自动生成替换项。它还将记录这些重复项。当绑定和执行方法传递一个命名参数时,它将测试该参数是否重复并将该值绑定到每个替换参数。
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); } } }