【问题标题】:MySQLI binding params using call_user_func_arrayMySQLI 使用 call_user_func_array 绑定参数
【发布时间】:2010-12-27 04:48:13
【问题描述】:

请看下面我的代码。 我正在尝试将一组参数绑定到我准备好的语句。 我一直在网上四处寻找,可以看到我必须使用 call_user_func_array 但无法让它工作。我得到的错误是: “第一个参数应该是一个有效的回调,'Array'被给出” 我可能是错的,但我假设第一个参数可以是一个数组,也许这个错误消息具有误导性。我认为问题在于我的阵列在某种程度上有问题。 谁能看到我做错了什么?谢谢。

$type = array("s", "s");
$param = array("string1","anotherstring");

$stmt = $SQLConnection->prepare("INSERT INTO mytable (comp, addl) VALUES (?,?)");

$params = array_merge($type, $param);

call_user_func_array(array(&$stmt, 'bind_param'), $params);
$SQLConnection->execute();

【问题讨论】:

  • 我看到第一行的错误应该是:$type = array("ss");
  • 正是这样的案例让我更喜欢 PDO 而不是 mysqli。

标签: php mysqli bind


【解决方案1】:

一定是这样的:

//connect
$mysqli = new mysqli($host, $user, $password, $db_name);

//prepare
$stmt = $mysqli->prepare("SELECT * FROM the_table WHERE field1= ? AND Field2= ?");

//Binding parameters. Types: s = string, i = integer, d = double,  b = blob
$params= array("ss","string_1","string_2");

//now we need to add references
$tmp = array();
foreach($params as $key => $value) $tmp[$key] = &$params[$key];
// now us the new array
call_user_func_array(array($stmt, 'bind_param'), $tmp);

$stmt->execute();

/* Fetch result to array */
$res = $stmt->get_result();
while($row = $res->fetch_array(MYSQLI_ASSOC)) {
  $a_data[]=$row;
}
print_r($a_data);

$stmt->close();

【讨论】:

  • 终于在这个例子的帮助下解决了我的问题。问题是大多数示例似乎都对params array' but I need to generate this dynamically based on a post to my API. I had to build a function that takes in the values and builds the params array that I then use with the call_user_function_array 进行了硬编码。我已将我的解决方案放在我原来的问题上:how to bind comma separated... with NOT IN...
【解决方案2】:

PHP 5.6 开始,您不必再纠结call_user_func_array()了。

代替:

$stmt->bind_param($param_types, $my_params_array);

你可以直接使用splat operator,像这样:

$stmt->bind_param($param_types, ...$my_params_array); // exact code

【讨论】:

  • 第一种方法比较好。这个很烂,因为你必须维护两个数组
【解决方案3】:

我不知道你为什么必须使用call_user_func_array,但那是另一回事了。

在我看来唯一可能出错的是您使用了对对象的引用。假设您使用的是 PHP 5.*,则没有必要:

call_user_func_array(array($stmt, 'bind_param'), $params);

【讨论】:

  • 感谢 Franz,我使用 call_user_func_array 是因为要绑定的参数数量不同。我正在使用 PHP5,但删除参考 staill 会产生相同的结果。
  • 那么这一定意味着你的对象有问题。请参阅此 PHP“错误”报告:bugs.php.net/bug.php?id=46229var_dump($stmt) 在那个地方给你什么?
  • 是的,我只是得到 bool(true) 而不是声明。不知道为什么要去看看。我想它给了我一个真实的说法,声明准备是成功的。但我在几个例子中看到人们在做我上面所做的事情。
  • 你是对的,这是一个对象问题。当我不需要创建 $SQLConnection 时,我使用了 stmt_init。我仍然期待一个对象,但删除它就可以了。谢谢。
  • 我注意到您的值类型是数组中的单个项目...我假设您使用的是 mysqli_stmt::bind_param(),它将其第一个参数作为字符串,所以 $type = array("s", "s") 需要 $type = "ss" 因为字符串由每个字符解析。请参阅手册。另一方面,我相信 bind_param 和 bind_result 仍然都需要通过引用传递值列表,但是 call_user_func_array() 不能再通过 ref 传递,因为那将是调用时 pass_by_reference 致命错误,所以这实际上破坏了动态引用传递到mysqli中的方法。
【解决方案4】:

如果你遇到错误,你应该试试这个:

call_user_func_array(array($stmt, 'bind_param'), refValues($params));

function refValues($arr){
    if (strnatcmp(phpversion(),'5.3') >= 0) {
        $refs = array();
        foreach($arr as $key => $value)
            $refs[$key] = &$arr[$key];
        return $refs;
    }
    return $arr;
}

【讨论】:

    【解决方案5】:

    无法回答我自己的问题,因为它被标记为欺骗:here。但我认为我的最终解决方案(使用此问题中的答案)适用于我的用例,可能对某人有所帮助。

    我的目标是获取一组已发布的 ID,并在 NOT IN MYSQL 语句中使用它们。假设发布了 5 个 ID 的数组。

    1. 计算发布 ID 的数量以构建 NOT IN 语句的 ? 占位符。使用$params_count = substr(str_repeat(',?', count($array_of_ids)), 1); 会得到结果:(?,?,?,?,?) 用于 SQL 语句。

    2. 制作带有 ID 并输入 is 等的函数。对我来说,它们都是 i,所以我的函数更简单。返回类似于 $params= array("iiiii",1,2,3,4,5) 的数组,其中第一个值是 i 的集合,随后的值是 ID,具体取决于传递给函数的总 ID。

      function build_bind_params($values, $bind_type) {
          $s = substr(str_repeat($bind_type, count($values)), 0);
          $bind_array = array();
          $bind_array[] = $s;
          foreach($values as $value) {
              $bind_array[] = $value;
          }
          return $bind_array; 
      }
      

    $params = build_bind_params($array_of_ids, "i");

    1. 然后使用foreach ($params as $key => $value) $tmp[$key] = &$params[$key]; 将新创建的$params 格式化为适合绑定。

    2. 然后使用call_user_func_array(array($stmt , 'bind_param') , $tmp);正确绑定数组。

    3. 然后执行$stmt

    【讨论】:

    • substr([string], 0) 的目的是什么?您正在尝试将子字符串从零位置隔离到字符串的末尾;所以子字符串=完整的字符串。请编辑以免其他人复制粘贴。
    • @mickmackusa 比较点 1。与有问题的行相比,我认为 0 应该是 1
    【解决方案6】:

    在将类型和值添加到 call_user_func_array() 之前,如果不将类型与值集成在一起,以上大部分都不是解决方案。这个解决方案对我有用:

    /* create a database connection */
    $db = new mysqli($host, $user, $password, $db_name);
    
    /* setup the sql, values, and types */
    $sql="SELECT * FROM languages 
             WHERE language_code = ?
               AND charset = ?
             ORDER BY native_name";
    $values = array($langCode, $charset);
    $types = "ss";   
    
    /* pass those variables to the execute() function defined below */
    if ($rows = execute($sql, $values, $types))
    {
       return $rows[0];
    }
    
    function execute($sql, $values='', $types='')
    {
       /* prepare the sql before binding values and types */
       $stmt = $db->prepare($sql);
    
       /*combine the values and types into $inputArray */
       $inputArray[] = &$types;
       $j = count($values);
       for($i=0;$i<$j;$i++){
         $inputArray[] = &$values[$i];
       }
       /* add the combined values and types to call_user_func_array() for binding */
       call_user_func_array(array($stmt, 'bind_param'), $inputArray);
       $result = $stmt->execute();
       return $result;
    }
    

    以下是此示例所基于的完整描述的参考: http://big.info/2015/08/php-use-call_user_func_array-for-variable-number-of-parameters-arrays-in-prepared-statements.html

    【讨论】:

      【解决方案7】:

      为什么要调用 call_user_func_array(array($statement, 'bind_param'), $bind_arguments)?因为 $bind_arguments 是一个数组。无论您有多少参数,您都可以拥有一个将语句绑定到其查询参数的函数。

      优秀代码示例...

          <?php
                  # link
              $dblink = new mysqli('HOSTNAME','USERNAME','PASSWORD','DATABASENAME');
      
                  # example data
              $statement = $dblink->prepare("SELECT * from Person WHERE FirstName = ? AND MiddleName = ? AND LastName = ? and Age = ?");
              $recordvalues = ['John', 'H.', 'Smith', 25];
              $sqlbindstring = "sssi";    # String, String, String, Integer example
      
                  # make the references
              $bind_arguments = [];
              $bind_arguments[] = $sqlbindstring;
              foreach ($recordvalues as $recordkey => $recordvalue)
              {
                  $bind_arguments[] = & $recordvalues[$recordkey];    # bind to array ref, not to the temporary $recordvalue
              }
      
                  # query the db
              call_user_func_array(array($statement, 'bind_param'), $bind_arguments);     # bind arguments
              $statement->execute();  # run statement
              $result = $statement->get_result(); # get results
      
                  # get the results
              if($result) {
                  while ($row = $result->fetch_assoc()) {
                      print("\n\nMy row is...");
                      print_r($row);
                  }
              }
          ?>
      

      错误代码示例...

          <?php
      
                  # Same setup as above..
      
              $statement->prepare("SELECT * from Person WHERE FirstName = ? AND MiddleName = ? AND LastName = ? and Age = ?");
              $statement->bind('John', 'H.", 'Smith', 25);
      
          ?>
      

      在第一个示例中:您可以将尽可能多的或尽可能少的传递给要完成的绑定,以便在整个应用程序中仅在一行中调用 bind()。这可以很好地扩展。

      在第二个示例中:您必须为数据库中每个可能的记录的每个可能的插入组编写一个 bind() 语句。这扩展性很差。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-01
        • 2010-12-03
        • 2012-08-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多