我正在寻找处理 mysqli bind_param
方法的可变长度参数的最佳方法/模式/习语。
我在 PHP 中创建了一个类似于 mysqli_stmt::bind_param
的可变参数函数。因此,第一个必需参数需要一个字符串,其他参数是可选的,但第一个参数的字符串长度必须等于可选参数的数量。
函数定义:
function bind_param_test($types, ...$args){
$typesArr = str_split($types);
foreach($args as $i => $n) {
switch ($typesArr[$i]) {
case 'd':
echo "Decimal: " . $n . '<br>';
break;
case 's':
echo "String: " . $n . '<br>';
break;
default:
break;
}
}
}
现在我正在尝试使用它。
// ------------- CASE 1 ---------------------------
// In this case I have 5 arguments. So I can do this:
$a = 10;
$b = 20;
$c = 'hello';
$d = 'cheese';
$e = 500;
if($a && $b && $c && $d && $e){
bind_param_test('ddssd', $a, $b, $c, $d, $e);
}
/* OUTPUT:
Decimal: 10
Decimal: 20
String: hello
String: cheese
Decimal: 500
*/
echo '<hr>';
请参阅下面的案例 2 和 3。
// ------------- CASE 2 ---------------------------
// I'm using 4 variables
$e = null;
if($a && $b && $c && $d && !$e){
bind_param_test('ddss', $a, $b, $c, $d);
}
/* OUTPUT:
Decimal: 10
Decimal: 20
String: hello
String: cheese
*/
echo '<hr>';
// ------------- CASE 3 ---------------------------
// 3 variables
$d = null;
$e = null;
if($a && $b && $c && !$d && !$e){
bind_param_test('dds', $a, $b, $c);
}
/* OUTPUT:
Decimal: 10
Decimal: 20
String: hello */
echo '<hr>';
案例 2 和 3 是硬编码的,因此不是 DRY。我想让它变干,以防万一 4。请参阅案例 4。
案例 4:通过数组处理 mysqli bind_param的模式
// ------------- CASE 4 ---------------------------
// I want to have a more generic approach.
$argumentList = array(
'a' => 10,
'b' => 20,
'c' => null,
'd' => null,
'e' => null,
);
$argumentList = array_filter($argumentList);
$actionStr = '';
foreach($argumentList as $k => $v){
if(is_numeric($v)){
$actionStr .= 'd';
}
if(is_string($v)){
$actionStr .= 's';
}
}
$varList = array_values($argumentList);
$param_arr = array();
$param_arr[] = $actionStr;
$param_arr = (array_merge($param_arr, $varList));
call_user_func_array('bind_param_test', $param_arr);
/* OUTPUT:
Decimal: 10
Decimal: 20 */
问题:你能找到比案例 4 更好的方法吗?我问是因为我认为案例4很神秘。恐怕会让其他开发人员感到惊讶。
回答我自己的问题。我只是想改善这个案子。这是我的进步。
$argumentList = array(
'a' => 10,
'b' => 20,
'c' => null,
'd' => null,
'e' => null,
);
$argumentList = array_filter($argumentList);
$mysqli_param_types = '';
foreach($argumentList as $k => $v){
if(is_numeric($v)){
$actionStr .= 'd';
}
if(is_string($v)){
$actionStr .= 's';
}
}
// instead of array_values() and array_merge()
// its more clear to iterate over argumentList (even if its twice)
$mysqli_reference_params[] =& $actionStr;
foreach($argumentList as &$var) {
$mysqli_reference_params[] =& $var;
}
call_user_func_array('bind_param_test', $mysqli_reference_params);
/* OUTPUT:
Decimal: 10
Decimal: 20 */
好吧,为什么这很重要?这很重要,因为我希望有一个与bind_param
和可变长度参数列表配合良好的模式。
成语:mysqli 中的可变长度预准备语句
结合预准备语句处理 mysqli 中可变长度参数列表的习语。在这里看到一个真正的代码习语。
$sql = " SELECT p.`productID`,
p.`productCode`,
p.`productName`,
p.`productDescription`,
p.`productPrice`,
p.`productRating`,
p.`productDateTime`
FROM `products` as p
WHERE `p`.`productID` IS NOT NULL ";
if($searchCode){
$sql .= "AND p.`productCode` = ? ";
}
if($searchName){
$sql .= "AND p.`productName` = ? ";
}
if($searchDescription) {
$sql .= "AND p.`productDescription` = ? ";
}
if($searchPrice) {
$sql .= "AND p.`productPrice` = ? ";
}
if($searchRating) {
$sql .= "AND p.`productRating` = ? ";
}
if($searchDateTime) {
$sql .= "AND p.`productDateTime` = ? ";
}
// Create mysqli_stmt
$statement = $mysqli->prepare($sql);
if ($statement instanceof mysqli_stmt === false) {
return null;
}
// Handle search variables through bind_param()
$bindParamVarList = array(
'productCode' => $searchCode,
'productName' => $searchName,
'productDescription' => $searchDescription,
'productPrice' => $searchPrice,
'productRating' => $searchRating,
'productDateTime' => $searchDateTime,
);
$bindParamVarList = array_filter($bindParamVarList);
if($bindParamVarList){
$types = '';
foreach($bindParamVarList as &$v){
if(is_numeric($v)){
$types .= 'd';
$v = (float)$v;
continue;
}
if(is_string($v)){
$types .= 's';
continue;
}
}
// call_user_func_array needs references and not values
$mysqli_reference_params = array();
$mysqli_reference_params[] =& $types;
foreach($bindParamVarList as &$bindParamVar) {
$mysqli_reference_params[] =& $bindParamVar;
}
call_user_func_array(array($statement, 'bind_param'), $mysqli_reference_params);
}
$statement->execute();
$statement->store_result();
$amount = $statement->num_rows;
$statement->bind_result($productID,
$productCode,
$productName,
$productDescription,
$productPrice,
$productRating,
$productDateTime
);
$products = array();
$productsSet = array();
while($statement->fetch()){
$product = array();
$product['productID'] = $productID;
$product['productCode'] = $productCode;
$product['productName'] = $productName;
$product['productDescription'] = $productDescription;
$product['productPrice'] = $productPrice;
$product['productRating'] = $productRating;
$product['productDateTime'] = $productDateTime;
$products[] = $product;
}
// JavaScript is only able to work with indexed Array's
$productsSet[] = $products;
$productsSet[] = $amount;
您可以减少处理所需所有类型的数组,并对不允许的类型抛出异常。此外,更现代的PHP支持数组销毁...$array
和[$var1, $var2] = $array
。
当我通过$actionStr = 'xyz'
时,您还应该抛出异常。
这个例子是根据您的顺序is_numeric
,is_string
将'123'
和123
处理为数字:
// ------------- CASE 4 ---------------------------
$argumentList = array(
'a' => 'ten',
'b' => 20,
'c' => '0',
'd' => null,
'e' => 0,
// 'e' => false, // throws Exception
// 'f' => [1,2,3], // throws Exception
);
[$argumentList, $actionStr] = array_reduce
(
$argumentList,
function(array $aggr, $v) : array
{
if(null === $v)
return $aggr;
if(is_numeric($v))
{
$aggr[0][] = $v;
$aggr[1] .= 'd';
return $aggr;
}
if(is_string($v))
{
$aggr[0][] = $v;
$aggr[1] .= 's';
return $aggr;
}
throw new InvalidArgumentException('Type ' . gettype($v) . ' not allowed.');
},
[[], '']
);
doSomething($actionStr, ...$argumentList);
如果您希望'123'
作为字符串而不是数字处理,请先检查is_string
。