简体   繁体   English

如何在PHP中动态绑定mysqli bind_param arguments?

[英]How to bind mysqli bind_param arguments dynamically in PHP?

I have been learning to use prepared and bound statements for my sql queries, and I have come out with this so far, it works okay but it is not dynamic at all when comes to multiple parameters or when there no parameter needed,我一直在学习为我的 sql 查询使用准备好的和绑定的语句,到目前为止我已经得出了这个,它工作正常但是当涉及多个参数或不需要参数时它根本不是动态的,

public function get_result($sql,$parameter)
{
    # create a prepared statement
    $stmt = $this->mysqli->prepare($sql);

    # bind parameters for markers
    # but this is not dynamic enough...
    $stmt->bind_param("s", $parameter);

    # execute query 
    $stmt->execute();
        
    # these lines of code below return one dimentional array, similar to mysqli::fetch_assoc()
    $meta = $stmt->result_metadata(); 
        
    while ($field = $meta->fetch_field()) { 
        $var = $field->name; 
        $$var = null; 
        $parameters[$field->name] = &$$var; 
    }
        
    call_user_func_array(array($stmt, 'bind_result'), $parameters); 
             
    while($stmt->fetch()) 
    { 
        return $parameters;
        //print_r($parameters);      
    }
        
        
    # close statement
    $stmt->close();
}

This is how I call the object classes,这就是我调用 object 类的方式,

$mysqli = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);

Sometimes I don't need to pass in any parameters,有时候不需要传入任何参数,

$sql = "
SELECT *
FROM root_contacts_cfm
";

print_r($output->get_result($sql));

Sometimes I need only one parameters,有时我只需要一个参数,

$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
ORDER BY cnt_id DESC
";

print_r($output->get_result($sql,'1'));

Sometimes I need only more than one parameters,有时我只需要一个以上的参数,

$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
AND root_contacts_cfm.cnt_firstname = ?
ORDER BY cnt_id DESC
";

print_r($output->get_result($sql,'1','Tk'));

So, I believe that this line is not dynamic enough for the dynamic tasks above,所以,我认为这条线对于上面的动态任务来说不够动态,

$stmt->bind_param("s", $parameter);

To build a bind_param dynamically, I have found this on other posts online.要动态构建 bind_param,我在其他在线帖子中找到了这个。

call_user_func_array(array(&$stmt, 'bind_params'), $array_of_params);

And I tried to modify some code from php.net but I am getting nowhere,我试图从php.net修改一些代码,但我一无所获,

if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+ 
{ 
    $refs = array(); 
    foreach($arr as $key => $value) 
        $array_of_param[$key] = &$arr[$key]; 
    call_user_func_array(array(&$stmt, 'bind_params'), $array_of_params); 
}

Why?为什么? Any ideas how I can make it work?有什么想法可以让它发挥作用吗?

Or maybe there are better solutions?或者也许有更好的解决方案?

Using PHP 5.6 you can do this easy with help of unpacking operator<\/a> ( ...$var<\/code> ) and use get_result()<\/a> insted of bind_result()<\/a>使用 PHP 5.6,您可以在解包运算符<\/a>( ...$var<\/code> ) 的帮助下轻松完成此操作,并使用get_result()<\/a> insted of bind_result()<\/a>

public function get_result($sql,$types = null,$params = null) {
    $stmt = $this->mysqli->prepare($sql);
    $stmt->bind_param($types, ...$params);

    if(!$stmt->execute()) return false;
    return $stmt->get_result();

}

found the answer for mysqli:找到了mysqli的答案:

public function get_result($sql,$types = null,$params = null)
    {
        # create a prepared statement
        $stmt = $this->mysqli->prepare($sql);

        # bind parameters for markers
        # but this is not dynamic enough...
        //$stmt->bind_param("s", $parameter);

        if($types&&$params)
        {
            $bind_names[] = $types;
            for ($i=0; $i<count($params);$i++) 
            {
                $bind_name = 'bind' . $i;
                $$bind_name = $params[$i];
                $bind_names[] = &$$bind_name;
            }
            $return = call_user_func_array(array($stmt,'bind_param'),$bind_names);
        }

        # execute query 
        $stmt->execute();

        # these lines of code below return one dimentional array, similar to mysqli::fetch_assoc()
        $meta = $stmt->result_metadata(); 

        while ($field = $meta->fetch_field()) { 
            $var = $field->name; 
            $$var = null; 
            $parameters[$field->name] = &$$var; 
        }

        call_user_func_array(array($stmt, 'bind_result'), $parameters); 

        while($stmt->fetch()) 
        { 
            return $parameters;
            //print_r($parameters);      
        }


        # the commented lines below will return values but not arrays
        # bind result variables
        //$stmt->bind_result($id); 

        # fetch value
        //$stmt->fetch(); 

        # return the value
        //return $id; 

        # close statement
        $stmt->close();
    }

With PHP 5.6 or higher<\/strong> :使用PHP 5.6 或更高版本<\/strong>:

$stmt->bind_param(str_repeat("s", count($data)), ...$data);

I found a nice mysqli class, it can handle dynamic parameters, and easy to use 我找到了一个不错的mysqli类,它可以处理动态参数,并且易于使用

https://github.com/ajillion/PHP-MySQLi-Database-Class https://github.com/ajillion/PHP-MySQLi-Database-Class

You could refer the source code how it dynamic build the query 您可以参考源代码如何动态构建查询

https://github.com/ajillion/PHP-MySQLi-Database-Class/blob/master/MysqliDb.php https://github.com/ajillion/PHP-MySQLi-Database-Class/blob/master/MysqliDb.php

Since PHP8.1 gave a facelift to MySQLi's execute() method , life got much easier for this type of task.自从PHP8.1 对 MySQLi 的execute()方法进行了改头换面,这类任务的生活变得更加轻松。

Now you no longer need to fuss and fumble with manually binding values to placeholders.现在您不再需要为手动将值绑定到占位符而大惊小怪。 Just pass in an indexed array of values and feed that data directly into execute() .只需传入一个索引值数组并将该数据直接提供给execute()

Code: ( PHPize.online Demo )代码:( PHPize.online 演示

class Example
{
    private $mysqli;
    
    public function __construct($mysqli)
    {
        $this->mysqli = $mysqli;
    }
    
    public function get(string $sql, array $data): object
    {
        $stmt = $this->mysqli->prepare($sql);
        $stmt->execute($data);
        return $stmt->get_result();
    }
}

$example = new Example($mysqli);

foreach ($example->get('SELECT * FROM example', []) as $row) {
    echo "<div>{$row['name']}</div>\n";
}

echo "\n---\n";
foreach ($example->get('SELECT * FROM example WHERE name = ?', ['Ned']) as $row) {
    echo "<div>{$row['name']}</div>\n";
}

echo "\n---\n";
foreach ($example->get('SELECT * FROM example WHERE name = ? OR flag = ?', ['Bill', 'foo']) as $row) {
    echo "<div>{$row['name']}</div>\n";
}

We have @Dharman to thank for this feature .我们要感谢@Dharman提供此功能

I solved it by applying a system similar to that of the PDO.我通过应用类似于 PDO 的系统解决了这个问题。 The SQL placeholders are strings that start with the double-point character. SQL 占位符是以双点字符开头的字符串。 Ex .:前任 。:

:id, :name, or :last_name

I generally use the mysqli<\/code> prepared statements method and frequently have this issue when I'm dynamically building the query based on the arguments included in a function (just as you described).我通常使用mysqli<\/code>准备好的语句方法,并且当我根据函数中包含的参数动态构建查询时经常遇到这个问题(正如您所描述的)。 Here is my approach:这是我的方法:

function get_records($status = "1,2,3,4", $user_id = false) {
    global $database;

    // FIRST I CREATE EMPTY ARRAYS TO STORE THE BIND PARAM TYPES AND VALUES AS I BUILD MY QUERY
    $type_arr = array();
    $value_arr = array();

    // THEN I START BUILDING THE QUERY
    $query = "SELECT id, user_id, url, dr FROM sources";

    // THE FIRST PART IS STATIC (IT'S ALWAYS IN THE QUERY)
    $query .= " WHERE status IN (?)";

    // SO I ADD THE BIND TYPE "s" (string) AND ADD TO THE TYPE ARRAY
    $type_arr[] = "s";

    // AND I ADD THE BIND VALUE $status AND ADD TO THE VALUE ARRAY
    $value_arr[] = $status;

    // THE NEXT PART OF THE QUERY IS DYNAMIC IF THE USER IS SENT IN OR NOT
    if ($user_id) {
        $query .= " AND user_id = ?";

    // AGAIN I ADD THE BIND TYPE AND VALUE TO THE CORRESPONDING ARRAYS
        $type_arr[] = "i";
        $value_arr[] = $user_id;
    }

    // THEN I PREPARE THE STATEMENT
    $stmt = mysqli_prepare($database, $query);

    // THEN I USE A SEPARATE FUNCTION TO BUILD THE BIND PARAMS (SEE BELOW)
    $params = build_bind_params($type_arr, $value_arr);
    
    // PROPERLY SETUP THE PARAMS FOR BINDING WITH CALL_USER_FUNC_ARRAY
    $tmp = array();
    foreach ($params as $key => $value) $tmp[$key] = &$params[$key];

    // PROPERLY BIND ARRAY TO THE STATEMENT
    call_user_func_array(array($stmt , 'bind_param') , $tmp);

    // FINALLY EXECUTE THE STATEMENT        
    mysqli_stmt_execute($stmt);
    $result = mysqli_stmt_get_result($stmt);
    mysqli_stmt_close($stmt);
    return $result;
}

An improvement to answer by @rray @rray 回答的改进

  function query($sql, $types = null, $params = null)
{
    $this->stmt = $this->conn->prepare($sql);
    if ($types && $params) {
        $this->stmt->bind_param($types, ...$params);
    }

    if (!$this->stmt->execute())
        return false;
    return $this->stmt->get_result();
}

This improvement only calls the bind function if the parameter and values for binding are set, on php the previous version by rray which gave an error incase you called the function with only an sql statement which is not ideal.如果设置了绑定的参数和值,此改进仅调用绑定 function,在 php 上,rray 的先前版本给出了错误,因为您仅使用不理想的 sql 语句调用了 function。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM