PHP:在5.4版中,使用构造的数组或func_get_args()调用反射方法之间的差异令人困惑

PHP:在5.4版中,使用构造的数组或func_get_args()调用反射方法之间的差异令人困惑,php,Php,在PHP 5.4中,这是一个非常边缘的例子,关于通过引用传递对象,其中出现以下错误: PHP Warning: Parameter 1 to A::foo() expected to be a reference, value given 但仅作为以下因素的复合效应: 使用反射将继承的方法设置为“可访问” 该方法采用显式引用参数(&argument sig) 然后使用func_get_args()调用它,而不是手动构造args数组 不知道为什么这些事情都会导致这种行为,或者它们是否应该引

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

PHP Warning:  Parameter 1 to A::foo() expected to be a reference, value given
但仅作为以下因素的复合效应:

  • 使用反射将继承的方法设置为“可访问”
  • 该方法采用显式引用参数(&argument sig)
  • 然后使用func_get_args()调用它,而不是手动构造args数组
不知道为什么这些事情都会导致这种行为,或者它们是否应该引起这种行为

需要注意的是,PHP5.5中不存在这种效果

这是导致上述错误的代码,但是如果您使用
注释这一行
注释这一行,代码运行正常(例如,对象被正确地传递到'foo'函数):

A类{
私有函数foo(&$arg1){
变量转储('arg1:',$arg1);
}   
}
B类扩展了A类{
公共功能条(){
$x=新的stdClass();
$x->baz=‘只是一个值’;
$this->callPrivate($x);
}
私有函数callPrivate($x)
{
$method=new\ReflectionMethod(
“A”,
“福”
);
//*出于某种原因,需要将私有函数更改为“可访问”,以便在5.4中使用
$method->setAccessible(true);
//工作5.4(*见上文),但不在5.5中
$arguments=func_get_args();
//两个都不工作
$arguments=array($x);//invokeArgs($this,$arguments);
}
}
$y=新的B();
$y->bar();
我不明白为什么这两个$arguments数组之间会有任何区别,因为var_转储它们显示相同的输出。因此,我假设这与较低级别的对象“指针”不同有关(超出了我的深度)


另一个问题是,这是否是PHP5.4、5.5或两者中的错误?

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

通常,这样的更改对用户代码没有任何影响。但在引擎中有几个地方,可观察到的行为根据zval的参考计数不同。其中一个地方通过引用传递:

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

在PHP5.5.6之前,
func\u get\u args()
返回的数组中的zvals始终具有refcount==1,因此它们根据第二种情况执行。从PHP5.5.6开始,这不再是真的,因为按值zvals将始终具有refcount>1,如果您尝试通过引用传递它们,则会导致错误

注意:在PHP5.5.6之前,该代码实际上不起作用(ByRef被忽略)。这只是一个不幸的巧合,你没有在告诉你时出错;)


更新:由于BC中断,我们决定恢复5.5分支上的更改。您将在PHP 5.5.8中获得旧的行为,而新的行为将只在PHP 5.6中出现。

请在此处发布代码,而不是在第三方网站上发布。此外,如果您可以在此处引用预期输出和实际输出,而不必强迫我们自己执行代码,这将有助于澄清您的问题。Fair point@deceze,更新。我希望我能说我完全理解它,但我知道它足以确保这回答了我的问题。回答得很好,谢谢。
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();