問題
参照を扱わないため、これを簡単に行うことはでき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);
}
実際に見てください。