我有以下代码:
<?php
class Testme {
public static function foo(&$ref) {
$ref = 1;
}
}
call_user_func_array(array('Testme', 'foo'), array(&$test));
var_dump($test);
并正确显示“1”。但是我想使用“Invoker”方法做同样的事情,如下所示:
<?php
class Testme {
public static function foo(&$ref) {
$ref = 1;
}
}
class Invoker {
public static function invoke($func_name) {
$args = func_get_args();
call_user_func_array(array('Testme', $func_name), array_slice($args,1));
}
}
$test = 2;
Invoker::invoke('foo', $test);
var_dump($test);
这会引发严格的标准错误(PHP 5.5)并显示“2”
问题是,在使用Testme::foo
时,有没有办法通过引用传递参数func_get_args()
? (欢迎解决方法)
答案 0 :(得分:3)
这是不可能的,因为func_get_args()
返回复制值而不是引用。但是通过使用许多可选参数,它有一个丑陋的解决方法。
class Testme {
public static function foo(&$ref, &$ref2) {
$ref = 1;
$ref2 = 2;
}
}
class Invoker {
public static function invoke($func_name,
&$arg1 = null, &$arg2 = null, &$arg3 = null,
&$arg4 = null, &$arg5 = null, &$arg6 = null)
{
$argc = func_num_args();
$args = array();
for($i = 1; $i < $argc; $i++) {
$name = 'arg' . $i;
if ($$name !== null) {
$args[] = &$$name;
}
}
call_user_func_array(array('Testme', $func_name), $args);
}
}
$test = 5;
$test2 = 6;
Invoker::invoke('foo', $test, $test2);
var_dump($test);
var_dump($test2);
答案 1 :(得分:3)
无法从func_get_args()
中获取引用,因为它返回的数组中包含传入值的副本。请参阅PHP Reference。
此外,由于不再支持运行时按引用传递,因此必须在每个方法/函数签名中表示引用。这是一个应该解决让Invoker通过引用传递的整体问题的例子,但func_get_args()
没有解决方法。
<?php
class Testme {
public static function foo(&$ref) {
$ref = 1;
}
}
class Invoker {
public static function invoke($func_name, &$args){
call_user_func_array(array('Testme', $func_name), $args);
}
}
$test = 10;
$args[] = &$test;
Invoker::invoke('foo', $args);
var_dump($test);
如果您知道要通过引用调用,这可能对您有用,并且可能有两个调用者,一个Invoker::invokeByRef
另一个正常Invoker::invoke
,它通过副本执行标准调用。
答案 2 :(得分:1)
这是不可能轻易做到的,因为func_get_args
没有处理引用,并且没有其他选择。
如果你愿意将自己限制在最大数量的论点中,并且不介意使用黑暗艺术,那么我认为在所有情况下都有正确的解决方法。
首先,将调用者声明为接受一定数量的参数,所有参数都通过引用并具有默认值(确切的默认值并不重要):
public static function invoke(callable $callable, &$p1 = null, &$p2 = null, ...);
然后,在invoke
内确定您正在处理的可调用类型。您需要这样做才能创建描述调用目标的ReflectionFunctionAbstract
的适当实例。这很重要,因为我们绝对需要确定目标所需的参数数量,并且还可以启用检测具有不正确数量的参数的调用等设施。
汇编一组参数后,首先使用call_user_func_array
,就像你想要的那样。
这种方法基于invisal uses的相同思想,但有一个重要区别:使用反射可以让你始终正确地确定要传递多少个参数(invisal的解决方案使用保护值),这反过来又不限制可以传递给调用目标的值(使用invisal的解决方案,您不能将保护值作为合法参数传递给调用目标)。
public static function invoke(callable $callable, &$p1 = null, &$p2 = null)
{
if (is_string($callable) && strpos($callable, '::')) {
// Strings are usually free function names, but they can also
// specify a static method with ClassName::methodName --
// if that's the case, convert to array form
$callable = explode('::', $callable);
}
// Get a ReflectionFunctionAbstract instance that will give us
// information about the invocation target's parameters
if (is_string($callable)) {
// Now we know it refers to a free function
$reflector = new ReflectionFunction($callable);
}
else if (is_array($callable)) {
list ($class, $method) = $callable;
$reflector = new ReflectionMethod($class, $method);
}
else {
// must be an object -- either a closure or a functor
$reflector = new ReflectionObject($callable);
$reflector = $reflector->getMethod('__invoke');
}
$forwardedArguments = [];
$incomingArgumentCount = func_num_args() - 1;
$paramIndex = 0;
foreach($reflector->getParameters() as $param) {
if ($paramIndex >= $incomingArgumentCount) {
if (!$param->isOptional()) {
// invocation target requires parameter that was not passed,
// perhaps we want to handle the error right now?
}
break; // call target will less parameters than it can accept
}
$forwardedArguments[] = &${'p'.(++$paramIndex)};
}
return call_user_func_array($callable, $forwardedArguments);
}
<强> See it in action 强>