__callStatic(),call_user_func_array(),引用和PHP 5.3.1

Dan*_*ugg 8 php pass-by-reference magic-methods dynamic-function

我一直在阅读SO和其他地方,但我似乎无法找到任何结论.

有没有办法通过此调用堆栈有效地携带引用,从而产生所需的功能,如下面的示例所述?虽然该示例并未尝试解决此问题,但它确实说明了问题:

class TestClass{
    // surely __call would result similarly
    public static function __callStatic($function, $arguments){
        return call_user_func_array($function, $arguments);
    }
}

// note argument by reference
function testFunction(&$arg){
    $arg .= 'bar';
}

$test = 'foo';
TestClass::testFunction($test);

// expecting: 'foobar'
// getting:   'foo' and a warning about the reference
echo $test;
Run Code Online (Sandbox Code Playgroud)

为了激发潜在的解决方案,我将在此处添加摘要详细信息:

只关注call_user_func_array(),我们可以确定(至少在PHP 5.3.1中)你不能通过引用隐式传递参数:

function testFunction(&$arg){
    $arg .= 'bar';
}

$test = 'foo';
call_user_func_array('testFunction', array($test));
var_dump($test);
// string(3) "foo" and a warning about the non-reference parameter
Run Code Online (Sandbox Code Playgroud)

通过显式传递数组元素$test作为参考,我们可以缓解这个问题:

call_user_func_array('testFunction', array(&$test));
var_dump($test);
// string(6) "foobar"
Run Code Online (Sandbox Code Playgroud)

当我们引入类时__callStatic(),引用的显式调用时参数似乎按照我的预期进行,但是发布了弃用警告(在我的IDE中):

class TestClass{
    public static function __callStatic($function, $arguments){
        return call_user_func_array($function, $arguments);
    }
}

function testFunction(&$arg){
    $arg .= 'bar';
}

$test = 'foo';
TestClass::testFunction(&$test);
var_dump($test);
// string(6) "foobar"
Run Code Online (Sandbox Code Playgroud)

省略参考运算符TestClass::testFunction()导致$test通过值传递__callStatic(),当然通过值作为数组元素传递给testFunction()via call_user_func_array().这会产生警告,因为testFunction()需要参考.

黑客攻击,一些额外的细节浮出水面.__callStatic()如果写入以引用(public static function &__callStatic())返回的定义没有可见效果.此外,$arguments__callStatic()作为引用重新构建数组元素时,我们可以看到它的call_user_func_array()工作方式与预期的一样:

class TestClass{
    public static function __callStatic($function, $arguments){
        foreach($arguments as &$arg){}
        call_user_func_array($function, $arguments);
        var_dump($arguments);
        // array(1) {
        //   [0]=>
        //   &string(6) "foobar"
        // }
    }
}

function testFunction(&$arg){
    $arg .= 'bar';
}

$test = 'foo';
TestClass::testFunction($test);
var_dump($test);
// string(3) "foo"
Run Code Online (Sandbox Code Playgroud)

这些结果是预期的,因为$test不再通过引用传递,更改不会传回其范围.然而,这证实call_user_func_array()了事实上正在按预期工作,并且问题肯定局限于调用魔法.

在进一步阅读时,似乎它可能是PHP处理用户函数和__call()/ __callStatic()magic 的"错误" .我已经仔细阅读了错误数据库中的现有问题或相关问题,并找到了一个,但无法再次找到它.我正在考虑发布另一份报告,或要求重新开放现有报告.

Cha*_*les 3

这是一件有趣的事。

TestClass::testFunction(&$string);
Run Code Online (Sandbox Code Playgroud)

这是可行的,但它也会引发调用时间传递引用警告。

主要问题是__callStatic第二个参数是按值进来的。它正在创建数据的副本,除非该数据已经是引用。

我们可以这样复制调用错误:

call_user_func_array('testFunction', array( $string ));
// PHP Warning:  Parameter 1 to testFunction() expected to be a reference, value given

call_user_func_array('testFunction', array( &$string ));
echo $string;
// 'helloworld'
Run Code Online (Sandbox Code Playgroud)

我尝试修改__callStatic方法以通过引用深度复制数组,但这也不起作用。

我非常确定,由 引起的直接复制__callStatic将成为这里的杀手,并且如果没有足够的跳圈使其在语法方面有点粘性,您将无法做到这一点。