【问题标题】:Use one bind_param() with variable number of input vars使用一个具有可变数量输入变量的 bind_param()
【发布时间】:2009-04-27 13:24:47
【问题描述】:

我尝试像这样使用变量绑定:

$stmt = $mysqli->prepare("UPDATE mytable SET myvar1=?, myvar2=... WHERE id = ?")) {
$stmt->bind_param("ss...", $_POST['myvar1'], $_POST['myvar2']...);

但有些 $_POST['...'] 可能是空的,所以我不想在数据库中更新它们。

考虑空 $_POST['...'] 的所有不同组合是不切实际的,虽然我可以根据需要构建字符串“UPDATE mytable SET...”,但 bind_param() 是不同的野兽。

我可以尝试将其调用构建为字符串并在其上使用 eval() 但感觉不对:(

【问题讨论】:

    标签: php mysqli


    【解决方案1】:

    这是我用可变数量的参数来执行 mysqli 准备语句的方法。这是我写的课程的一部分。对于你需要的东西来说,它可能是矫枉过正,但它应该为你指明正确的方向。

    public function __construct($con, $query){
        $this->con = $con;
        $this->query = $query;
        parent::__construct($con, $query);
        //We check for errors:
        if($this->con->error) throw new Exception($this->con->error);
    }
    
    protected static $allowed = array('d', 'i', 's', 'b'); //allowed types
    
    protected static function mysqliContentType($value) {
        if(is_string($value)) $type = 's';
        elseif(is_float($value)) $type = 'd';
        elseif(is_int($value)) $type = 'i';
        else throw new Exception("type of '$value' is not string, int or float");
        return $type;
    }
    
    //This function checks if a given string is an allowed mysqli content type for prepared statement (s, d, b, or i)
    protected static function mysqliAllowedContentType($s){
        return in_array($s, self::$allowed);
    }
    
    public function feed($params){
        //These should all be empty in case this gets used multiple times
        $this->paramArgs = array();
        $this->typestring = '';
        $this->params = $params;
        $this->paramArgs[0] = '';
        $i = 0;
        foreach($this->params as $value){
            //We check the type:
            if(is_array($value)){
                $temp = array_keys($value);
                $type = $temp[0];
                $this->params[$i] = $value[$type];
                if(!self::mysqliAllowedContentType($type)){
                    $type = self::mysqliContentType($value[$type]);
                }
            }
            else{
                $type = self::mysqliContentType($value);
            }
            $this->typestring .= $type;
            //We build the array of values we pass to the bind_params function
            //We add a refrence to the value of the array to the array we will pass to the call_user_func_array function. Thus say we have the following
            //$this->params array:
                //$this->params[0] = 'foo';
                //$this->params[1] = 4;
            //$this->paramArgs will become: 
                //$this->paramArgs[0] = 'si'; //Typestring
                //$this->paramArgs[1] = &$this->params[0];
                //$this->paramArgs[2] = &$this->params[1].
            //Thus using call_user_func_array will call $this->bind_param() (which is inherented from the mysqli_stmt class) like this:
                //$this->bind_param( 'si', &$this->params[0], &$this->params[1] );
            $this->paramArgs[] = &$this->params[$i];
            $i++;
        }
        unset($i);
        $this->paramArgs[0] = $this->typestring;
        return call_user_func_array(array(&$this, 'bind_param'), $this->paramArgs);
    }
    

    你可以这样使用它:

     $prep = new theClassAboveHere( $mysqli, $query );
     $prep->feed( array('string', 1, array('b', 'BLOB DATA') );
    

    该类应扩展 mysqli_stmt 类。

    我希望这可以帮助您朝着正确的方向前进。
    如果你不想,我也可以发布整个课程,它包括变量结果绑定。

    【讨论】:

      【解决方案2】:

      您可以使用call_user_func_array 函数调用带有可变编号或参数的bind_param 方法:

      $paramNames = array('myvar1', 'myvar2', /* ... */);
      $params = array();
      foreach ($paramNames as $name) {
          if (isset($_POST[$name]) && $_POST[$name] != '') {
              $params[$name] = $_POST[$name];
          }
      }
      if (count($params)) {
          $query = 'UPDATE mytable SET ';
          foreach ($params as $name => $val) {
              $query .= $name.'=?,';
          }
          $query = substr($query, 0, -1);
          $query .= 'WHERE id = ?';
          $stmt = $mysqli->prepare($query);
          $params = array_merge(array(str_repeat('s', count($params))), array_values($params));
          call_user_func_array(array(&$stmt, 'bind_param'), $params);
      }
      

      【讨论】:

      • 只是想提一下,在 PHP 5.3+ 中,上面的代码将不起作用,因为它需要数组值作为参考。见:php.net/manual/de/mysqli-stmt.bind-param.php
      • 然后改用bind_values
      • 在 MYSQLi 中哪里可以找到这个?我只从 PDO 知道。
      • @Alex2php 哦,是的,我的意思是 PDO 的 bindValue
      • @Alex2php 我遇到了同样的问题,但是通过将所有内容添加到 $params[] 作为参考来解决这个问题。例如$params[$name] =& $_POST[$name];
      【解决方案3】:

      使用数组构建语句会稍微清晰一些:

      $params = array();
      $fragments = array();
      foreach($_POST as $col => $val)
      {
        $fragments[] = "{$col} = ?";
        $params[] = $val;
      }
      
      $sql = sprintf("UPDATE sometable SET %s", implode(", ", $fragments));
      $stmt = $mysqli->prepare($sql);
      $stmt->bind_param($params);
      

      【讨论】:

      • 这样让用户指定要更新的字段是否安全?我只是在想,如果那是一个用户表,并且用户以某种方式发现有一列存储了他们的用户权限,那么他们只需提交具有正确键/值组合的表单帖子,就可以轻松提升其用户权限。
      • 在任何情况下都不能让用户指定字段。他至少应该有一个 $valid_fieldnames 数组并检查 (isset($valid_fieldnames[$col])) 作为 foreach 的主体。 (我更喜欢 Array('field' => 1, 'field2' => 1,...) 而不是 Array('field','field2',...) 因为 isset() 比 in_array() 快。)
      • 通过不检查列名或至少验证它们(例如只允许有限的字符等),您也允许 SQL 注入发生
      • 稍微容易被SQL注入,phpdelusions.net/pdo/sql_injection_example
      【解决方案4】:

      将它构建为一个字符串,但将您的值放入一个数组并将其传递给 bindd_param。 (并用 ? 代替 SQL 字符串中的值。

      $stmt = $mysqli->prepare("UPDATE mytable SET myvar1=?, myvar2=... WHERE id = ?")) { $stmt->bind_param("ss...", $_POST['myvar1'], $_POST['myvar2']...);

      例如:

      $args = array();
      $sql = "UPDATE sometable SET ";
      $sep = "";
      $paramtypes = "";
      foreach($_POST as $key => $val) {
        $sql .= $sep.$key." = '?'";
        $paramtypes .= "s"; // you'll need to map these based on name
        array_push($args, $val);
        $sep = ",";
      }
      $sql .= " WHERE id = ?";
      array_push($args, $id);
      array_insert($args, $paramtypes, 0);
      
      $stmt = $mysqli->prepare($sql);
      call_user_func_array(array(&$stmt, 'bindparams'), $array_of_params);
      $stmt->bind_param($args);
      

      【讨论】:

      • @altCongnito array_insert() 真的存在吗?
      • 你写的是$sql +=而不是$sql .=
      【解决方案5】:

      array_insert 不存在,我猜他指的是一些自制函数,但我不确定它到底是做什么的......在开头的某个地方将参数类型插入到数组中我猜想因为值0 已通过,但最终也可能是 ;)

      【讨论】:

      • 嗯……这看起来更像是评论而不是答案?
      猜你喜欢
      • 1970-01-01
      • 2020-03-23
      • 1970-01-01
      • 2016-06-15
      • 2021-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多