PHP 的 call_user_func_array 方法是否效率很低?

Laravel 5.1Facade 类 的 __callStatic 方法代码如下:

public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    switch (count($args)) {
        case 0:
            return $instance->$method();

        case 1:
            return $instance->$method($args[0]);

        case 2:
            return $instance->$method($args[0], $args[1]);

        case 3:
            return $instance->$method($args[0], $args[1], $args[2]);

        case 4:
            return $instance->$method($args[0], $args[1], $args[2], $args[3]);

        default:
            return call_user_func_array([$instance, $method], $args);
    }
}

为什么不直接写成:

public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    return call_user_func_array([$instance, $method], $args);
}
阅读 2.9k
评论 更新于 2017-11-21
    8 个回答
    fevin
    • 1.1k

    答:call_user_func_array 效率偏低。

    基准测试如下

    对比范围

    • 直接调用

    • 变量函数调用

    • call_user_func 调用

    • call_user_func_array 调用

    测试结果

    clipboard.png
    我们可以看到,call_user_func_array 所用时间为:1.1608240604401s

    测试过程

    测试代码如下:

    <?php
    error_reporting(E_ALL | E_STRICT);
    define('ITERATIONS', 2000000);
    
    class Bench
    {
        private $bench_name;
        private $start_time;
        private $end_time;
    
        public function start($name)
        {
            $this->bench_name = $name;
            $this->start_time = microtime(true);
        }
    
        public function end()
        {
            $this->end_time = microtime(true);
            echo "Call style: " . $this->bench_name . '; ' . ($this->end_time - $this->start_time) . " seconds". PHP_EOL;
        }
    }
    
    class Test
    {
        public function test($a, $b, $c)
        {
            return;
        }
    }
    
    
    $bench = new Bench();
    $test = new Test();
    $arg = [1, 2, 3];
    $func_name = 'test';
    
    $bench->start('normal');
    for ($i=0; $i < ITERATIONS; ++$i) {
        $test->test($arg[0], $arg[1], $arg[2]);
    }
    $bench->end();
    
    $bench->start('var_function');
    for ($i=0; $i < ITERATIONS; ++$i) {
        $test->$func_name($arg[0], $arg[1], $arg[2]);
    }
    $bench->end();
    
    $bench->start('call_user_func');
    for ($i=0; $i < ITERATIONS; ++$i) {
        call_user_func([$test, $func_name], $arg[0], $arg[1], $arg[2]);
    }
    $bench->end();
    
    $bench->start('call_user_func_array');
    for ($i=0; $i < ITERATIONS; ++$i) {
        call_user_func_array([$test, $func_name], $arg);
    }
    $bench->end();
    评论 赞赏 2017-11-21

      题主是不是看错了或者看的是修改过的源码,原始laravel中并没有发现存在这些代码,能否标出具体的laravel版本和文件路径

      我看到的laravel的 Facade 类中代码是这样的

      /**
           * Handle dynamic, static calls to the object.
           *
           * @param  string  $method
           * @param  array   $args
           * @return mixed
           *
           * @throws \RuntimeException
           */
          public static function __callStatic($method, $args)
          {
              $instance = static::getFacadeRoot();
      
              if (! $instance) {
                  throw new RuntimeException('A facade root has not been set.');
              }
      
              return $instance->$method(...$args);
          }
      评论 赞赏 2017-11-20

        这问题我也纳闷. mark, 看会不会遇到能解答这个问题的人.

        评论 赞赏 2017-11-20

          我刚刚创建了一个laravel的项目

          "laravel/framework": "5.5.*"

          然后看了源码:

          /**
           * Handle dynamic, static calls to the object.
           *
           * @param  string  $method
           * @param  array   $args
           * @return mixed
           *
           * @throws \RuntimeException
           */
          public static function __callStatic($method, $args)
          {
              $instance = static::getFacadeRoot();
          
              if (! $instance) {
                  throw new RuntimeException('A facade root has not been set.');
              }
          
              return $instance->$method(...$args);
          }
          评论 赞赏 2017-11-20

            额 题主是什么版本的啊? 感觉跟楼上就几个同学看到的一样啊

            评论 赞赏 2017-11-21

              三个...不定参数的写法是php 5.6版本才有的新特性,我猜可能框架5.1版本的时候还没支持php新特性的吧

              评论 赞赏 2017-11-21

                光看题主的问题, php 中的 call_user_func_array() 并不慢, 相反这个比普通的函数执行更快, 因为这个在 php7 添加为语言结构(其他语言结构, 比如 echo), 这个信息可以从鸟哥的博客获取到.

                另外 laravel 中 facade 中多这样一层封装, 其实是使用 __callStatic() 函数实现, 这个稍微会有一点性能损失.

                评论 赞赏 2017-11-21

                  Iterations: 100 000
                  Averaged over: 10
                  PHP 5.6.30 (cli) (built: Jan 18 2017 19:47:28)

                  Overall Average
                  Invocation Time (s) Delta (s) %
                  directFunction 0.0089 -0.0211 -70.19
                  directStatic 0.0098 -0.0202 -67.39
                  directLambda 0.0109 -0.0191 -63.52
                  directInstance 0.0116 -0.0184 -61.31
                  directClosure 0.0150 -0.0150 -50.15
                  Invoke 0.0282 -0.0018 -6.13
                  call_user_func 0.0300
                  ClosureFactory 0.0316 +0.0016 +5.20
                  assignedClosureFactory 0.0328 +0.0028 +9.28
                  call_user_func_array 0.0399 +0.0099 +33.02
                  InvokeCallUserFunc 0.0418 +0.0118 +39.17
                  directImplementation 0.0475 +0.0175 +58.28

                  Iterations: 100 000
                  Averaged over: 10
                  PHP 7.1.2 (cli) (built: Feb 14 2017 21:24:45)

                  Overall Average
                  Invocation Time (s) Delta (s) %
                  directFunction 0.0043 -0.0096 -68.92
                  directStatic 0.0050 -0.0089 -64.04
                  directInstance 0.0058 -0.0081 -58.22
                  directLambda 0.0063 -0.0075 -54.44
                  directClosure 0.0081 -0.0058 -41.57
                  call_user_func 0.0139
                  call_user_func_array 0.0147 +0.0008 +5.84
                  Invoke 0.0187 +0.0048 +34.61
                  ClosureFactory 0.0207 +0.0069 +49.43
                  assignedClosureFactory 0.0219 +0.0080 +57.75
                  directImplementation 0.0232 +0.0094 +67.53
                  InvokeCallUserFunc 0.0264 +0.0126 +90.67
                  评论 赞赏 2018-03-25
                    撰写回答

                    登录后参与交流、获取后续更新提醒