9

我一直在阅读 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;

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

只关注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

通过显式传递数组元素$test作为引用,我们可以缓解这种情况:

call_user_func_array('testFunction', array(&$test));
var_dump($test);
// string(6) "foobar"

当我们使用 引入类时__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"

省略引用运算符会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"

这些结果是预期的,因为$test不再通过引用传递,更改也不会传递回其范围。然而,这证实了它call_user_func_array()实际上是按预期工作的,而且问题肯定仅限于召唤魔法。

经过进一步阅读,这似乎是 PHP 处理用户函数和__call()/__callStatic()魔法中的一个“错误”。我仔细阅读了现有或相关问题的错误数据库,并找到了一个,但无法再次找到它。我正在考虑发布另一份报告,或请求重新打开现有报告。

4

4 回答 4

3

这是一个有趣的。

TestClass::testFunction(&$string);

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

主要问题是__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'

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

我很确定由 引起的直接副本__callStatic将在这里成为杀手,并且如果没有足够的箍跳使其在语法上有点贴纸,您将无法做到这一点。

于 2011-04-09T03:53:01.690 回答
2

我自己通过逐步重现该问题来回答这个问题,这表明可能无法实现所需的功能。

我们的测试功能相当简单,如下所示:

function testFunction(&$argument)
{
    $argument++;
}

如果我们直接调用该函数,它当然会按预期运行:

$value = 1;
testFunction($value);
var_dump($value); // int(2)

如果我们使用以下方法调用函数call_user_func_array

call_user_func_array('testFunction', [$value]);
var_dump($value); // int(1)

该值保持在1,因为$value没有通过引用传递。我们可以强制这样做:

call_user_func_array('testFunction', [&$value]);
var_dump($value); // int(2)

这又可以直接调用函数。

现在,我们介​​绍这个类,使用__callStatic

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

如果我们通过转发来调用该函数__callStatic

TestClass::testFunction($value);
var_dump($value); // int(1)

该值保持在1,我们还会收到警告:

Warning: Parameter 1 to testFunction() expected to be a reference, value given

验证$argumentArray传入__callStatic的内容不包含引用。这是完全有道理的,因为 PHP 不知道参数旨在通过引用被接受__callStatic,也不知道我们正在转发参数。

如果我们更改__callStatic为通过引用接受数组,以尝试强制执行所需的行为:

public static function __callStatic($name, &$argumentArray)
{
    return call_user_func_array($name, $argumentArray);
}

哦哦!意大利面-O's!

Fatal error: Method TestClass::__callstatic() cannot take arguments by reference

不能那样做;此外,无论如何,文档都支持这一点:

注意:
这些魔术方法的参数都不能通过引用传递。

尽管无法__callStatic通过引用声明接受,但如果我们使用call_user_func_array这在实践中会相当尴尬和弄巧成拙)通过以下方式转发引用__callStatic

call_user_func_array(['TestClass', 'testFunction'], [&$value]);
var_dump($value); // int(2)

它再次起作用。

于 2014-05-10T12:19:18.507 回答
0

哇,经过一个多小时的震撼,我仍然不确定,但是......

mixed call_user_func_array ( callback $function , array $param_arr )

如果我们在类调用函数时使用简单的逻辑(在这种情况下),call_user_func_array()它使用静态字符串作为参数(通过魔术方法传递__callStatic()),所以参数 2 在函数眼中是一个字符串,call_user_func_array()该参数也是函数的参数 1 testFunction()。它只是字符串而不是指向变量的指针,这可能是如下消息的原因:

Warning: Parameter 1 to testFunction() expected to be a reference, value given in ... on line ...

换句话说,它不能将值赋回不存在但仍然存在的变量,testFunction() 期望参数是一个引用并抛出警告,因为它不是。

您可以在课堂外对其进行测试,例如:

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

$string = 'hello';
call_user_func_array('testFunction', array($string));
echo $string;

它会引发相同的警告!所以问题不在于类,问题在于这个功能。

该函数显然将函数参数作为静态数据传递,而不指向变量,它调用的函数无法处理引用的参数。

于 2011-04-09T04:32:29.950 回答
0

多年后,我有一个偶然的时刻:

class TestClass{
    public static function __callStatic($functionName, $arguments) {
        $arguments[0]->{'ioParameter'} = 'two';
    }
}
$objectWrapper = new StdClass();
$objectWrapper->{'ioParameter'} = 'one';
TestClass::testFunction($objectWrapper);
var_dump($objectWrapper); 
// outputs: object(stdClass)#3 (1) { ["ioParameter"]=> string(3) "two" }

(在 php 5.5 上测试)

如果您控制接口,此解决方法是干净且可行的。

它通过将参数包装在一个对象中来工作,根据语言定义,该对象总是“通过引用”传递。

即使使用 __callStatic(可能还有 __call),它也允许传递可写(引用)参数。

于 2018-06-05T12:16:22.020 回答