我想在基于字符串数组的查询中创建一个“动态”WHERE子句。我想使用Mysqi的预准备语句来运行创建的查询。
到目前为止,我的代码PHP:
$searchArray = explode(' ', $search);
$searchNumber = count($searchArray);
$searchStr = "tags.tag LIKE ? ";
for($i=1; $i<=$searchNumber-1 ;$i++){
$searchStr .= "OR tags.tag LIKE ? ";
}我的问题是:
SELECT tag FROM tags WHERE $searchStr;更多PHP:
$stmt -> bind_param(str_repeat('s', count($searchArray)));现在,这显然给了我一个错误,因为bind_param部分只包含所需细节的一半。
我应该如何继续?
有没有其他(更好的)方法来做到这一点?
安全吗?
发布于 2013-01-12 04:09:14
在here找到的答案的帮助下解决了它。
$query = "SELECT * FROM tags WHERE tags.tag LIKE CONCAT('%',?,'%')" . str_repeat(" OR tags.tag LIKE CONCAT('%',?,'%')", $searchNumber - 1)
$stmt = $mysqli -> prepare($query);
$bind_names[] = str_repeat('s', $searchNumber);
for ($i = 0; $i < count($searchArray); $i++){
$bind_name = 'bind'.$i; //generate a name for variable bind1, bind2, bind3...
$$bind_name = $searchArray[$i]; //create a variable with this name and put value in it
$bind_names[] = & $$bind_name; //put a link to this variable in array
}
call_user_func_array(array($stmt, 'bind_param'), &$bind_names);
$stmt -> execute();发布于 2013-01-11 08:21:39
关于问题的安全性部分,带有占位符的预准备语句与使用值填充这些占位符的验证机制一样安全。在mysqli prepared语句的情况下,documentation说:
SQL标记只在
语句中的特定位置是合法的。例如,允许在INSERT语句的VALUES()列表中使用它们(为行指定列值),或者在与WHERE子句中的列进行比较时指定比较值。
但是,它们不允许用于标识符(如表名或列名),不允许在命名select语句返回的列的选择列表中使用,也不允许用于指定二元运算符的两个操作数,例如=等号。后一种限制是必要的,因为无法确定参数类型。不允许将marker与NULL by进行比较?也为空。通常,参数只在数据操作语言(DML)语句中是合法的,而在数据定义语言(DDL)语句中不合法。
这显然排除了修改查询的一般语义的任何可能性,这使得它更难(但不是不可能)改变它的原始意图。
关于查询的动态部分,您可以在查询条件构建部分使用str_repeat,而不是执行循环:
$searchStr = 'WHERE tags.tag LIKE ?' .
str_repeat($searchNumber - 1, ' OR tags.tag LIKE ?');对于bind_param调用,您应该像这样使用call_user_func_array:
$bindArray[0] = str_repeat('s', $searchNumber);
array_walk($searchArray,function($k,&$v) use (&$bindArray) {$bindArray[] = &$v;});
call_user_func_array(array($stmt,'bind_param'), $bindArray);希望上面的代码片段能够将$bindArray的每个值与查询中相应的占位符绑定在一起。
附注:
然而,你应该注意两件事:
call_user_func_array的第二个参数需要一个整数索引数组。我不确定当dictionary.mysqli_stmt_bind_param requires的参数通过引用传递时,它会有什么表现。对于第一点,您只需要确保$bindArray使用整数索引,这就是上面代码中的情况(或者检查call_user_func_array不会被您提供的数组阻塞)。
对于第二点,如果您打算在调用bind_param (即.通过call_user_func_array函数),并在执行查询之前执行。如果您希望这样做-例如,在同一脚本中使用不同的参数值多次运行同一查询,那么您将不得不在下面的查询执行中使用相同的数组( $bindArray),并使用相同的键更新数组条目。复制另一个数组将不起作用,除非手动完成:
foreach($bindArray as $k => $v)
$bindArray[$k] = some_new_value();或
foreach($bindArray as &$v)
$v = some_new_value();上面的方法是可行的,因为它不会破坏bind_param在之前调用语句时绑定到语句的数组条目上的引用。同样,下面的代码应该可以工作,因为它不会更改先前设置的引用。
array_walk($bindArray, function($k,&$v){$v = some_new_value();});发布于 2013-01-11 06:37:31
预准备语句需要有定义良好的参数数量;它不能有任何动态功能的元素。这意味着您必须生成所需的特定语句,并为此做好准备。
如果您的代码在数据库连接存在期间被多次调用,您可以做的是缓存这些准备好的语句,并根据您接受的参数数量对它们进行索引。(- in)这意味着当您第二次使用三个参数调用函数时,您已经完成了语句。但是,由于预准备语句无论如何都不能幸免于断开连接,因此只有在同一脚本运行中执行多个查询时,这才有意义。(我有意省略持久连接,因为这打开了一个完全不同的蠕虫罐头。)
顺便说一句,我不是MySQL专家,但是如果不连接where条件,而是编写WHERE tags in (tag1, tag2, tag3, tag4),这不是很重要吗
https://stackoverflow.com/questions/14268473
复制相似问题