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 に答える
1

問題

参照を扱わないため、これを簡単に行うことはできfunc_get_argsません。また、参照を扱う代替手段もありません。

アイデア

既知の最大数の引数に制限しても構わないと思っていて、ダーク アーツを使用することを気にしない場合は、すべての場合に正しく機能すると私が信じている恐ろしい回避策があります。

最初に、可能な数のパラメーターを受け入れるように呼び出し元を宣言します。すべてのパラメーターは参照によって受け入れられ、デフォルト値があります (正確なデフォルト値は実際には重要ではありません)。

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

次に、内部で、invoke処理している callable のタイプを決定します。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 に答える