How to Bind MySQLi Bind_Param Arguments Dynamically in PHP

How to bind mysqli bind_param arguments dynamically in PHP?

found the answer for 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();
}

then:

$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 is so lame when comes to this. I think I should be migrating to PDO!

Dynamically bind params in $bind_param(); Mysqli

Here is an example that could help ( prepare() function is a class method ).

function prepare( $query, $bind = array() )
{
if ( !$stmt = $this->mysqli->prepare( $query ) )
throw new Exception( 'Query failed: ' . $query . PHP_EOL . $this->mysqli->error );

// if $bind is not an empty array shift the type element off the beginning and call stmt->bind_param() with variables to bind passed as reference
if ( $type = array_shift( $bind ) )
call_user_func_array(
array( $stmt, 'bind_param' ),
array_merge( array( $type ), array_map( function( &$item ) { return $item; }, $bind ) )
);

if ( !$stmt->execute() )
throw new Exception( 'Execute failed: ' . PHP_EOL . $stmt->error );

// choose what to return here ( 'affected_rows', 'insert_id', 'mysqli_result', 'stmt', 'array' )

}

Example of usage:

$db->prepare( "SELECT * FROM user WHERE user_name = ? OR user_email = ?", [ 'ss', $user_name, $user_name ] );

Dynamic variables binding in mysqli bind_param

$stmt = $mysqli->prepare('SELECT * FROM users WHERE lname = ? AND fname = ?');
$stmt->bind_param('ss', $param[0], $param[1]);
$stmt->execute();

//other version
$stmt = $mysqli->prepare('SELECT * FROM users WHERE lname = ? AND fname = ?');
$type = "ss";
$names = array("Maq", "bool");
$params = array(&$type, &$names[0], &$names[1]);

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

How to use mysqli bind_param dynamically

Yes it's possible and very simple with php5.6, first you need know how many paraments str_repeat(), count() can you help and the unpacking operator (...) too, so this way is possible make binds dynimic.

$params = [10, 50, 51, 99];
$types = str_repeat('i',count($params));

$stmt = $mysqli->prepare("SELECT * FROM t WHERE id IN (?,?,?,?)");
$stmt->bind_param($types, ...$params);
if(!$stmt->excute()){
echo mysqli_error($con);
}

How to dynamically bind params in mysqli prepared statement?

You are on a very right track! Such a function should be an everyday companion for the every PHP programmer using mysqli, but strangely, only few ever have an idea of creating one.

I've had an exactly the same idea once and implemented a mysqli helper function of my own:

function prepared_query($mysqli, $sql, $params, $types = "")
{
$types = $types ?: str_repeat("s", count($params));
$stmt = $mysqli->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt;
}

Main differences from your approach

  • the connection is made only once. Your code connects every time it executes a query and this is absolutely NOT what it should do
  • it can be used for any query, not only SELECT. You can check the versatility of the function in the examples section down in the article
  • types made optional as most of time you don't care for the type
  • no bizarre $location variable. Honestly, whatever HTTP stuff should never be a part of a database operation! If you're curious how to properly deal with errors, here is my other article Error reporting in PHP

With your example query it could be used like this

$check = prepared_query($sql, [$id, $mail])->get_result()->fetch_row();

or, if you want a distinct function, you can make it as well

function check_database($mysqli, $sql, $params, $types = "")
{
return prepared_query($mysqli, $sql, $params, $types)->get_result()->fetch_row();
}

and now it can be called as

$check = check_database($sql, [$id, $mail]);

Build SELECT query with dynamic number of LIKE conditions as a mysqli prepared statement

The % wrapping goes around the parameters, not the placeholders.

My snippet will be using object-oriented mysqli syntax instead of the procedural syntax that your code demonstrates.

First you need to set up the necessary ingredients:

  1. the WHERE clause expressions -- to be separated by ORs
  2. the data types of your values -- your values are strings, so use "s"
  3. the parameters to be bound to the prepared statement

I am going to combine #2 and #3 into one variable for simpler "unpacking" with the splat operator (...). The data type string must be the first element, then one or more elements will represent the bound values.

As a logical inclusion, if you have no conditions in your WHERE clause, there is no benefit to using a prepared statement; just directly query the table.

Code: (100% Tested / Successful Code)

$string = "my name";

$conditions = [];
$parameters = [''];
foreach (array_unique(explode(' ', $string)) as $value) {
$conditions[] = "name LIKE ?";
$parameters[0] .= 's';
$parameters[] = "%{$value}%";
}
// $parameters now holds ['ss', '%my%', '%name%']

$query = "SELECT * FROM info";
if ($conditions) {
$stmt = $conn->prepare($query . ' WHERE ' . implode(' OR ', $conditions));
$stmt->bind_param(...$parameters);
$stmt->execute();
$result = $stmt->get_result();
} else {
$result = $conn->query($query);
}
foreach ($result as $row) {
echo "<div>{$row['name']} and whatever other columns you want</div>";
}

For anyone looking for similar dynamic querying techniques:

  • SELECT with dynamic number of values in IN()
  • INSERT dynamic number of rows with one execute() call

Dynamically bind mysqli_stmt parameters and then bind result (PHP)

Okay, here is a way to do it:

Edited, to fix bug when fetching multiple rows

$sql = "SELECT `first_name`,`last_name` FROM `users` WHERE `country` =? AND `state`=?";
$params = array('Australia','Victoria');

/*
In my real app the below code is wrapped up in a class
But this is just for example's sake.
You could easily throw it in a function or class
*/

// This will loop through params, and generate types. e.g. 'ss'
$types = '';
foreach($params as $param) {
if(is_int($param)) {
$types .= 'i'; //integer
} elseif (is_float($param)) {
$types .= 'd'; //double
} elseif (is_string($param)) {
$types .= 's'; //string
} else {
$types .= 'b'; //blob and unknown
}
}
array_unshift($params, $types);

// Start stmt
$query = $this->connection->stmt_init(); // $this->connection is the mysqli connection instance
if($query->prepare($sql)) {

// Bind Params
call_user_func_array(array($query,'bind_param'),$params);

$query->execute();

// Get metadata for field names
$meta = $query->result_metadata();

// initialise some empty arrays
$fields = $results = array();

// This is the tricky bit dynamically creating an array of variables to use
// to bind the results
while ($field = $meta->fetch_field()) {
$var = $field->name;
$$var = null;
$fields[$var] = &$$var;
}


$fieldCount = count($fieldNames);

// Bind Results
call_user_func_array(array($query,'bind_result'),$fields);

$i=0;
while ($query->fetch()){
for($l=0;$l<$fieldCount;$l++) $results[$i][$fieldNames[$l]] = $fields[$fieldNames[$l]];
$i++;
}

$query->close();

// And now we have a beautiful
// array of results, just like
//fetch_assoc
echo "<pre>";
print_r($results);
echo "</pre>";
}

How to assign the correct type to bind_param dynamically based on retrieved column types?

Guessing the parameter type always smells. Whereas setting all parameters to "s" works flawless most of time.

So let me suggest you another solution. Make the types explicit but optional. By default it will be "s" but with a possibility to define the types manually, just like I did in my mysqli helper function:

function prepared_query($mysqli, $sql, $params, $types = "")
{
$types = $types ?: str_repeat("s", count($params));
$stmt = $mysqli->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt;
}

when you don't need any sprecific types (most of time), just leave them out:

$sql = "SELECT * FROM tmp_mysqli_helper_test WHERE id > ?";
$res = prepared_query($conn, $sql, [1])->get_result();

but every time you need it, it's already here and explicit, so you could set the exact type you want:

$sql = "SELECT * FROM tmp_mysqli_helper_test WHERE id > ?";
$res = prepared_query($conn, $sql, [1], "i")->get_result();

simple, clean and concise



Related Topics



Leave a reply



Submit