views:

125

answers:

1

So I've run into a bit of an issue. I know of a solution but it doesn't seem very clean and I'm wondering if there's a better one.

I'm writing a MySQLi wrapper for running prepared statements. As it's a wrapper and is meant to be reused (dynamic) the amount of columns returned depends on the query and isn't static.

The one solution to this that I've found, and seems is what everyone uses, is call_user_func_array.

It works, but my issue with it is it has me creating an extra array of references that I shouldn't really need in the first place.

For example:

<?php

// Connect, prepare statement, etc.

$stmt->execute();

$fields = $stmt->result_metadata();
$fields = $fields->fetch_fields();

foreach ($fields as $field) {
    // Unnecessary creation of an array.
    $params[] = &$row[$field->name];
}

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

?>

So now I'm creating another array just to get call_user_func_array to pass the parameters by reference because it doesn't adhere to the method definition.

Is there some way to get call_user_func_array to adhere to method / function definitions? Is there a cleaner way of calling a method / function with a variable number of parameters? Or, is there just an overall cleaner and better solution to this issue all together?

I've also run into similar problems when calling bind_param while trying to keep the feature of being able to change the bound parameters and rerunning the statement intact.

On somewhat of a side issue, what exactly is the code below doing.

$row[$field->name] = &$row[$field->name];

It oddly works and doesn't produce any errors, though I don't exactly get how a variable can reference itself. Are there any major differences between that and the former other than the fact I'm not creating another array? Is it better?

Thanks.

EDIT:

This is how I used the Reflection API instead of call_user_fun_array. Any input on my usage of it, performance vs. call_user_func_array, implementation, etc. would be greatly appreciated. I haven't really messed with it before and just about every method is undocumented so I'm not really sure of the proper way to use them.

<?php

foreach ($fields as $field) {
    $row[$field->name] = NULL;
}

$refMethod = new ReflectionMethod($stmt, 'bind_result');
$refMethod->invokeArgs($stmt, $row);

?>
+2  A: 

One way i know of is that you can use the Reflection class's isPassedByReference method.

Here is a solution i came up with after facing that issue for a framework i have had been developing.

Sarfraz
Thanks for the answer. I played around a little with `ReflectionMethod` and used that to call `bind_result` and it looks cleaner. I tried to do some benchmarking and couldn't really come to any conclusion if it was slower or not. _(was most likely my method of testing)_ -- I haven't really played with the Reflection classes at all and most methods are undocumented so that didn't help much. Not sure if there's a better way of using them than how I did. -- Thanks. I'm still interested in other solution if there are any though.
anomareh
just wondering, which method are undocumented, I looked for some at php.net/ and methods were documented and generally their names are self documenting (at least for playing with)
mathroc
@mathroc I was more referring to only argument lists being available for everything. I haven't messed with reflection in general so it wasn't really a matter of just trying to interpret PHP's implementation of it for me. -- It's uses to get information about a class and it's methods and parameters is pretty self documented as you said. It was trying to figure out how to use it properly when trying to invoke methods or other such things that wasn't exactly crystal for me. -- It really wasn't too much to figure out. I just wasn't sure if my implementation was the right way to go about it.
anomareh
@anomareh: does your new reflection implementation work even with arguments passed by reference, if yes and you are getting the expected results then it is fine. I personally used isPassedByReference to check if anything was passed by reference and did the appropirate handling afterwords.
Sarfraz
@Sarfraz Yes it works. `invokeArgs` passes by reference. `invoke` doesn't however. As far as I can tell `isPassedByReference` just returns whether or not the parameter was passed by reference. The function call fails if it's not passed by reference so there really isn't a need for me to check if it was passed by reference or not. `bind_result` only accepts references and will fail otherwise. The function behaves fine if you call it normally. The problem is `call_user_func_array` ignores the method definition and passes by value even though the method is defined to pass by reference.
anomareh
@anomareh:: unfortunately that is the issue with `call_user_func_array`, you are better with your current implementation, and of course reflection class turns out to be really handy in many situations.
Sarfraz
@sarfraz by current do you mean using `ReflectionMethod` or `call_user_func_array`? -- Also if you want to edit your answer with anything I'll up vote you. For some reason it's saying `Vote too old to be changed, unless this answer is edited`. Guess I double clicked it by accident at some point. -- Thanks for the help thus far. Appreciate it.
anomareh
Using `call_user_func_array` gives error with arguments passed by reference as you have seen, and i have not seen any work around to that yet, so as a last resort, you should be using reflection class instead, there is nothing wrong with using reflection class, it is just less often because most developers don't know about it or can't use it due to less documentation, the reflection class is there in all popular languages too. i have edited my answer, should not be too old now :) Thanks and you are welcome :)
Sarfraz
Ah ok. Using `call_user_func_array` as shown in my first example is the work around you speak of. You have to create the extra array `params` with references to `row` and it will pass by reference. That's why right now I'm not sure which is better performance wise.
anomareh