LoginSignup
5
2

More than 5 years have passed since last update.

PHPで名前付き引数の部分適用を実現する

Last updated at Posted at 2017-01-27

久しぶりに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)
5
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2