その函数は引数をいくつ要求するのか、つまり「函数の引数の数」のことを、一般的にarity(アリティ)と呼びます。
これはJavaScriptやRubyでは比較的容易に取得できます。
> function f (a) {}
> f.length
1
> function g (a, b) {}
> g.length
2
> function k (a, b, c) {}
> k.length
3
> (function(a){}).length
1
> (function(a,b){}).length
2
> (function(a,b,c){}).length
3
Rubyだとこんな感じ
> lambda{|a|}.arity
=> 1
> lambda{|a, b|}.arity
=> 2
> lambda{|a, b, c|}.arity
=> 3
> def f (a); end
> method(:f).arity
=> 1
> def g (a, b); end
> method(:g).arity
=> 2
> def h (a, b, c); end
> method(:h).arity
=> 3
じゃあPHPは? 函数の内部からは実行時に受け取った個数をfunc_num_args
で参照することはできますが、欲しいのはコレジャナイです。飽くまで 定義された仮引数の個数 を、 実行することなく知りたいのです。
じゃあPHPはどうすんのさ
リフレクションです… PHPのリフレクションを利用するのです…
函数
<?php
$f = function($a, $b){};
$c = new ReflectionFunction($f);
var_dump($c->getNumberOfParameters());
$d = new ReflectionFunction("strcmp");
var_dump($d->getNumberOfParameters());
ReflectionFunctionを利用します。
これでユーザー定義函数もビルトイン函数も無名函数もばっちりです。やったね。
メソッド
<?php
class Hoge
{
public static function fuga ($a, $b)
{
return "fuga";
}
public function piyo ($a, $b, $c)
{
return "piyo";
}
}
$c = new ReflectionClass("Hoge");
var_dump($c->getMethod("fuga")->getNumberOfParameters());
var_dump($c->getMethod("piyo")->getNumberOfParameters());
$d = new ReflectionClass(new Hoge);
var_dump($d->getMethod("fuga")->getNumberOfParameters());
var_dump($d->getMethod("piyo")->getNumberOfParameters());
ReflectionObjectとReflectionMethodを利用します。
ReflectionClass::__constructに渡す値はインスタンスオブジェクトでもクラス名を表す文字列でも大丈夫です。
まとめ
めんどくさいのでcallbackの形式をまとめてサポートする函数を書いた。
<?php
/**
* Returns an indication of the number of arguments accepted by a callable.
*
* @param callable $callable
* @return int|null
*/
function arity (callable $callable)
{
try {
if (is_array($callable) || (is_string($callable) && strpos($callable, '::') !== false)) {
list($class, $method) = is_string($callable) ? explode('::', $callable) : $callable;
$reflection = (new ReflectionClass($class))->getMethod($method);
} else {
$reflection = new ReflectionFunction($callable);
}
return $reflection->getNumberOfParameters();
} catch (Exception $e) {
return null;
}
}
test_arity('strpos', 3);
test_arity('hoge', 3);
test_arity('Datetime::createFromFormat', 3);
test_arity(function($a, $b, $c){}, 3);
test_arity(array(new DateTime, 'setTime'), 3);
function hoge($a, $b, $c) {}
function test_arity($value, $expected)
{
$actual = arity($value);
echo str_replace(PHP_EOL, '', var_export($value, true));
echo ' is ' . ($actual !== $expected ? 'in' : '') . 'valid.' . PHP_EOL;
}
煮るなり焼くなりお好きにどうぞ or You just DO WHAT THE FUCK YOU WANT TO.
追記
1
忘れてた。PHP: ReflectionFunctionAbstract::getNumberOfParameters - ManualのほかにもPHP: ReflectionFunctionAbstract::getNumberOfRequiredParameters - Manualとかあるんで、お好みに合せてどうぞ。
2
タイプヒンティングのcallbackが追加されたのは5.4なので、5.3では動きません(のでバージョン指定を5.3から5.4に変更)。 @t_cyrill ありがたうございます。