|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2007-12-11 17:11 UTC] gabriel at oxeva dot fr
Description:
------------
The Mysqli method stmt->bind_param() doesn't work when called by call_user_func_array. Too bad Zend Framework is using this method with the Mysqli driver.
In the following example, call_user_func_array always return NULL, but directly calling $stmt->bind_param() works and return the correct bool(true) value.
Note: this works in PHP 5.2
Note: The same error occurs using either libmysqlclient or mysqlnd.
Final note: ATM the PDO_mysql driver works. But leaving a bug like this in the mysqli driver is not an option :-)
Reproduce code:
---------------
$my = new mysqli('localhost', 'test', 'test', 'testdb');
$stmt = $my->prepare('INSERT INTO `car` (`brand`, `brand_id`, `car_type`) VALUES (?, ?, ?)');
$param = array('sss', 'GMC', 1, 'SUV');
$return = call_user_func_array(array($stmt, 'bind_param'), $param);
var_dump($return);
$stmt->execute();
echo $stmt->errno, ':', $stmt->error;
Expected result:
----------------
var_dump($return) prints bool(true)
No error returned in $stmt->errno / $stmt->error
Actual result:
--------------
var_dump($return) prints NULL
Statement error is returned:
2031:No data supplied for parameters in prepared statement
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Oct 30 18:00:02 2025 UTC |
IMHO this is not a bug. However, I leave it to Johannes/Andrey to review and close the issue. bind_param() is about binding variables not constant values: "stmt->bind_param() ? Binds variables to a prepared statement as parameters". The signature of the function tells you that you have to pass input parameter variables by reference: bool mysqli_stmt_bind_param ( mysqli_stmt $stmt , string $types , mixed &$var1 [, mixed &$... ] ) Using references to "real variables" instead of "constant values" does work fine. Note the small difference between references to variables in PHP and memory pointer in C. In PHP your references have to point to existing variables. You can't make them point to arbitrary places in memory like in C. If a PHP function asks you to provide a reference to an existing variable, you must not try to pass a (constant) value to it. But this is exactly what you do in your call_user_func_array() example. Use the function like this: $int = 1; mysqli_stmt_bind_param($stmt, 'i', $int); $stmt->bind_param('i', $int); And, if you want to use call_user_func_array(), the syntax is: $int = 1; $format = 'i'; $params = array(0 => $format, 1 => &$int); call_user_func_array('mysqli_stmt_bind_param', $params); call_user_func_array(array($stmt, 'bind_param'), $params); // - or - $int = 1; $params = array(0 => 'i', 1 => &$int); call_user_func_array('mysqli_stmt_bind_param', $params); call_user_func_array(array($stmt, 'bind_param'), $params); // - or - $int = 1; call_user_func_array('mysqli_stmt_bind_param', array('i', &$int)); Neither of the following does work with PHP 5_3. Recall that the function is about binding variables not binding (constant) values, recall that you have to pass a reference to a variable: WRONG: mysqli_stmt_bind_param($stmt, 'i', 1); WRONG: call_user_func_array('mysqli_stmt_bind_param', array('i', 1));Hold on, a PHP internals expert should have a look. sort() is another example of a function which uses pass by reference. The manual says about call_user_func_array(): "Note: Referenced variables in param_arr are passed to the function by a reference, others are passed by a value. In other words, it does not depend on the function signature whether the parameter is passed by a value or by a reference. " From a discussion with Johannes: sapi/cli/php -r '$a = array(2, 1); var_dump(call_user_func_array("sort", array($a)));' PHP 5.2 -> bool(true) PHP 5.3 -> NULL Looks like its not a mysqli issue but rather a general PHP issue.Gabriel you don't need to use eval() to solve this issue.. I'll try to explain by making your first example work the way you're thinking. I changed some variable names for convenience but it's the same example. $my = new mysqli('localhost', 'test', 'test', 'testdb'); $stmt = $my->prepare('INSERT INTO `car` (`brand`, `brand_id`, `car_type`) VALUES (?, ?, ?)'); $params = array('sss', 'GMC', 1, 'SUV'); //Okay I'll assume that the above is what I have to work with. $types = $params[0]; unset($param[0]); $i = 0; foreach ($params as $param) { $bind_name = 'bind' . $i; $$bind_name = $param; $bind_names[] = &$$bind_name; } $bind_params[] = $types; $bind_params[] = array_merge($bind_params, $bind_names) //Added code ends here. $return = call_user_func_array(array($stmt, 'bind_param'), $bind_params); var_dump($return); $stmt->execute(); echo $stmt->errno, ':', $stmt->error; So now you got yourself a working code. With the same method of binding, you can make yourself the ultimate lazy query function that handles this situation: $params = array('sss', 'GMC', 1, 'SUV'); query('INSERT INTO `car` (`brand`, `brand_id`, `car_type`) VALUES (?, ?, ?)', $params); or this is my preference but it doesn't handle BLOB types: query('INSERT INTO `car` (`brand`, `brand_id`, `car_type`) VALUES (?, ?, ?)', $brand, $brand_id, $car_type); Anyways my point is I do agree with this logic though the doc explanation "call_user_func_array" is MISLEADING. I couldn't figure it out until I saw uw.php.net's examples. The whole binding scenario is pretty tough and inflexible. Still needs to be looked over by an expert =)It's been a long time I haven't seen dynamic variables ($$var) in PHP code. I consider that such language constructs are what make PHP code sometime very unreadable, and creates very hard-to-find bugs. Nice workaround though :-) And you're right, the binding scenario is totally illogical and inflexible. I think the normal scenario is creating the variables (or an array of constant variables such as "array('abc', '3, 'test')", and then binding these with the prepared statement, but not like in the bind_param doc example which does a binding with auto-created variables, then assigns them some data. Besides, using undefined variables in a function call causes PHP to throw a Notice in E_ALL error mode, doesn't it? (Maybe it's different with references, but undocumented) Note this is not the first time I see some very strange PHP behavior when using references (PHP-like pointers), not to mention how hard it is to do debugging with them. Or maybe I'm completely wrong in the way I write my own PHP code logic? Anyway, I'm a bit out of this bug report's context. We still have a bug in the way call_user_func_array returns functions passing variables by reference.