why should one prefer call_user_func_array over regular calling of function?
1 2 3 4 5 6 7 | function foobar($arg, $arg2) { echo __FUNCTION__," got $arg and $arg2\ "; } foobar('one','two'); // OUTPUTS : foobar got one and two call_user_func_array("foobar", array("one","two")); // // OUTPUTS : foobar got one and two |
正如我所看到的,常规的一个方法和
在哪种情况下常规调用方法会失败而
我能得到任何这样的例子吗?
谢谢您
您有一个数组,其中包含函数的参数,其长度不
1 2 3 | $args = someFuncWhichReturnsTheArgs(); foobar( /* put these $args here, you do not know how many there are */ ); |
替代方法是:
1 2 3 4 5 6 7 8 9 | switch (count($args)) { case 1: foobar($args[0]); break; case 2: foobar($args[0], $args[1]); break; ... } |
这不是解决方案。
这种情况的用例可能很少,但是当您遇到它时,就需要它。
In which scenario regular calling method will fail but call_user_func_array will not ?
如果您事先不知道要传递给函数的参数个数,建议使用
另一种情况是事先不知道要调用的函数,例如,
1 2 3 |
请注意,使用
1 | myfn([1, 2, 3]); |
但是,这消除了在函数声明中键入提示每个参数的可能性,并且通常被认为是代码异味。
您应该更喜欢像通常那样调用该函数。将
1 2 3 4 5 6 7 8 | 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" |
以下内容适用于所有PHP \\ " callables \\\\"(命名函数,闭包,方法,
如果要接受有多个参数,PHP使我们可以使用3种不同的API来实现。通常的方式是:
1 2 3 4 | $usual = function($a, $b, $c, $d) { return $a + $b + $c + $d; }; $result = $usual(10, 20, 30, 40); // $result == 100 |
另一种方式称为咖喱形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $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 |
优点是所有咖喱函数都可以以相同的方式调用:给它们一个参数。
如果需要更多参数,则会返回更多咖喱函数,这些函数"记住"先前的参数。这使我们现在可以传递一些参数,以后再传递其他参数。
存在一些问题:
- 很显然,在此编写和调用函数非常繁琐
- 如果我们提供咖喱函数,那么当他们不需要'记忆'功能时,他们就会很尴尬。
- 如果我们依靠'记忆'库函数的功能,当其他人的代码不提供时,我们会感到失望。
我们可以使用转换函数来解决所有这些问题(免责声明: \\是我的博客)。这使我们能够以通常的方式编写和调用我们的函数,但赋予它们与"咖喱"相同的"记忆"能力:
1 2 3 4 5 | $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 |
第三种方式称为"非咖喱"并采用其所有功能一个参数中的参数:
1 2 3 4 | $uncurried = function($args) { return $args[0] + $args[1] + $args[2] + $args[3]; }; $result = $uncurried([10, 20, 30, 40]); // $result == 100 |
就像使用咖喱函数一样,非咖喱函数都可以用一个参数调用,尽管这次是一个数组。我们仍然面临与咖喱函数相同的兼容性问题:如果我们选择使用非咖喱函数,我们将不能依赖其他所有人选择相同的功能。因此,我们还需要一个用于转换的函数。这就是
1 2 3 4 5 | $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 |
有趣的是,我们可以摆脱多余的
1 2 3 | $uncurried = curry('call_user_func_array', $usual); $result = $uncurried([10, 20, 30, 40]); // $result == 100 |
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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的\\\\"通常\\\\"定义函数的方式实际上是多余的:我们用\\'smart \\'咖喱或非咖喱函数替换了PHP的\\\\"通常\\\\"函数,许多代码将继续工作。如果这样做的话,最好咖喱所有内容并根据需要有选择地选择非curry,因为这样做比其他方法更容易。
不幸的是,有些事情期望使用
有趣的是,默认值只是一种特殊的currying形式。如果我们将这些参数放在第一位而不是最后一位,并且提供了一系列替代定义(默认为默认值),我们几乎可以不用它们。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $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开始,要将数组而不是参数列表传递给函数,只需在数组前面加上省略号(这称为" argument unpacking ")。
1 2 3 4 5 6 7 8 | function foo($var1, $var2, $var3) { echo $var1 + $var2 + var3; } $array = [1,2,3]; foo(...$array); // 6 // same as call_user_func_array('foo',$array); |
自php 5.6起
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | $params = [1,2,3,4,5]; function test_function() { echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\ \ "; } // 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())."\ \ "; } public function testMethod() { echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\ \ "; } } // 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增加了通过变量函数调用静态方法的功能,因此PHP 7的这种区别不再存在。总之,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?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 ?> |