我一直在学习为我的sql查询使用预备语句和绑定语句,到目前为止我已经提出了这个问题,但是它工作正常但是当涉及到多个参数或者当不需要参数时,它根本不是动态的,
public function get_result($sql,$parameter)
{
# create a prepared statement
$stmt = $this->mysqli->prepare($sql);
# bind parameters for markers
# but this is not dynamic enough...
$stmt->bind_param("s", $parameter);
# execute query
$stmt->execute();
# these lines of code below return one dimentional array, similar to mysqli::fetch_assoc()
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$var = $field->name;
$$var = null;
$parameters[$field->name] = &$$var;
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while($stmt->fetch())
{
return $parameters;
//print_r($parameters);
}
# close statement
$stmt->close();
}
这就是我调用对象类的方式,
$mysqli = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);
有时候我不需要传递任何参数,
$sql = "
SELECT *
FROM root_contacts_cfm
";
print_r($output->get_result($sql));
有时候我只需要一个参数,
$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
ORDER BY cnt_id DESC
";
print_r($output->get_result($sql,'1'));
有时我只需要多个参数,
$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
AND root_contacts_cfm.cnt_firstname = ?
ORDER BY cnt_id DESC
";
print_r($output->get_result($sql,'1','Tk'));
所以,我认为这条线对于上面的动态任务来说还不够动态,
$stmt->bind_param("s", $parameter);
要动态构建bind_param,我在网上的其他帖子中找到了这个。
call_user_func_array(array(&$stmt, 'bind_params'), $array_of_params);
我尝试修改php.net中的一些代码,但我无处可去,
if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+
{
$refs = array();
foreach($arr as $key => $value)
$array_of_param[$key] = &$arr[$key];
call_user_func_array(array(&$stmt, 'bind_params'), $array_of_params);
}
为什么呢?我有什么想法可以让它发挥作用吗?
或许还有更好的解决方案?
答案 0 :(得分:16)
找到了mysqli的答案:
public function get_result($sql,$types = null,$params = null)
{
# create a prepared statement
$stmt = $this->mysqli->prepare($sql);
# bind parameters for markers
# but this is not dynamic enough...
//$stmt->bind_param("s", $parameter);
if($types&&$params)
{
$bind_names[] = $types;
for ($i=0; $i<count($params);$i++)
{
$bind_name = 'bind' . $i;
$$bind_name = $params[$i];
$bind_names[] = &$$bind_name;
}
$return = call_user_func_array(array($stmt,'bind_param'),$bind_names);
}
# execute query
$stmt->execute();
# these lines of code below return one dimentional array, similar to mysqli::fetch_assoc()
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$var = $field->name;
$$var = null;
$parameters[$field->name] = &$$var;
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while($stmt->fetch())
{
return $parameters;
//print_r($parameters);
}
# the commented lines below will return values but not arrays
# bind result variables
//$stmt->bind_result($id);
# fetch value
//$stmt->fetch();
# return the value
//return $id;
# close statement
$stmt->close();
}
然后:
$mysqli = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);
$sql = "
SELECT *
FROM root_contacts_cfm
ORDER BY cnt_id DESC
";
print_r($output->get_result($sql));
$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
ORDER BY cnt_id DESC
";
print_r($output->get_result($sql,'s',array('1')));
$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
AND root_contacts_cfm.cnt_firstname = ?
ORDER BY cnt_id DESC
";
print_r($output->get_result($sql, 'ss',array('1','Tk')));
对于这个问题,mysqli是如此蹩脚。我想我应该迁移到PDO!
答案 1 :(得分:6)
使用PHP 5.6,您可以在unpacking operator(...$var
)的帮助下轻松完成此操作,并使用get_result()
public function get_result($sql,$types = null,$params = null) {
$stmt = $this->mysqli->prepare($sql);
$stmt->bind_param($types, ...$params);
if(!$stmt->execute()) return false;
return $stmt->get_result();
}
示例:
$mysqli = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);
$sql = "SELECT * FROM root_contacts_cfm WHERE root_contacts_cfm.cnt_id = ?
AND root_contacts_cfm.cnt_firstname = ?
ORDER BY cnt_id DESC";
$res = $output->get_result($sql, 'ss',array('1','Tk'));
while($row = res->fetch_assoc()){
echo $row['fieldName'] .'<br>';
}
答案 2 :(得分:5)
或许还有更好的解决方案?
这个答案对你没什么帮助,但你应该认真考虑从mysqli切换到PDO。
这主要是因为PDO通过内置函数完成了你在mysqli中所做的事情。除了manual param binding之外,execute method还可以使用一组参数。
PDO很容易扩展,并且添加方便的方法来获取所有东西和返回而不是做准备 - 执行舞蹈非常容易。
答案 3 :(得分:3)
PHP 5.6或更高版本:
$stmt->bind_param(str_repeat("s", count($data)), ...$data);
使用 PHP 5.5或更低版本,您可能(以及我所做的)期望以下工作:
call_user_func_array(
array($stmt, "bind_param"),
array_merge(array(str_repeat("s", count($data))), $data));
...但是mysqli_stmt::bind_param
期望它的参数是引用,而这会传递一个值列表。
你可以通过首先创建一个对原始数组的引用数组来解决这个问题(虽然这是一个丑陋的解决方法)。
$references_to_data = array();
foreach ($data as &$reference) { $references_to_data[] = &$reference; }
unset($reference);
call_user_func_array(
array($stmt, "bind_param"),
array_merge(array(str_repeat("s", count($data))), $references_to_data));
答案 4 :(得分:2)
我发现了一个很好的mysqli类,它可以处理动态参数,并且易于使用
https://github.com/ajillion/PHP-MySQLi-Database-Class
您可以参考源代码动态构建查询的方式
https://github.com/ajillion/PHP-MySQLi-Database-Class/blob/master/MysqliDb.php
答案 5 :(得分:1)
我通过应用类似于PDO的系统解决了这个问题。 SQL占位符是以双点字符开头的字符串。例如:
:id, :name, or :last_name
然后,您可以在占位符字符串中直接指定数据类型,方法是在双点后立即添加规范字母,并在助记符变量之前添加下划线字符。例如:
:i_id (i=integer), :s_name or :s_last_name (s=string)
如果未添加任何类型字符,则该函数将通过分析保存数据的php变量来确定数据的类型。例如:
$id = 1 // interpreted as an integer
$name = "John" // interpreted as a string
该函数返回一个类型数组和一个值数组,您可以使用它们循环执行php函数mysqli_stmt_bind_param()。
$sql = 'SELECT * FROM table WHERE code = :code AND (type = :i_type OR color = ":s_color")';
$data = array(':code' => 1, ':i_type' => 12, ':s_color' => 'blue');
$pattern = '|(:[a-zA-Z0-9_\-]+)|';
if (preg_match_all($pattern, $sql, $matches)) {
$arr = $matches[1];
foreach ($arr as $word) {
if (strlen($word) > 2 && $word[2] == '_') {
$bindType[] = $word[1];
} else {
switch (gettype($data[$word])) {
case 'NULL':
case 'string':
$bindType[] = 's';
break;
case 'boolean':
case 'integer':
$bindType[] = 'i';
break;
case 'double':
$bindType[] = 'd';
break;
case 'blob':
$bindType[] = 'b';
break;
default:
$bindType[] = 's';
break;
}
}
$bindValue[] = $data[$word];
}
$sql = preg_replace($pattern, '?', $sql);
}
echo $sql.'<br>';
print_r($bindType);
echo '<br>';
print_r($bindValue);
答案 6 :(得分:-2)
我通常使用 mysqli
准备好的语句方法,并且在我根据函数中包含的参数动态构建查询时经常遇到这个问题(正如您所描述的)。这是我的方法:
function get_records($status = "1,2,3,4", $user_id = false) {
global $database;
// FIRST I CREATE EMPTY ARRAYS TO STORE THE BIND PARAM TYPES AND VALUES AS I BUILD MY QUERY
$type_arr = array();
$value_arr = array();
// THEN I START BUILDING THE QUERY
$query = "SELECT id, user_id, url, dr FROM sources";
// THE FIRST PART IS STATIC (IT'S ALWAYS IN THE QUERY)
$query .= " WHERE status IN (?)";
// SO I ADD THE BIND TYPE "s" (string) AND ADD TO THE TYPE ARRAY
$type_arr[] = "s";
// AND I ADD THE BIND VALUE $status AND ADD TO THE VALUE ARRAY
$value_arr[] = $status;
// THE NEXT PART OF THE QUERY IS DYNAMIC IF THE USER IS SENT IN OR NOT
if ($user_id) {
$query .= " AND user_id = ?";
// AGAIN I ADD THE BIND TYPE AND VALUE TO THE CORRESPONDING ARRAYS
$type_arr[] = "i";
$value_arr[] = $user_id;
}
// THEN I PREPARE THE STATEMENT
$stmt = mysqli_prepare($database, $query);
// THEN I USE A SEPARATE FUNCTION TO BUILD THE BIND PARAMS (SEE BELOW)
$params = build_bind_params($type_arr, $value_arr);
// PROPERLY SETUP THE PARAMS FOR BINDING WITH CALL_USER_FUNC_ARRAY
$tmp = array();
foreach ($params as $key => $value) $tmp[$key] = &$params[$key];
// PROPERLY BIND ARRAY TO THE STATEMENT
call_user_func_array(array($stmt , 'bind_param') , $tmp);
// FINALLY EXECUTE THE STATEMENT
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
mysqli_stmt_close($stmt);
return $result;
}
这里是 build_bind_params
函数:
// I PASS IN THE TYPES AND VALUES ARRAY FROM THE QUERY ABOVE
function build_bind_params($types, $values) {
// THEN I CREATE AN EMPTY ARRAY TO STORE THE FINAL OUTPUT
$bind_array = array();
// THEN I CREATE A TEMPORARY EMPTY ARRAY TO GROUP THE TYPES; THIS IS NECESSARY BECAUSE THE FINAL ARRAY ALL THE TYPES MUST BE A STRING WITH NO SPACES IN THE FIRST KEY OF THE ARRAY
$i = array();
foreach ($types as $type) {
$i[] = $type;
}
// SO I IMPLODE THE TYPES ARRAY TO REMOVE COMMAS AND ADD IT AS KEY[0] IN THE BIND ARRAY
$bind_array[] = implode('', $i);
// FINALLY I LOOP THROUGH THE VALUES AND ADD THOSE AS SUBSEQUENT KEYS IN THE BIND ARRAY
foreach($values as $value) {
$bind_array[] = $value;
}
return $bind_array;
}
此 build_bind_params
函数的输出如下所示:
Array ( [0] => isiisi [1] => 1 [2] => 4 [3] => 5 [4] => 6 [5] => 7 [6] => 8 )
您可以看到 [0]
键是所有没有空格和逗号的绑定类型。其余的键代表相应的值。请注意,上面的输出是一个示例,说明如果我有 6 个绑定值,所有绑定值都具有不同的绑定类型。
不确定这是否是一种明智的做法,但它确实有效,而且在我的用例中没有性能问题。