为什么应该更喜欢call_user_func_array通常的调用函数?

function foobar($arg, $arg2) { echo __FUNCTION__, " got $arg and $arg2\n"; } foobar('one','two'); // OUTPUTS : foobar got one and two call_user_func_array("foobar", array("one", "two")); // // OUTPUTS : foobar got one and two 

正如我可以看到普通的一个和call_user_func_array方法都输出相同的,那么为什么要喜欢它?

在哪种情况下,普通调用方法会失败,但call_user_func_array不会?

我能得到这样的例子吗?

谢谢

  1. 你有一个与你的函数的参数是不确定的长度的数组。

     $args = someFuncWhichReturnsTheArgs(); foobar( /* put these $args here, you do not know how many there are */ ); 

    替代scheme是:

     switch (count($args)) { case 1: foobar($args[0]); break; case 2: foobar($args[0], $args[1]); break; ... } 

    这不是一个解决scheme。

这个用例可能很less见,但是当你遇到它时,你需要它。

在哪种情况下,普通调用方法会失败,但call_user_func_array不会?

如果事先不知道将要传递给函数的参数有多less,build议使用call_user_func_array() ; 唯一的select是一个switch语句或一组条件来完成预定义的可能性子集。

另一种情况是被调用的函数事先不知道,例如array($obj, 'method') ; 这也是你可以使用call_user_func()

 $fn = array($obj, 'method'); $args = [1, 2, 3]; call_user_func_array($fn, $args); 

请注意,使用call_user_func_*函数不能用于调用私有或受保护的方法。

所有这些的替代方法是让你的函数接受一个数组作为唯一的参数:

 myfn([1, 2, 3]); 

但是,这消除了在函数声明中键入提示每个参数的可能性,并且通常被认为是代码异味。

您应该更喜欢定期调用函数。 使用call_user_func_array和dynamic参数。 例如:

 function func(arg1, arg2, arg3) { return "$arg1, $arg2, $arg3"; } func(1, 2, 3); //=> "1, 2, 3" $args = range(5,7); // dynamic arguments call_user_func_array('func', $args); //=> "5, 6, 7" 

call_user_func_array执行“uncurrying”,这是“currying”的反义词。

以下内容适用于所有PHP的“可调用”(命名函数,闭包,方法, __invoke等),所以为了简单起见,我们忽略差异,只关注闭包。

如果我们想要接受多个参数,PHP可以让我们用3个不同的API来实现。 通常的方法是这样的:

 $usual = function($a, $b, $c, $d) { return $a + $b + $c + $d; }; $result = $usual(10, 20, 30, 40); // $result == 100 

另一种方式叫做咖喱forms:

 $curried = function($a) { return function($b) use ($a) { return function($c) use ($a, $b) { return function($d) use ($a, $b, $c) { return $a + $b + $c + $d; }; }; }; }; $result = call_user_func( call_user_func( call_user_func( $curried(10), 20), 30), 40); // $result == 100 

好处是可以用同样的方式调用所有curried函数:给它们一个参数。

如果需要更多参数,则返回更多的curried函数,它们会“记住”以前的参数。 这使我们现在可以通过一些论点,其余的则在后面。

这有一些问题:

  • 显然,以这种方式编写和调用函数是非常繁琐的。
  • 如果我们提供curry函数,只要不需要它们的“记忆”能力,它们就会变得笨拙。
  • 如果我们依靠咖喱函数的“记忆”能力,当其他人的代码不提供时,我们会感到失望。

我们可以通过使用转换function来解决所有这些问题(免责声明:这是我的博客)。 这可以让我们以通常的方式编写和调用我们的function,但是给予他们相同的“记忆”能力,就好像他们是咖喱一样:

 $curried = curry(function($a, $b, $c, $d) { return $a + $b + $c + $d; }); $result1 = $curried(10, 20, 30, 40); // $result1 = 100 $result2 = call_user_func($curried(10, 20), 30, 40); // $result2 = 100 

第三种方法叫做“ 不安全” ,把所有的论点都集中在一个中:

 $uncurried = function($args) { return $args[0] + $args[1] + $args[2] + $args[3]; }; $result = $uncurried([10, 20, 30, 40]); // $result == 100 

就像使用curried函数一样,uncurried函数可以用一个参数来调用,尽pipe这次它是一个数组。 我们仍然面临着与curried函数相同的兼容性问题:如果我们select使用uncurried函数,我们不能依赖于其他人select相同的函数。 因此我们也需要一个转换函数来实现uncurrying。 这就是call_user_func_array所做的事情:

 $uncurried = function($args) use ($usual) { return call_user_func_array($usual, $args); }; $result1 = $usual(10, 20, 30, 40); // $result1 = 100 $result2 = $uncurried([10, 20, 30, 40]); // $result2 = 100 

有趣的是,我们可以通过currying call_user_func_array去除额外的function($args)包装器(一个称为“eta-reduction”的过程):

 $uncurried = curry('call_user_func_array', $usual); $result = $uncurried([10, 20, 30, 40]); // $result == 100 

不幸的是, call_user_func_array没有curry那么聪明; 它不会自动在两者之间转换。 我们可以编写我们自己的具有这种能力的uncurry函数:

 function uncurry($f) { return function($args) use ($f) { return call_user_func_array( $f, (count(func_get_args()) > 1)? func_get_args() : $args); }; } $uncurried = uncurry($usual); $result1 = $uncurried(10, 20, 30, 40); // $result1 == 100 $result2 = $uncurried([10, 20, 30, 40]); // $result2 == 100 

这些转换函数表明,PHP定义函数的“常用”方式实际上是多余的:如果用“聪明”的curry或uncurried代替PHP的“常用”函数,很多代码将继续工作。 如果我们这样做了,最好咖啡一杯咖啡,根据需要有select性地放弃,因为这比其他方式更容易。

不幸的是,一些使用func_get_args的参数数量可能会变多,而且会有一些缺省参数值的函数。

有趣的是,默认值只是一种特殊的forms。 如果我们把这些参数放在第一个而不是最后一个,我们大多可以没有它们,并且提供了一堆用于默认值的替代定义。 例如:

 $defaults = function($a, $b, $c = 30, $d = 40) { return $a + $b + $c + $d; }; $def1 = $defaults(10, 20, 30, 40); // $def1 == 100 $def2 = $defaults(10, 20, 30); // $def2 == 100 $def3 = $defaults(10, 20); // $def3 == 100 $curried = function($d, $c, $a, $b) { return $a + $b + $c + $d; }; $curriedD = $curried(40); $curriedDC = $curriedD(30); $cur1 = $curried(10, 20, 30, 40); // $cur1 == 100 $cur2 = $curriedD(10, 20, 30); // $cur2 == 100 $cur3 = $curriedDC(10, 20); // $cur3 == 100 

从PHP 5.6开始,将一个数组而不是一个参数列表传递给一个函数只需要在数组前加上一个省略号(称为“参数解包”)。

 function foo($var1, $var2, $var3) { echo $var1 + $var2 + var3; } $array = [1,2,3]; foo(...$array); // 6 // same as call_user_func_array('foo',$array); 

call_user_func_array()和php 5.6的variables函数之间的区别是,variables函数不允许你调用静态方法:

 $params = [1,2,3,4,5]; function test_function() { echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\r\n"; } // Normal function as callback $callback_function = 'test_function'; call_user_func_array($callback_function,$params); // 1+2+3+4+5=15 $callback_function(...$params); // 1+2+3+4+5=15 class TestClass { static function testStaticMethod() { echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\r\n"; } public function testMethod() { echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\r\n"; } } // Class method as callback $obj = new TestClass; $callback_function = [$obj,'testMethod']; call_user_func_array($callback_function,$params); // 1+2+3+4+5=15 $callback_function(...$params); // 1+2+3+4+5=15 // Static method callback $callback_function = 'TestClass::testStaticMethod'; call_user_func_array($callback_function,$params); // 1+2+3+4+5=15 $callback_function(...$params); // Fatal error: undefined function 

PHP 7增加了通过一个variables函数调用静态方法的能力,所以对于PHP 7来说, 这个差别不再存在。 总之, call_user_func_array()为您的代码提供了更好的兼容性。

 <?php class Demo { public function function1() { echo 'in function 1'; } } $obj = new Demo(); $function_list = get_class_methods('Demo'); print_r($function_list); //Array ( [0] => function1 ) call_user_func_array(array($obj, $function_list[0]), array()); // Output => in function 1 ?>