Как динамически связать аргументы mysqli bind_param в PHP? - PullRequest
14 голосов
/ 24 февраля 2011

Я учился использовать подготовленные и связанные операторы для моих 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();
    }

Так я называю классы объектов,

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

Иногда мне не нужно передавать какие-либо параметры,

$sql = "
SELECT *
FROM root_contacts_cfm
";

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

Иногда янужны только один параметр,

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

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

Иногда мне нужно только более одного параметра,

$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'));

Итак, я считаю, что эта строка недостаточно динамична для вышеуказанных динамических задач,

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

Чтобы динамически создать bind_param, я нашел это в других постах в сети.

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

И я попытался изменить некоторый код из 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);

     }

Почему?Любые идеи, как я могу заставить его работать?

Или, может быть, есть лучшие решения?

Ответы [ 6 ]

16 голосов
/ 24 февраля 2011

нашел ответ для 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();
    }

затем:

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

$sql = "
SELECT *
FROM root_contacts_cfm
ORDER BY cnt_id DESC
";
print_r($output->get_result($sql));

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

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

$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, 'ss',array('1','Tk')));

mysqli настолько неубедителен, когда приходит к этомуЯ думаю, что я должен перейти на PDO!

6 голосов
/ 17 марта 2016

Используя PHP 5.6, вы можете легко это сделать с помощью оператора распаковки (...$var) и использования get_result () вместо bind_result ()

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();

}

Пример:

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


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

$res = $output->get_result($sql, 'ss',array('1','Tk'));
while($row = res->fetch_assoc()){
   echo $row['fieldName'] .'<br>';
}
5 голосов
/ 24 февраля 2011

Или, может быть, есть лучшие решения ??

Этот ответ не очень вам поможет, но вы должны серьезно подумать о переходе на PDO из mysqli.

Основная причина этого заключается в том, что PDO делает то, что вы пытаетесь сделать в MySQL со встроенными функциями.В дополнение к наличию ручной привязки параметров , метод execute может вместо этого принимать массив аргументов.

PDO легко расширять, и добавить удобные методы для извлечения «все и возврат» вместо танца «приготовь-выполнить» очень просто.

3 голосов
/ 29 апреля 2018

При PHP 5.6 или выше :

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

При PHP 5.5 или ниже вы можете (и I ожидали) ожидатьследующие для работы:

call_user_func_array(
    array($stmt, "bind_param"),
    array_merge(array(str_repeat("s", count($data))), $data));

... но mysqli_stmt::bind_param ожидает, что его параметры будут ссылками, тогда как это передает список значений.

Вы можете обойти это (хотя это уродливоВременное решение), сначала создав массив ссылок на исходный массив.

$references_to_data = array();
foreach ($data as &$reference) { $references_to_data[] = &$reference; }
unset($reference);
call_user_func_array(
    array($stmt, "bind_param"),
    array_merge(array(str_repeat("s", count($data))), $references_to_data));
2 голосов
/ 08 апреля 2013

Я нашел хороший класс mysqli, он может обрабатывать динамические параметры и прост в использовании

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

Вы можете ссылаться на исходный код, как он динамически строит запрос

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

1 голос
/ 22 октября 2018

Я решил это, применив систему, аналогичную системе PDO. Заполнители SQL - это строки, начинающиеся с символа двойной точки. Пример:

:id, :name, or :last_name

Затем вы можете указать тип данных непосредственно внутри строки заполнителя, добавив буквы спецификации сразу после двойной точки и добавив символ подчеркивания перед мнемонической переменной. Пример:

:i_id (i=integer), :s_name or :s_last_name (s=string)

Если символ типа не добавлен, функция определит тип данных, проанализировав переменную php, содержащую данные. Пример:

$id = 1 // interpreted as an integer
$name = "John" // interpreted as a string

Функция возвращает массив типов и массив значений, с помощью которых вы можете выполнять функцию php mysqli_stmt_bind_param () в цикле.

$sql = 'SELECT * FROM table WHERE code = :code AND (type = :i_type OR color = ":s_color")';
$data = array(':code' => 1, ':i_type' => 12, ':s_color' => 'blue');

$pattern = '|(:[a-zA-Z0-9_\-]+)|';
if (preg_match_all($pattern, $sql, $matches)) {
    $arr = $matches[1];
    foreach ($arr as $word) {
        if (strlen($word) > 2 && $word[2] == '_') {
            $bindType[] = $word[1];
        } else {
            switch (gettype($data[$word])) {
                case 'NULL':
                case 'string':
                    $bindType[] = 's';
                    break;
                case 'boolean':
                case 'integer':
                    $bindType[] = 'i';
                    break;
                case 'double':
                    $bindType[] = 'd';
                    break;
                case 'blob':
                    $bindType[] = 'b';
                    break;
                default:
                    $bindType[] = 's';
                    break;
            }
        }    
        $bindValue[] = $data[$word];
    }    
    $sql = preg_replace($pattern, '?', $sql);
}

echo $sql.'<br>';
print_r($bindType);
echo '<br>';
print_r($bindValue);
...