- URL: https://www.laruence.com/en/2010/06/22/1618.html
- Please include attribution when republishing.
A colleague reported a problem today: in PHP 5.2.x, when using reflection to wrap a function, you get an "Invocation failed" exception, whereas using call_user_func instead does not.
The original logic was too complex; after trimming it down, the code that reproduces the exception is as follows (using ReflectionFunction as the example; ReflectionMethod is similar):
function who(&$name) {
echo $name;
}
$name = "laruence";
$method = new ReflectionFunction("who");
$method->invokeArgs(array($name));
//exception:
Uncaught exception 'ReflectionException' with message
'Invocation of function who() failed'
I won't go into the dead ends I hit while tracking down the cause. In the end I traced that invokeArgs calls zend_call_function, provided by the Zend engine, and inside zend_call_function there is a piece of logic that aroused my suspicion (note the comments):
int zend_call_function(zend_fcall_info *fci
, zend_fcall_info_cache *fci_cache TSRMLS_DC) {
//omitted above
if (ARG_SHOULD_BE_SENT_BY_REF(EX(function_state).function, i+1)
&& !PZVAL_IS_REF(*fci->params[i])) {
/*if the formal parameter is passed by reference and the argument is not a reference */
if ((*fci->params[i])->refcount>1) {
/*if the argument's refcount is greater than 1 */
zval *new_zval;
if (fci->no_separation) {
/*if separation is not allowed, return failure */
return FAILURE;
}
//omitted below
That is, if a parameter declared to be passed by reference is not passed by reference, and its refcount is greater than 1, then under the condition that separation is not allowed, zend_call_function fails and returns (if you're unfamiliar with refcount and variable separation, see my earlier article Understanding PHP Internals: Variable Separation/Reference).
After verifying, it turned out that invokeArgs indeed forbids separation when constructing the zend_fcall_info fci, so zend_call_funcion returns FAILURE.
And the reason using call_user_func doesn't have this problem is that call_user_function performs separation directly without considering no_separation. This point is documented in the PHP manual under call_user_func:
Note: Note that the parameters for call_user_func() are not passed by reference.
Having found the cause, the fix is easy:
function who(&$name) {
echo $name;
}
$name = "laruence";
$method = new ReflectionFunction("who");
$method->invokeArgs(array(&$name)); //is_ref
Be First to Comment