PHP MySQLi prepare/bind where 子句
PHP MySQLi prepare/bind where-clause
出于安全原因,我尝试使用 mysqli::prepare(),但如果我尝试使用“?”准备 where 子句mysqli::bind_params() 失败(没有任何错误):
调用查询函数:
$messages = query('SELECT * FROM messages WHERE (receiverID=? AND receiverType=?) OR (senderID=? AND senderType=?) ORDER BY date DESC', false, '', (int)$_SESSION['ID'], (string)$_SESSION['mode'][0], (int)$_SESSION['ID'], (string)$_SESSION['mode'][0]);
查询函数:
function query($sql, $fetch, $error, ...$values) {
global $language; //get translations
global $mysqli; //get database
$returnResult = (strtolower(substr(trim($sql), 0, 6)) === 'select'); //check for select ---> retrun result
if(!empty($error)) $error .= '\n'; //next line after error message (styling / readability)
$stmt = $mysqli->prepare($sql) or back($error.$language['mysqliErrorPrepare'].$stmt->error); //prepare
foreach($values as $value) { //go through all params
$stmt->bind_param((is_string($value)?'s':(is_float($value)?'d':(is_int($value)?'i':'b'))), $value) or back($error.$language['mysqliErrorBind'].$stmt->error); //bind param
}
$stmt->execute() or back($error.$language['mysqliErrorExecute'].$stmt->error); //execute
if($returnResult) $result = $stmt->get_result() or back($error.$language['mysqliErrorGetResult'].$mysqli->error); //get result if necessary
$stmt->close(); //close
if($returnResult AND $fetch) $result = $result->fetch_assoc()or back($error.$language['mysqliErrorFetch'].$mysqli->error); //fetch if required
return ($returnResult?$result:true); //return result / true
}
问题:
- 为什么会失败?
- 我可以准备 where 子句吗?
谢谢! - Minding
不是因为它没有被任何活动数据库调用吗?
尝试 $db->query();
如下:
$messages = $db->query('SELECT * FROM messages WHERE (receiverID=? AND receiverType=?) OR (senderID=? AND senderType=?) ORDER BY date DESC', false, '', (int)$_SESSION['ID'], (string)$_SESSION['mode'][0], (int)$_SESSION['ID'], (string)$_SESSION['mode'][0]);
这不起作用的原因是我试图绑定参数 一个接一个 ,而 mysqli::bind_param() 期望接收 一次所有参数。因为这个 MySQL 只有一个参数,但是有 4 个问号可以使用,这显然会导致错误。
动态绑定多个参数的"right"方法是这样的:
if(count($paramValues) > 0) {
$paramTypes = '';
$paramBindValues = [&$paramTypes];
foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($paramValues), RecursiveIteratorIterator::LEAVES_ONLY) as $paramValue) {
$paramTypes .= (is_string($paramValue)?'s':(is_float($paramValue)?'d':(is_int($paramValue)?'i':'b')));
$paramBindValues[] = &$paramValue;
unset($paramValue);
}
if(!$stmt->bind_param(...$paramBindValues))
throw new Error($sql);
}
但请注意,这也破坏了准备好的语句的可重用性,因此可能具有次优性能。
例子:
foreach($users as $user) {
//This prepares the statement everytime.
$info = query('SELECT info FROM users WHERE ID=?', true, '', $user['ID']);
}
//This only prepares the statement once.
$stmt = $mysqli->prepare('SELECT info FROM users WHERE ID=?');
foreach($users as $user) {
$stmt->bind_params('i', $user['ID']);
$stmt->execute();
$info = $stmt->get_result()->fetch_assoc();
}
$stmt->close();
感谢@Sean 指出 mysqli::bind_params() 一次接受所有参数并提供示例。
希望这对您有所帮助。 -心灵
出于安全原因,我尝试使用 mysqli::prepare(),但如果我尝试使用“?”准备 where 子句mysqli::bind_params() 失败(没有任何错误):
调用查询函数:
$messages = query('SELECT * FROM messages WHERE (receiverID=? AND receiverType=?) OR (senderID=? AND senderType=?) ORDER BY date DESC', false, '', (int)$_SESSION['ID'], (string)$_SESSION['mode'][0], (int)$_SESSION['ID'], (string)$_SESSION['mode'][0]);
查询函数:
function query($sql, $fetch, $error, ...$values) {
global $language; //get translations
global $mysqli; //get database
$returnResult = (strtolower(substr(trim($sql), 0, 6)) === 'select'); //check for select ---> retrun result
if(!empty($error)) $error .= '\n'; //next line after error message (styling / readability)
$stmt = $mysqli->prepare($sql) or back($error.$language['mysqliErrorPrepare'].$stmt->error); //prepare
foreach($values as $value) { //go through all params
$stmt->bind_param((is_string($value)?'s':(is_float($value)?'d':(is_int($value)?'i':'b'))), $value) or back($error.$language['mysqliErrorBind'].$stmt->error); //bind param
}
$stmt->execute() or back($error.$language['mysqliErrorExecute'].$stmt->error); //execute
if($returnResult) $result = $stmt->get_result() or back($error.$language['mysqliErrorGetResult'].$mysqli->error); //get result if necessary
$stmt->close(); //close
if($returnResult AND $fetch) $result = $result->fetch_assoc()or back($error.$language['mysqliErrorFetch'].$mysqli->error); //fetch if required
return ($returnResult?$result:true); //return result / true
}
问题:
- 为什么会失败?
- 我可以准备 where 子句吗?
谢谢! - Minding
不是因为它没有被任何活动数据库调用吗?
尝试 $db->query();
如下:
$messages = $db->query('SELECT * FROM messages WHERE (receiverID=? AND receiverType=?) OR (senderID=? AND senderType=?) ORDER BY date DESC', false, '', (int)$_SESSION['ID'], (string)$_SESSION['mode'][0], (int)$_SESSION['ID'], (string)$_SESSION['mode'][0]);
这不起作用的原因是我试图绑定参数 一个接一个 ,而 mysqli::bind_param() 期望接收 一次所有参数。因为这个 MySQL 只有一个参数,但是有 4 个问号可以使用,这显然会导致错误。
动态绑定多个参数的"right"方法是这样的:
if(count($paramValues) > 0) {
$paramTypes = '';
$paramBindValues = [&$paramTypes];
foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($paramValues), RecursiveIteratorIterator::LEAVES_ONLY) as $paramValue) {
$paramTypes .= (is_string($paramValue)?'s':(is_float($paramValue)?'d':(is_int($paramValue)?'i':'b')));
$paramBindValues[] = &$paramValue;
unset($paramValue);
}
if(!$stmt->bind_param(...$paramBindValues))
throw new Error($sql);
}
但请注意,这也破坏了准备好的语句的可重用性,因此可能具有次优性能。
例子:
foreach($users as $user) {
//This prepares the statement everytime.
$info = query('SELECT info FROM users WHERE ID=?', true, '', $user['ID']);
}
//This only prepares the statement once.
$stmt = $mysqli->prepare('SELECT info FROM users WHERE ID=?');
foreach($users as $user) {
$stmt->bind_params('i', $user['ID']);
$stmt->execute();
$info = $stmt->get_result()->fetch_assoc();
}
$stmt->close();
感谢@Sean 指出 mysqli::bind_params() 一次接受所有参数并提供示例。
希望这对您有所帮助。 -心灵