什么情况下需要在php中使用call_user_func()函数?

4

我不理解call_user_func()这个函数的作用,虽然我知道它是如何工作的,但我不确定为什么它是必需的,以及在什么上下文中在PHP中使用它。 就我而言,为什么不直接调用该函数而不是使用一个函数来调用另一个函数呢?谢谢!


这里有一个很好的比较:https://dev59.com/x3I-5IYBdhLWcg3w9tdw - Jaspreet Chahal
3个回答

2
"call_user_func"使PHP能够将方法和函数视为准一等公民。在像JavaScript这样的函数式语言中,没有这些特殊的工具,因为函数是一个可调用的对象。
PHP越来越接近于拥有这种概念,特别是随着PHP5.3引入的闭包支持。看看我在@deceze答案下面的评论。还有一些其他工具(即变量函数反射和现在的闭包),提供了相同的基本功能。"
关于 call_user_func 最值得注意的一点是它允许你使用统一的接口处理全局函数、静态类和对象。这可能是他们实现函数调用的最接近单一接口的方法。在内部,PHP核心开发人员正在努力使语言中的某种可调用或可调用接口同构化,我相信我们会在PHP5.4或下一个主要版本中看到一个干净的解决方案。

1
我曾经遇到过几种情况,这是非常必要的。例如,当我创建一个项目,允许用户构建编程语言的部分时,系统会在运行之前将“标签”定义包装在函数中以确保安全性。然而,在这样做的过程中,我不能简单地调用这些函数,因为我不知道何时需要调用它们,甚至不知道它们的名称是什么。这时就需要使用call_user_func()函数了...
所以,我相信你现在很困惑。让我解释一下。我的系统所做的是将一些XML转换成PHP/HTML/JS,并使用PHP进行转换。这样可以快速创建GUI(这是目标)。以这个XML为例:
<window id="login-win" title="Access Restricted" width="310" height="186" layout="accordion" layoutConfig="animate:true">
    <panel id="login-panel" title="User Login">
        <form id="login-form" title="Credentials">
            <textbox id="login-uname" label="Username"/>
            <password id="login-pass" label="Password"/>
            <submit text="Login"/>
        </form>
    </panel>
    <panel id="login-register" title="Register">
        Nothing, you can't register!
    </panel>
</window>

每个XML标签都会被读取,然后传递到相应的PHP函数中。该函数将确定如何处理该标签。为了实现这一点,我创建了一个文件,每个文件都以它处理的标签命名。例如,submit标签的文件名为"submit.php"。该文件的内容将被包装在一个生成的函数中,类似于:
function tag_submit($variables, $parent, $children){

    // deal with the data, then echo what is needed

}

该函数的名称将存储在一个数组中,与其相关联的标记名称和其他生成的函数一起。这样做可以使函数只生成一次,并且仅在需要时才创建,节省内存,因为我会进行if(func_exists())调用以确定是否需要它。
但由于这全部都是动态的,用户可能希望添加新标记,例如<date>标记,因此我需要使用call_user_func()来使事情正常工作。如果我不知道名称,我就无法硬编码函数调用。
希望这一切都讲得通。基本上,它是一个很少使用的功能,但它仍然非常有用。

1
命名函数、匿名函数、静态方法、实例方法和带有 "__invoke" 方法的对象统称为 '可调用项'. 如果我有一个可调用项,通过在后面加上括号 "()" (假设它不需要参数)就可以调用它。例如,当我们的可调用项存储在一个变量中时,这种方式是可行的:
$f();  // Call $f

然而,如果我将可调用对象存储在一个对象属性中呢?
$obj = new stdClass;
$obj->my_func = function() {};
$obj->my_func();  // Error: stdClass does not have a "my_func" method

问题在于PHP解析器会因为代码含糊不清而混淆,它可能意味着调用可调用属性或调用方法。PHP选择始终将这种代码视为方法调用,因此我们无法以这种方式调用可调用属性。这就是为什么我们需要使用“call_user_func”的原因:
call_user_func($obj->my_func); // Calls $obj->my_func

有时候PHP无法理解正常的括号语法,例如,PHP目前(5.5)不知道如何调用另一个可调用对象的返回值:

get_a_callable()();  // Parse error, unexpected "("
call_user_func(get_a_callable());  // Calls the return value of get_a_callable

目前也无法直接调用函数定义,这在解决 PHP 缺乏“let”语句的问题时非常有用,例如:

function($x) { echo $x . $x; }('hello');  // Parse error, unexpected "("
call_user_func(function($x) { echo $x . $x; }, 'hello'); // Calls the function

有时候将函数调用/reified/作为'call_user_func'函数也是很有用的。例如,在使用高阶函数如array_map时:

$funcs = [function() { return 'hello'; },
          function() { return 'world'; }];
array_map('call_user_func', $funcs);  // ['hello', 'world']

如果我们不知道需要多少个参数,即我们无法替换自己的“function($f, $x, $y, ...) { return $f($x, $y, ...); }”,它也很有用。

其中一个特别好的定义是部分应用程序,由于call_user_func和call_user_func_array,只需要几行代码就可以实现:

function partial() {
  $args1 = func_get_args();  // $f, $a, $b, $c, ...
  return function() use ($args1) {
    // Returns $f($a, $b, $c, ..., $d, $e, $f, ...)
    $args2 = func_get_args();  // $d, $e, $f, ...
    return call_user_func_array('call_user_func',
                                array_merge($args1, $args2));
  };
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接