Лучшие практики для списков аргументов переменной длины в mysqli bind_param - PullRequest
0 голосов
/ 23 января 2019

Я ищу лучший подход / шаблон / идиома для обработки аргумента переменной длины для метода 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 жестко запрограммированы и поэтому не СУХИЕ. Я хочу сделать СУХОЙ в случае 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 таинственный. Боюсь, что это может удивить других разработчиков.

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Я хотел бы ответить на свой вопрос.Я просто хотел улучшить дело.Вот мое улучшение.

$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;
0 голосов
/ 23 января 2019

Вы можете уменьшить массив, обрабатывающий все типы, которые вам нужны, и создать исключение для запрещенных типов.Более современный 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);

Сначала проверьте is_string, если хотите, чтобы '123' обрабатывался как строка вместо числа.

...