msgbartop
PHP语言, PHP扩展, Zend引擎相关的研究,技术,新闻分享 – 左手代码 右手诗
msgbarbottom

22 Jun 10 ReflectionFunction(Method)引用参数导致Invocation failed

今天同事反馈一个问题, PHP5.2.x在使用反射做函数包装的时候, 得到”Invocation failed”的异常, 而使用call_user_func代替则不会,

原逻辑太复杂, 经过精简以后可重现异常的代码如下(使用ReflectionFunction为例, ReflectionMethod类似):

function who(&$name) {
    echo $name;
}
$name = "laruence";
$method = new ReflectionFunction("who");
$method->invokeArgs(array($name));
//异常:
Uncaught exception 'ReflectionException' with message
'Invocation of function who() failed'

找原因过程中歧途我就不多言了, 最后跟踪到invokeArgs会调用Zend引擎提供的zend_call_function, 而在zend_call_function中, 有如下一段逻辑引起我的怀疑(注意注释部分):

int zend_call_function(zend_fcall_info *fci
         , zend_fcall_info_cache *fci_cache TSRMLS_DC) {
//以上省略
        if (ARG_SHOULD_BE_SENT_BY_REF(EX(function_state).function, i+1)
         && !PZVAL_IS_REF(*fci->params[i])) {
/*如果形参是引用传递 并且参数不是引用 */
            if ((*fci->params[i])->refcount>1) {
/*如果参数的refcount大于1 */
                zval *new_zval;

                if (fci->no_separation) {
/*如果不容许执行分离操作, 则返回失败 */
                    return FAILURE;
                }
//以下省略

也就是说, 如果一个申明为引用传递的参数不为引用传递, 而refcount又大于1, 那么在不容许分离的条件下, 就会导致zend_call_function失败返回(如果对refcount和变量分离不了解, 可以参看我之前的文章深入理解PHP原理之变量分离/引用).

经过验证, 果然invokeArgs在构造zend_fcall_info fci的时候, 是禁止separation的, 所以导致zend_call_funcion返回FAILURE.

而使用call_user_func则不会是因为,call_user_function不考虑no_separation直接分离, 这一点在PHP手册中, call_user_func中是有说明的:

Note: Note that the parameters for call_user_func() are not passed by reference.

找到了原因, 那解决的办法, 也就容易了:

function who(&$name) {
    echo $name;
}
$name = "laruence";
$method = new ReflectionFunction("who");
$method->invokeArgs(array(&$name)); //is_ref


分享到:



Random Posts:

Tags: , ,

8 Responses to “ReflectionFunction(Method)引用参数导致Invocation failed”

  1. ReflectionFunction(Method)引用参数导致Invocation failed | 万维网黑客联盟 |

    [...] 本文地址: http://www.laruence.com/2010/06/22/1618.html [...]

  2. mahone |

    好文,已阅。只是这上面的很多文章都有点看不懂

  3. wangchao |

    array_walk($keys, ‘trimblank’);
    function trimblank(&$var) {
    $var = trim($var);
    return $var;
    }
    上面可以去除数组中元素的空白
    但是
    array_walk($keys, ‘trimblank’);
    function trimblank(&$var) {
    return trim($var);
    }
    就不能去除空白了,这个是为什么啊。请赐教。

  4. 烂叶 |

    鸟哥出东西。都是好东西。

  5. ezsky |

    在这也能碰上熟人.

  6. 打假网 |

    word shown in the picture. Click on t

  7. sexy nightwear |

    Shop for sexy nightwear,satin nightwear,thongs,tights,stockings,chemises,nightgowns, corsets, costumes FREE UK delivery on all orders.

  8. CFC4N |

    鸟哥最近很高产,又出新作了。先占位,慢慢拜读。

Leave a Reply

*