php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #43568 mysqli statement stmt->bind_param doesn't work when called by call_user_func
Submitted: 2007-12-11 17:11 UTC Modified: 2008-01-31 13:11 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (100.0%)
From: gabriel at oxeva dot fr Assigned:
Status: Closed Package: MySQLi related
PHP Version: 5.3CVS-2007-12-11 (snap) OS: Linux 2.6
Private report: No CVE-ID:
 [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

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2008-01-04 13:42 UTC] uw@php.net

 [2008-01-04 13:49 UTC] uw@php.net
Forgot to say: IMHO this should be reported over at Zend Framework but not here. I've tested 5_2, 5_3, 6_0 and I see no differences in handling.
 [2008-01-04 14:30 UTC] uw@php.net
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.





 [2008-01-04 14:53 UTC] gabriel at oxeva dot fr
I understand now why this isn't supposed to work, but it is with PHP 5.2  

This "new" behavior make this bind_param function a lot less attractive when using it in situations like ZF does: binding an unknown number of parameters to a statement (like in any Object-Relational Mapper). There is no way now other than using the very ugly eval() to dynamically create the required number of variables to bind with bind_param.

Besides, this method is never suppposed to return NULL, (doc says boolean return, TRUE on success, FALSE on failure). This is why I almost immediately thought that was a bug.

Another fact is the doc example is very strange with calling stmt_bind_param with variables _before_ having them created. This is very "special" coding style.

My final thought: The old behavior (PHP 5.2) is really useful because there is no other way to do the same with the new one except using eval() as an ugly workaround, and the new behavior is returning an undocumented (and useless) value.

I really don't know if I have to file a new bug report in 5_2 about the incorrect behavior of bind_param (which is IMHO the most useful one).

Thank you anyway for the explanation, I'm now waiting for the final opinions about this.
 [2008-01-04 17:20 UTC] gabriel at oxeva dot fr
It seems you posted your comment before I finished writing mine. Good to see that I'm not going crazy because I just tested the case with sort and got the same bug as you! Should I change the category for this bug? If so, which one?
 [2008-01-04 18:00 UTC] uw@php.net

 [2008-01-05 07:47 UTC] heyitsjunk at gmail dot com
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 =)
 [2008-01-05 15:54 UTC] gabriel at oxeva dot fr
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.
 [2008-01-10 15:15 UTC] johannes@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.

As Ulf (uw) said above: mysqli_stmt_bind_param() expects the parameters to be references.It worked (more or less) by accident in previous versions. But Dmitry now added a Warning so it won't fail silently; with a current snapshot you'll get a error like "Warning: Parameter 1 to mysqli_stmt::bind_param() expected to be a reference, value give in ...". see eyitsjunk at gmail dot com's comment to see how you can set the references right.
 [2008-01-31 13:11 UTC] johannes@php.net
A fix for the Zend Framework has been proposed: http://framework.zend.com/issues/browse/ZF-2529
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Sun Apr 20 08:02:33 2014 UTC