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

Prepared Statement when not knowing how many parameters

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

file.php?name=John&age=20

然而,可能会使用更多的参数。工作、地址、phone 号码等。这是我容易与准备好的陈述混淆的地方。

例如:

$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 因为我不知道有多少参数可能可用。

我将如何以合乎逻辑的方式处理这个问题?

好吧,这个过程将与您构建 $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;
}

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

你需要的是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);
}