php 为什么应该更喜欢 call_user_func_array 而不是函数的常规调用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18526060/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
why should one prefer call_user_func_array over regular calling of function?
提问by codepixlabs
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
As I can see both regular one and call_user_func_array
method both outputs same, then why should one prefer it?
正如我可以看到常规的和 call_user_func_array
方法的输出相同,那么为什么要更喜欢它呢?
In which scenario regular calling method will fail but call_user_func_array
will not?
在哪种情况下常规调用方法会失败但call_user_func_array
不会?
Can I get any such example?
我能得到任何这样的例子吗?
Thank you
谢谢
回答by deceze
You have an array with the arguments for your function which is of indeterminate length.
$args = someFuncWhichReturnsTheArgs(); foobar( /* put these $args here, you do not know how many there are */ );
The alternative would be:
switch (count($args)) { case 1: foobar($args[0]); break; case 2: foobar($args[0], $args[1]); break; ... }
Which is not a solution.
您有一个包含不确定长度的函数参数的数组。
$args = someFuncWhichReturnsTheArgs(); foobar( /* put these $args here, you do not know how many there are */ );
替代方案是:
switch (count($args)) { case 1: foobar($args[0]); break; case 2: foobar($args[0], $args[1]); break; ... }
这不是解决方案。
The use case for this may be rare, but when you come across it you needit.
这种用例可能很少见,但是当您遇到它时,您就需要它。
回答by Ja?ck
In which scenario regular calling method will fail but call_user_func_array will not ?
在哪种情况下常规调用方法会失败但 call_user_func_array 不会?
If you don't know beforehand how many arguments you're going to pass to your function, it would be advisable to use call_user_func_array()
; the only alternative is a switch
statement or a bunch of conditions to accomplish a predefined subset of possibilities.
如果您事先不知道要传递给函数的参数数量,建议使用call_user_func_array()
; 唯一的选择是一个switch
语句或一组条件来完成预定义的可能性子集。
Another scenario is where the function to be called is not known beforehand, e.g. array($obj, 'method')
; this is also where you could use call_user_func()
.
另一种情况是事先不知道要调用的函数,例如array($obj, 'method')
;这也是您可以使用call_user_func()
.
$fn = array($obj, 'method');
$args = [1, 2, 3];
call_user_func_array($fn, $args);
Note that using call_user_func_*
functions can't be used to call private or protected methods.
请注意, usingcall_user_func_*
函数不能用于调用私有或受保护的方法。
The alternative to all of this is to make your functions accept an array as its only argument:
所有这些的替代方法是让您的函数接受一个数组作为其唯一参数:
myfn([1, 2, 3]);
However, this eliminates the possibility to type-hint each argument in your function declaration and is generally considered a code smell.
但是,这消除了在函数声明中对每个参数进行类型提示的可能性,并且通常被认为是代码异味。
回答by elclanrs
You should prefer calling the function as you'd do regularly. Use call_user_func_array
with dynamic arguments. For example:
您应该更喜欢像平常那样调用该函数。call_user_func_array
与动态参数一起使用。例如:
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"
回答by Warbo
call_user_func_array
performs "uncurrying", which is the opposite of "currying".
call_user_func_array
执行“uncurrying”,这与“currying”相反。
The following applies to all of PHP's "callables" (named functions, closures, methods, __invoke
, etc.), so for simplicity let's ignore the differences and just focus on closures.
以下适用于所有 PHP 的“可调用对象”(命名函数、闭包、方法__invoke
等),因此为简单起见,让我们忽略差异,只关注闭包。
If we want to accept multiple arguments, PHP lets us do that with 3 different APIs. The usual way is this:
如果我们想接受多个参数,PHP 允许我们使用 3 个不同的 API 来实现。通常的方法是这样的:
$usual = function($a, $b, $c, $d) {
return $a + $b + $c + $d;
};
$result = $usual(10, 20, 30, 40); // $result == 100
Another way is called curriedform:
另一种方式称为咖喱形式:
$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
The advantage is that all curried functions can be called in the same way: give them one argument.
优点是所有柯里化函数都可以以相同的方式调用:给它们一个参数。
If more arguments are required, more curried functions are returned, which 'remember' the previous arguments. This allows us to pass in some arguments now and the rest later.
如果需要更多参数,则返回更多柯里化函数,它们“记住”先前的参数。这允许我们现在传入一些参数,其余的稍后传入。
There are some problems with this:
这有一些问题:
- Clearly it's very tedious to write and call functions in this way.
- If we provide curried functions, they'll be awkward whenever their 'memory' ability isn't needed.
- If we rely on the 'memory' ability of curried functions, we'll be disappointed when other people's code doesn't provide it.
- 显然,以这种方式编写和调用函数是非常乏味的。
- 如果我们提供柯里化函数,当不需要它们的“记忆”能力时它们会很尴尬。
- 如果我们依赖柯里化函数的“记忆”能力,当其他人的代码没有提供它时,我们会感到失望。
We can fix all of these issues by using a conversion function(disclaimer: that's my blog). This lets us write and call our functions in the usual way, but gives them the same 'memory' ability as if they were curried:
我们可以通过使用转换函数来解决所有这些问题(免责声明:那是我的博客)。这让我们可以以通常的方式编写和调用我们的函数,但赋予它们与柯里化相同的“记忆”能力:
$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
The third way is called uncurriedand takes all of its arguments in one:
第三种方式称为uncurred并将其所有参数合二为一:
$uncurried = function($args) {
return $args[0] + $args[1] + $args[2] + $args[3];
};
$result = $uncurried([10, 20, 30, 40]); // $result == 100
Just like with curried functions, uncurried functions can all be called with one argument, although this time it's an array. We still face the same compatibility problems as curried functions: if we choose to use uncurried functions, we can't rely on everyone else choosing the same. Hence we also need a conversion function for uncurrying. That's what call_user_func_array
does:
就像柯里化函数一样,非柯里化函数都可以用一个参数调用,尽管这次它是一个数组。我们仍然面临与柯里化函数相同的兼容性问题:如果我们选择使用非柯里化函数,我们不能依赖其他人选择相同的函数。因此,我们还需要一个用于取消柯里化的转换函数。这就是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
Interestingly, we can get rid of that extra function($args)
wrapper (a process known as "eta-reduction") by currying call_user_func_array
:
有趣的是,我们可以function($args)
通过 currying来摆脱那个额外的包装器(一个称为“eta-reduction”的过程)call_user_func_array
:
$uncurried = curry('call_user_func_array', $usual);
$result = $uncurried([10, 20, 30, 40]); // $result == 100
Unfortunately call_user_func_array
isn't as smart as curry
; it won't automatically convert between the two. We can write our own uncurry
function which has that ability:
不幸的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
These conversion functions show that PHP's "usual" way of defining functions is actually redundant: if we replaced PHP's "usual" functions with 'smart' curried or uncurried ones, lots of code would carry on working. If we did that, it's better to curry everything and selectively uncurry as needed, since that's easier than going the other way around.
这些转换函数表明 PHP 定义函数的“通常”方式实际上是多余的:如果我们将 PHP 的“通常”函数替换为“智能”柯里化或非柯里化的函数,许多代码将继续工作。如果我们这样做,最好将所有内容都进行咖喱处理,并根据需要选择性地取消咖喱处理,因为这比反过来更容易。
Unfortunately, some things which expect a variable number of arguments using func_get_args
would break, as well as functions with default argument values.
不幸的是,一些期望使用可变数量参数的东西func_get_args
会中断,以及具有默认参数值的函数。
Interestingly, default values are just a special form of currying. We could mostly do without them if we put those arguments firstinstead of last, and provided a bunch of alternative definitions which curry in the defaults. For example:
有趣的是,默认值只是一种特殊的柯里化形式。如果我们把这些参数放在最前面而不是最后,我们几乎可以不用它们,并提供一堆替代定义,这些定义包含在默认值中。例如:
$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
回答by Thoracius Appotite
As of php 5.6, to pass an arrayinstead of an argument listto a function simply precede the array with an ellipsis (this is called "argument unpacking").
从 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);
The difference between call_user_func_array()
and variable functionsas of php 5.6 is that variable functions do not allow you to call a static method:
从php 5.6 开始,变量函数call_user_func_array()
和变量函数之间的区别在于变量函数不允许您调用静态方法:
$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 adds the ability to call static methods via a variable function, so as of php 7 thisdifference no longer exists. In conclusion, call_user_func_array()
gives your code greater compatibility.
php 7 增加了通过变量函数调用静态方法的能力,所以从 php 7 开始,这种差异不再存在。总之,call_user_func_array()
给你的代码更大的兼容性。
回答by Harshit
<?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
?>