Как динамически связывать аргументы mysqli bind_param в PHP?

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

}

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

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

14

Решение

нашел ответ для 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!

16

Другие решения

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

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

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

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

5

Используя 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

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

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

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

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

2

С PHP 5.6 или выше:

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

С PHP 5.5 или ниже Вы могли бы (и я сделал) ожидаю, что следующее сработает:

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

Я решил это, применив систему, аналогичную системе 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);
1