In my older projects, I used a function to ‘shorten’ my code a bit when doing queries.
Instead of using the usual approach of
$conn = [...]
$stmt = $conn->prepare(...)
$stmt->bind_param(...)
$stmt->execute();
$stmt->close();
$conn->close();
I got a function to do that fore me, called dynamic_db_reader($mysqli, $param, $qry)
.
It returns an array (or null) like: $array[index]['column_name'] = value
Or at least, that’s what it used to do in previous versions. (Worked in PHP 7.4.16)
Here is the code to my function:
/**
* Dynamically executes a given sql statement as prepared statement (?-placeholder).
* Expects correct parameters as an array to replace ?.
* Returns an array with ($arr[index]['column_name'] = value), or null.
*
* @param $ms mysqli
* @param $params array
* @param $qry string
* @return array|null
*/
function dynamic_db_reader($ms, $params, $qry){
$fields = array();
$results = array();
// Replace prefix (DBPREF in: inc/config.php)
if (strpos($qry, 'prefix_') !== false){
$qry = str_replace('prefix', DBPREF, $qry);
}
// Set charset
mysqli_set_charset($ms, 'utf8mb4');
if ($stmt = $ms->prepare($qry)){
// Dynamically bind parameters from $params
if (!isset($params) || !empty($params)){
// Parameters are set
$types = '';
foreach($params as $param){
// Set parameter data type
if (is_string($param)){
$types .= 's'; // Strings
} else if (is_int($param)){
$types .= 'i'; // Integer
} else if (is_float($param)){
$types .= 'd'; // Double/Float
} else {
$types .= 'b'; // Default: Blob and unknown types
}
}
$bind_names[] = $types;
for ($i = 0; $i < count($params); $i++){
$bind_name = 'bind' . $i;
$$bind_name = $params[$i];
$bind_names[] = &$$bind_name;
}
call_user_func_array(array($stmt, 'bind_param'), $bind_names);
}
$stmt->execute();
$meta = $stmt->result_metadata();
// Dynamically create an array to bind the results to
while ($field = $meta->fetch_field()){
$var = $field->name;
$$var = null;
$fields[$var] = &$$var;
}
// Bind results
call_user_func_array(array($stmt, 'bind_result'), $fields); // --> Error :(
// Fetch results
$i = 0;
while ($stmt->fetch()){
$results[$i] = array();
foreach($fields as $k => $v){
$results[$i][$k] = $v;
}
$i++;
}
// Close statement
$stmt->close();
if (sizeof($results) > 0){
return $results;
}
}
return NULL;
}
The error:
Fatal error: Uncaught ArgumentCountError: mysqli_stmt::bind_result() does not accept unknown named parameters in [...]incdb.php:87
Stack trace:
#0 [...]rootincdb.php(87): mysqli_stmt->bind_result(data_key: NULL, data_value: NULL)
#1 [...]rootincfunccommon.php(76): dynamic_db_reader(Object(mysqli), Array, 'SELECT * FROM v...')
#2 [...]rootwwwindex.php(22): getTestArray()
#3 {main}
thrown in [...]rootincdb.php on line 87
How can I fix this code, so that it works in PHP 8 too?
2
Answers
That is an awfully long method for something that is so simple. PHP 8 has added named arguments. When you unpack an array to be used as arguments, its keys are used as parameter names.
mysqli_stmt::bind_result()
doesn’t accept named arguments like you are passing it.If we simplify this code then it should look something like this:
If for some reason you are using mysqli compiled from libmysql client, then … well, it’s time to figure out how to enable mysqlnd or switch over to PDO.
P.S. Please make sure that you have mysqli error reporting enabled. How to get the error message in MySQLi?. Also, there is no point in setting the charset each time. Set it right after making the connection.
What I see happening is that
call_user_func_array
is being passed$fields
as['array_key_txt'=>'array_value']
causing the error.Try to wrap your
$fields
array in "array_values"call_user_func_array(array($stmt, ‘bind_result’), array_values($fields));
UPDATED: The site here explains about the new "Named Parameters" in PHP8
It states that:
will produce the error:
https:php.watch say’s: