我一直在读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()
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()
魔法。我已经仔细阅读了错误数据库中的现有问题或相关问题,并找到了一个,但无法再次找到它。我正在考虑发布另一份报告,或要求重新打开现有报告。
答案 0 :(得分:3)
这是一个有趣的。
TestClass::testFunction(&$string);
这样可行,但它也会抛出一个call-time-pass-by-reference警告。
主要问题是__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
引起的直接拷贝在这里会成为一个杀手,如果没有足够的箍跳使你有点贴纸,你将无法做到这一点,语法-wise。
答案 1 :(得分: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
的{{1}}不包含引用。这是完全合理的,因为PHP并不知道这些参数旨在被__callStatic
中的引用所接受,也不是我们正在转发参数。
如果我们更改__callStatic
以通过引用接受数组,则试图强制执行所需的行为:
__callStatic
哦,哦!意大利面条-O'!小号
public static function __callStatic($name, &$argumentArray) { return call_user_func_array($name, $argumentArray); }
不能这样做;此外,那是supported by the documentation anyway:
注意:强>
这些魔术方法的所有参数都不能通过引用传递。
尽管我们无法通过引用宣布Fatal error: Method TestClass::__callstatic() cannot take arguments by reference
接受,但如果我们使用__callStatic
(,这在实践中会相当尴尬和自我挫败)来转发引用通过call_user_func_array
:
__callStatic
它再次起作用。
答案 2 :(得分:0)
mixed call_user_func_array ( callback $function , array $param_arr )
如果我们在类调用函数call_user_func_array()
时使用简单逻辑(在这种情况下),它使用静态字符串作为参数(通过魔术方法__callStatic()
传递),因此参数2是{ {1}}函数和该参数也是函数call_user_func_array()
的参数1。它只是字符串而不是变量的指针,这可能是消息的原因:
testFunction()
换句话说,它只是不能将值赋值给不存在的变量,但是,testFunction()希望参数成为引用并抛出警告,因为它不是。
您可以在课外测试,例如:
Warning: Parameter 1 to testFunction() expected to be a reference, value given in ... on line ...
它会抛出相同的警告!因此问题不在于课程,问题在于此功能。
这个函数显然将函数参数作为静态数据传递,而不指向它调用的变量和函数,只是无法处理引用的参数。
答案 3 :(得分: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上测试)
如果您控制接口,此解决方法是干净且可行的。
它的工作方式是将参数包装在一个对象中,该对象按语言定义,始终以“by reference”方式传递。
它允许传递可写(引用)参数,即使使用__callStatic(可能还有__call)。