久しぶりにPHP小ネタ。知恵袋から転載します。
<?php
/**
* あらゆるcallableを対象にReflectionFunctionまたはReflectionMethodを生成します。
*
* @param callable $callable
* @return \ReflectionFunctionAbstract
*/
function reflect_callable(callable $callable)
{
if (is_string($callable) && strpos($callable, '::')) {
$callable = explode('::', $callable);
} elseif (!$callable instanceof \Closure && is_object($callable)) {
$callable = [$callable, '__invoke'];
}
return $callable instanceof \Closure || is_string($callable)
? new \ReflectionFunction($callable)
: new \ReflectionMethod($callable[0], $callable[1]);
}
/**
* 名前で指定された引数を部分適用したクロージャを返します。
* デフォルト引数でないものはすべて指定されている必要があります。
*
* @param callable $f
* @param array $named_args 名前=>値 の形で引数の配列を渡します。
* @return \Closure
*/
function named_partial(callable $f, array $named_args)
{
return function (...$args) use ($f, $named_args) {
foreach (reflect_callable($f)->getParameters() as $i => $p) {
if (array_key_exists($i, $args)) continue;
$args[$i] = array_key_exists($p->name, $named_args)
? $named_args[$p->name]
: $p->getDefaultValue();
}
return $f(...$args);
};
}
function hoge($a = 1, $b = 2, $c = 3)
{
return $a + $b + $c;
}
// $b = 10 だけ指定しておく
$f = named_partial('hoge', ['b' => 10]);
// $a = 1, $b = 10, $c = 3
var_dump($f()); // int(14)
// $a = 20, $b = 10, $c = 3
var_dump($f(20)); // int(33)