使用 mysqli::bindParams() 时返回空结果集
Empty resultsset returned when using mysqli::bindParams()
对于我当前的项目,我正在构建一个简单的 QueryBuilder class。一切正常,除了与 mysqli 的 bindParam() 方法绑定的参数,我就是找不到错误。
代码和过程:
请求处理程序从我的用户模型调用以下方法:
public function getPasshashByUsername($username) {
$qb = new SelectQuery();
$qb->select(['passhash'])
->from($this->sourceName)
->where('nickname', $username);
$stmt = $qb->build();
$resultset = $qb->execute($stmt);
return $resultset->fetch_assoc()['passhash'] ?? null;
}
对于具有潜在参数的部分,SelectQuery class 的工作方式如下:
public function where($col, $val) {
$this->aQueryParts['where'] = $this->addCondition('WHERE',$col, $val);
return $this;
}
private function addCondition($type, $col, $val) {
$sExpr = $type . ' ';
$sType = '';
if ( is_int($val) || is_bool($val) ) {
$sExpr .= '? = ' . $val;
$sType .= 'i';
} elseif ( is_double($val) ) {
$sExpr .= '? = ' . $val;
$sType .= 'd';
} else {
$sExpr .= '? LIKE \'' . $val . '\'';
$sType .= 's';
}
$this->sTypes .= $sType;
$this->aParams[] = $col;
return $sExpr;
}
现在 build() 方法开始运行,参数将被绑定到这里:
public function build() {
if ( isset($this->aQueryParts['from']) ) {
$sQuery = '';
foreach ( $this->aQueryParts as $part ) {
if ( is_string($part) ) {
if ( !empty($part) ) {
$sQuery .= $part . ' ';
}
continue;
}
if ( is_array($part) ) {
foreach ( $part as $entry ) {
if ( !empty($entry) ) {
$sQuery .= $entry . ' ';
}
}
}
}
$this->sQuery = $sQuery;
// Marker 1
//$this->sQuery = 'SELECT * FROM vwuser WHERE nickname LIKE \'TestUser\'';
} else {
Logger::send('You must at least call from() to get a valid query.');
die();
}
$this->con = (new DatabaseAdapter())->connect();
if ( !$stmt = $this->con->prepare($this->sQuery) ) {
$msg = 'Error while preparing statement: "' . $this->sQuery . '"';
Logger::send($msg);
die();
}
// make params by-ref
$params = [];
foreach ( $this->aParams as $param ) {
$params[] = &$param;
}
// Start Marker 2
if ( !empty($this->sTypes) ) {
if ( !$stmt->bind_param($this->sTypes, ...$params) ) {
$msg = 'Failed to bind parameters to Query: "' . $this->sQuery . '"';
Logger::send($msg);
die();
}
}
// End Marker 2
return $stmt;
}
execute() 方法只是以通常的方式包装 mysqli execute()。没有 Error/Exception 被抛出,它只是 returns 一个没有匹配项的结果集。关于 XDebug,这些是调用 bind_params():
时的相关值
$this->sQuery = "SELECT passhash FROM vwuser WHERE ? LIKE 'TestUser'";
$this->sParams = "s";
$this->aParams = [ "nickname" ];
如果我在“标记 1”下方的行中取消注释查询字符串的硬编码分配并注释掉标记 2 的块(bind_params 的调用),一切正常,所以看起来连接本身是有效的。
在 php.net 阅读 bind_params 需要引用后,我插入了“make by-ref”块,但它没有改变任何东西。
你真的应该考虑学习 PDO 而不是 mysqli 或使用一些现有的数据库访问库。
您不能绑定列名。重点是您绑定数据。你正在做相反的事情。我建议去掉 addCondition
方法,因为它实际上没有任何意义,并且会导致代码中出现许多错误。相反,只需将参数直接传递给 bind_param
并使用 str_repeat()
作为类型。
以下是您的操作方法:
public function where($col, $val)
{
$this->aQueryParts['where'] = 'WHERE '.$col. '=?';
$this->aParams[] = $val;
return $this;
}
public function build()
{
if (isset($this->aQueryParts['from'])) {
$sQuery = '';
foreach ($this->aQueryParts as $part) {
if (is_string($part)) {
if (!empty($part)) {
$sQuery .= $part . ' ';
}
continue;
}
if (is_array($part)) {
foreach ($part as $entry) {
if (!empty($entry)) {
$sQuery .= $entry . ' ';
}
}
}
}
$this->sQuery = $sQuery;
// Marker 1
//$this->sQuery = 'SELECT * FROM vwuser WHERE nickname LIKE \'TestUser\'';
} else {
Logger::send('You must at least call from() to get a valid query.');
die();
}
$this->con = (new DatabaseAdapter())->connect();
$stmt = $this->con->prepare($this->sQuery);
// Start Marker 2
if ($this->aParams) {
$stmt->bind_param(str_repeat('s', count($this->aParams)), ...$this->aParams);
}
// End Marker 2
return $stmt;
}
如您所见,我还删除了 if
语句。你真的应该考虑 enabling mysqli error reporting 而不是手动检查错误。
这是更正后的版本。
感谢@Dharman 给我提示。
错误是对行中问号位置的一些混淆:
$sExpr .= '? LIKE \'' . $val . '\'';
当然应该是:
$sExpr .= $col . ' LIKE ?';
所以addCondition()的修正版本是:
private function addCondition($type, $col, $val) {
$sExpr = $type . ' ';
$sType = '';
if ( is_int($val) || is_bool($val) ) {
$sExpr .= $col . ' = ?';
$sType .= 'i';
} elseif ( is_double($val) ) {
$sExpr .= $col . ' = ?';
$sType .= 'd';
} else {
$sExpr .= $col . ' LIKE ?';
$sType .= 's';
}
$this->sTypes .= $sType;
$this->aParams[] = $val;
return $sExpr;
}
对于我当前的项目,我正在构建一个简单的 QueryBuilder class。一切正常,除了与 mysqli 的 bindParam() 方法绑定的参数,我就是找不到错误。
代码和过程:
请求处理程序从我的用户模型调用以下方法:
public function getPasshashByUsername($username) { $qb = new SelectQuery(); $qb->select(['passhash']) ->from($this->sourceName) ->where('nickname', $username); $stmt = $qb->build(); $resultset = $qb->execute($stmt); return $resultset->fetch_assoc()['passhash'] ?? null; }
对于具有潜在参数的部分,SelectQuery class 的工作方式如下:
public function where($col, $val) { $this->aQueryParts['where'] = $this->addCondition('WHERE',$col, $val); return $this; } private function addCondition($type, $col, $val) { $sExpr = $type . ' '; $sType = ''; if ( is_int($val) || is_bool($val) ) { $sExpr .= '? = ' . $val; $sType .= 'i'; } elseif ( is_double($val) ) { $sExpr .= '? = ' . $val; $sType .= 'd'; } else { $sExpr .= '? LIKE \'' . $val . '\''; $sType .= 's'; } $this->sTypes .= $sType; $this->aParams[] = $col; return $sExpr; }
现在 build() 方法开始运行,参数将被绑定到这里:
public function build() { if ( isset($this->aQueryParts['from']) ) { $sQuery = ''; foreach ( $this->aQueryParts as $part ) { if ( is_string($part) ) { if ( !empty($part) ) { $sQuery .= $part . ' '; } continue; } if ( is_array($part) ) { foreach ( $part as $entry ) { if ( !empty($entry) ) { $sQuery .= $entry . ' '; } } } } $this->sQuery = $sQuery; // Marker 1 //$this->sQuery = 'SELECT * FROM vwuser WHERE nickname LIKE \'TestUser\''; } else { Logger::send('You must at least call from() to get a valid query.'); die(); } $this->con = (new DatabaseAdapter())->connect(); if ( !$stmt = $this->con->prepare($this->sQuery) ) { $msg = 'Error while preparing statement: "' . $this->sQuery . '"'; Logger::send($msg); die(); } // make params by-ref $params = []; foreach ( $this->aParams as $param ) { $params[] = &$param; } // Start Marker 2 if ( !empty($this->sTypes) ) { if ( !$stmt->bind_param($this->sTypes, ...$params) ) { $msg = 'Failed to bind parameters to Query: "' . $this->sQuery . '"'; Logger::send($msg); die(); } } // End Marker 2 return $stmt; }
execute() 方法只是以通常的方式包装 mysqli execute()。没有 Error/Exception 被抛出,它只是 returns 一个没有匹配项的结果集。关于 XDebug,这些是调用 bind_params():
时的相关值$this->sQuery = "SELECT passhash FROM vwuser WHERE ? LIKE 'TestUser'";
$this->sParams = "s";
$this->aParams = [ "nickname" ];
如果我在“标记 1”下方的行中取消注释查询字符串的硬编码分配并注释掉标记 2 的块(bind_params 的调用),一切正常,所以看起来连接本身是有效的。 在 php.net 阅读 bind_params 需要引用后,我插入了“make by-ref”块,但它没有改变任何东西。
你真的应该考虑学习 PDO 而不是 mysqli 或使用一些现有的数据库访问库。
您不能绑定列名。重点是您绑定数据。你正在做相反的事情。我建议去掉 addCondition
方法,因为它实际上没有任何意义,并且会导致代码中出现许多错误。相反,只需将参数直接传递给 bind_param
并使用 str_repeat()
作为类型。
以下是您的操作方法:
public function where($col, $val)
{
$this->aQueryParts['where'] = 'WHERE '.$col. '=?';
$this->aParams[] = $val;
return $this;
}
public function build()
{
if (isset($this->aQueryParts['from'])) {
$sQuery = '';
foreach ($this->aQueryParts as $part) {
if (is_string($part)) {
if (!empty($part)) {
$sQuery .= $part . ' ';
}
continue;
}
if (is_array($part)) {
foreach ($part as $entry) {
if (!empty($entry)) {
$sQuery .= $entry . ' ';
}
}
}
}
$this->sQuery = $sQuery;
// Marker 1
//$this->sQuery = 'SELECT * FROM vwuser WHERE nickname LIKE \'TestUser\'';
} else {
Logger::send('You must at least call from() to get a valid query.');
die();
}
$this->con = (new DatabaseAdapter())->connect();
$stmt = $this->con->prepare($this->sQuery);
// Start Marker 2
if ($this->aParams) {
$stmt->bind_param(str_repeat('s', count($this->aParams)), ...$this->aParams);
}
// End Marker 2
return $stmt;
}
如您所见,我还删除了 if
语句。你真的应该考虑 enabling mysqli error reporting 而不是手动检查错误。
这是更正后的版本。 感谢@Dharman 给我提示。 错误是对行中问号位置的一些混淆:
$sExpr .= '? LIKE \'' . $val . '\'';
当然应该是:
$sExpr .= $col . ' LIKE ?';
所以addCondition()的修正版本是:
private function addCondition($type, $col, $val) {
$sExpr = $type . ' ';
$sType = '';
if ( is_int($val) || is_bool($val) ) {
$sExpr .= $col . ' = ?';
$sType .= 'i';
} elseif ( is_double($val) ) {
$sExpr .= $col . ' = ?';
$sType .= 'd';
} else {
$sExpr .= $col . ' LIKE ?';
$sType .= 's';
}
$this->sTypes .= $sType;
$this->aParams[] = $val;
return $sExpr;
}