我正在尝试将一年前制作的复杂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
,因为我不知道有多少参数可能可用。
我将如何以合乎逻辑的方式做到这一点?
一个非常好的问题。解决方案非常简单。
你需要的称为参数解包运算符。它将允许您将任意长度的数组与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);
}
嗯,这个过程将与您构建$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;
}