PHP:在5.4版本中,使用构造数组调用反射方法和使用func_get_args()调用反射方法之间存在混淆


PHP: Confusing disparity between invoking reflection method using constructed array or func_get_args() in version 5.4

这是PHP 5.4中关于通过引用传递对象的一个非常边缘的情况,其中会出现以下错误:

PHP Warning:  Parameter 1 to A::foo() expected to be a reference, value given

但仅作为的复合效应

  • 使用反射将继承的方法设置为"可访问"
  • AND采用显式引用参数(&argument sig)的方法
  • 然后用func_get_args()调用它,而不是手动构造args数组

不知道为什么这些事情都会导致这种行为,也不知道他们是否应该这样做。

需要注意的是,PHP 5.5中没有这种效果。

这是将导致上述错误的代码,但如果您用COMMENT THIS LINE注释该行,则代码运行良好(例如,对象被正确地传递到"foo"函数):

class A {
    private function foo(&$arg1) {
        var_dump('arg1: ', $arg1);
    }   
}
class B extends A {
    public function bar() {
        $x = new stdClass();
        $x->baz = 'just a value';
        $this->callPrivate($x);
    }
    private function callPrivate($x)
    {
        $method = new 'ReflectionMethod(
            'A',
            'foo'
        );
        //* for some reason, the private function needs to have been changed to be 'accessible' for this to work in 5.4
        $method->setAccessible(true);
        //working 5.4 (* see above) but not in 5.5
        $arguments = func_get_args();
        //not working in either
        $arguments = array($x); // <---- COMMENT THIS LINE TO SEE IT WORK IN PHP 5.4
        return $method->invokeArgs($this, $arguments);
    }
}
$y = new B();
$y->bar();

我不明白为什么这两个$arguments数组之间会有任何区别,因为var_dumping会显示相同的输出。因此,我认为这与较低级别的东西有关,比如对象"指针"不同(超出了我的深度)?

另一个问题是,这是否是PHP 5.4、5.5或两者中的错误?

在PHP 5.5.6之前,func_get_args()从VM堆栈中获取参数,复制它们并在数组中返回它们。在PHP 5.5.6中引入了一个优化,在常见情况下避免了这些昂贵的副本。不是复制zval,而是增加refcount(尽管有ref args)

通常情况下,这样的更改对用户代码没有任何影响。但在引擎中的一些地方,可观察到的行为会因zval的引用次数而有所不同。其中一个地方是通过引用传递:

对于动态函数调用的情况,如果zval是引用或refcount==1,则可以通过引用传递zval。

在PHP 5.5.6之前,func_get_args()返回的数组中的zval总是refcount==1,所以它们是基于第二种情况进行的。从PHP 5.5.6开始,这不再是真的,因为按值zvals将始终具有refcount>1,并且如果您试图通过引用传递它们,则会导致错误。

注意:在PHP 5.5.6之前,代码实际上不起作用(by-ref被忽略)。这只是一个不幸的巧合,你没有得到一个错误告诉你这样做;)

更新:由于BC中断,我们决定恢复5.5分支的更改。您将在PHP5.5.8中恢复旧的行为,而新的行为仅在PHP5.6中。