1

我有以下代码:

<?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()?(欢迎解决方法)

4

3 回答 3

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);
于 2013-08-28T15:23:35.247 回答
3

无法从中获取引用,func_get_args()因为它返回一个包含传入值副本的数组。请参阅PHP 参考

此外,由于不再支持运行时按引用传递,因此您必须在每个方法/函数签名中指明引用。这是一个示例,应该可以解决通过引用传递的调用程序的整体问题,但是对于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通过副本执行标准调用的普通调用者。

于 2013-08-28T15:44:08.197 回答
1

问题

这不可能很容易做到,因为func_get_args不涉及引用,并且没有其他选择。

这个想法

如果您愿意将自己限制在已知的最大参数数量并且不介意使用黑暗艺术,那么有一个可怕的解决方法,我相信它在所有情况下都能正常工作。

首先,将调用者声明为接受一定数量的参数,所有参数都通过引用并具有默认值(确切的默认值并不重要):

public static function invoke(callable $callable, &$p1 = null, &$p2 = null, ...);

然后,在内部invoke确定您正在处理的可调用对象类型。您需要这样做才能创建ReflectionFunctionAbstract描述调用目标的适当实例。这很重要,因为我们绝对需要确定目标需要多少个参数,并且它还启用了诸如检测参数数量不正确的调用之类的便利。

在组装了一系列参数之后,call_user_func_array像你最初打算的那样使用。

这种方法基于与invisal 使用的相同的想法,但有一个重要区别:使用反射允许您始终正确确定要传递的参数数量(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);
}

看到它在行动

于 2013-08-28T20:23:55.797 回答