不知道有多少参数时的预准备语句

时间:2020-01-22 14:24:52

标签: php mysqli prepared-statement

我正在尝试将一年前制作的复杂php文件转换为准备好的语句。参数将像这样传递:

file.php?name=John&age=20

但是,可能还有更多可能使用的参数。工作,地址,电话号码等。这是我容易与准备好的陈述混淆的地方。

例如:

$query = "SELECT name, id, age, address, phone_number from person_db WHERE 1=1 ";

if (isset($_REQUEST['name'])) {
   $query  .= " and name = ?";
}

if (isset($_REQUEST['age'])) {
   $query  .= " and age = ?";
}

if (isset($_REQUEST['address'])) {
   $query  .= " and address = ?";
}

$stmt = $db->prepare($query);
$stmt->bind_param('sis', $_REQUEST['name'], $_REQUEST['age'], $_REQUEST['address']);
$stmt->execute();

这里的问题是bind_param,因为我不知道可能有多少个参数可用。

我将如何以合理的方式解决这个问题?

3 个答案:

答案 0 :(得分:2)

一个很好的问题。解决方案非常简单。

您需要的称为argument unpacking operator。它将允许您对bind_param使用任意长度的数组。您需要做的就是将可接受的参数收集到数组中,然后动态创建类型字符串,最后使用上述运算符:

$query = "SELECT name, id, age, address, phone_number from person_db WHERE 1=1 ";
$params = array();

if (isset($_REQUEST['name'])) {
   $query  .= " and name = ?";
   $params[] = $_REQUEST['name'];
}

if (isset($_REQUEST['age'])) {
   $query  .= " and age = ?";
   $params[] = $_REQUEST['age'];
}

if (isset($_REQUEST['address'])) {
   $query  .= " and address = ?";
   $params[] = $_REQUEST['address'];
}

if ($params)
    $stmt = $db->prepare($query);
    $types = str_repeat("s", count($params));
    $stmt->bind_param($types, ...$params);
    $stmt->execute();
    $result = $stmt->get_result();
} else {
    $result = $db->query($query);
}

答案 1 :(得分:1)

对于选择查询,我认为您不需要获取所有参数,获取ID或姓名或职位名称就足以列出,我的意思是单个参数可能是两个。

但是插入那么多的值会很痛苦,所以,我将使用如下函数:

此函数对数组和绑定中的值进行计数。

function bindQueryParams($sql, $param_type, $param_value_array) {
    $param_value_reference[] = & $param_type;
    for($i=0; $i<count($param_value_array); $i++) {
        $param_value_reference[] = & $param_value_array[$i];
    }
    call_user_func_array(array(
        $sql,
        'bind_param'
    ), $param_value_reference);
}

此函数插入​​:

function insert($query, $param_type, $param_value_array) {
    $sql = $this->conn->prepare($query);
    $this->bindQueryParams($sql, $param_type, $param_value_array);
    $sql->execute();
}

Usaqe:

 $query = "INSERT INTO tbl_token_auth (username, password_hash, selector_hash, expiry_date) values (?, ?, ?,?)";
        $result = insert($query, 'ssss', array($username, $random_password_hash, $random_selector_hash, $expiry_date));

更新:我的函数可以像这样用于选择:

function runQuery($query, $param_type, $param_value_array) {

    $sql = $this->conn->prepare($query);
  //This function calling bindQueryParams function and auto creating binds
    $this->bindQueryParams($sql, $param_type, $param_value_array);
    $sql->execute();
    $result = $sql->get_result();

    if ($result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {
            $results[] = $row;
        }
    }

    if(!empty($results)) {
        return $results;
    }
}

Usaqe:

 $query = "Select * from your table where name = ?";
  //You just need to variables in array 
 $result = runQuery($query, 's', array($your_variables));

答案 2 :(得分:-1)

好吧,该过程将与构建$query变量的过程非常相似-即一次将一个变量添加到参数列表中。

考虑bind_param需要做两件事:

首先是一个简单字符串形式的数据类型列表。因此,您只需为每个参数添加一个字符串变量,然后将其最后传递给bind_param。

第二个是参数列表。这比较棘手,因为您不能仅向bind_param提供数组。但是,您可以创建自己的数组,然后使用call_user_func_array将其转换为平面列表。

这是我之前写的(效果不错)。请注意,它会尝试检测参数类型并创建合适的字符串,但是您可以根据需要在if语句中手动构建字符串:

$query = "SELECT name, id, age, address, phone_number from person_db WHERE 1=1 ";
$params = array();

if (isset($_REQUEST['name'])) {
   $query  .= " and name = ?";
   $params[] = $_REQUEST['name'];
}

if (isset($_REQUEST['age'])) {
   $query  .= " and age = ?";
   $params[] = $_REQUEST['age'];
}

if (isset($_REQUEST['address'])) {
   $query  .= " and address = ?";
   $params[] = $_REQUEST['address'];
}

$stmt = $db->prepare($query);

if (!is_null($params))
{
  $paramTypes = "";

  foreach ($params as $param)
  {
    $paramTypes .= mysqliContentType($param);
  }

  $bindParamArgs = array_merge(array($paramTypes), $params);

  //call bind_param using an unpredictable number of parameters
  call_user_func_array(array(&$stmt, 'bind_param'), getRefValues($bindParamArgs));
}

$stmt->execute();

function mysqliContentType($value)
{
    if(is_string($value)) $type = 's';
    elseif(is_float($value)) $type = 'd';
    elseif(is_int($value)) $type = 'i';
    elseif($value == null) $type = 's'; //for nulls, just have to assume a string. hopefully this doesn't mess anything up.
    else throw new Exception("type of '".$value."' is not string, int or float");
    return $type;
}

function getRefValues($arr)
{
  $refs = array();

  foreach($arr as $key => $value)
  {
    $refs[$key] = &$arr[$key];
  }

  return $refs;
}