IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

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

风雪之隅 2010-06-23 12:58:37 累计浏览 1,769 次
本机暂存

今天同事反馈一个问题, 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

同分类推荐文章

  1. Vibe新开源项目 - Vaala AI Gateway (2026-05-17 02:10:19)
  2. SmartPerfetto 架构文章 Q&A:8 个深度技术问答 (2026-04-10 11:00:00)
  3. 让 AI 把我的 PHP 博客重写成 Go (2026-03-27 18:33:54)

查看更多 后端 文章 →

建议继续学习

  1. 使用gettext来支持PHP的多语言 (累计阅读 39,197)
  2. WordPress插件开发 -- 在插件使用数据库存储数据 (累计阅读 29,097)
  3. Paypal接口详细代码(PHP版,非API接口) (累计阅读 19,352)
  4. Go Reflect 性能 (累计阅读 14,073)
  5. 我的PHP,Python和Ruby之路 (累计阅读 13,075)
  6. include(“./file.php”)和include(“file.php”)区别 (累计阅读 12,731)
  7. 15个最好的免费开源电子商务平台 (累计阅读 12,471)
  8. Redis消息队列的若干实现方式 (累计阅读 12,011)
  9. 到底什么是MVC? (累计阅读 11,722)
  10. 整理了一份招PHP高级工程师的面试题 (累计阅读 11,506)