【问题标题】:When to use call_user_func_array何时使用 call_user_func_array
【发布时间】:2017-12-24 20:14:25
【问题描述】:

我在 stack 上阅读了其他关于使用 call_user_func_array 与仅调用函数的答案,但我仍然无法收集何时应该使用前者。我知道当您不知道传递了多少个参数时,您可能想使用call_user_func_array,因此您可以这样做:$args = func_get_args(); ...但如果要使用它们,您是否总是需要知道参数在函数中?

以下两项工作,我假设第一项的开销较小。

$format = new Foo;
$method = 'somemethod';
$arg = 'somevalue';
if (method_exists($format, $method)) {
    return $format->$method($arg);
}

return call_user_func_array(array($format, $method), array($arg));

什么时候可以真正从使用call_user_func_array 中受益?

【问题讨论】:

  • 我已经在静态类中看到并使用了它,其中已经导入了具体类的实例,并且希望通过静态方法访问具体类。
  • 当您的 PHP 版本不支持存储函数名称调用时(5.4?)。当不支持可变参数(5.6)时,也经常使用func_get_args。但是到目前为止,我已经看到这是一个偏好问题。保持一致是关键。
  • 现在通过参数解包它有点使这个函数静音,因为这是答案中的 argumentstackoverflow.com/questions/18526060/… 假设它只是归结为如果你想支持旧php 版本。
  • @Chay22 究竟什么是“存储函数名称调用”?

标签: php call-user-func-array


【解决方案1】:

call_user_func_array 非常有用的一个例子

假设您希望访问一个对象,但通过静态方法,如下所示:

Helper::load();

好吧,这本身是行不通的,因为如果您查看具体类,它没有静态方法:

class Helper {

  public function load()
  {
      // Do some loading ...
  }

  public function aFunctionThatNeedsParameters( $param1, $param2 )
  {
      // Do something, and $param1 and $param2 are required ...
  }
}

所以在第二个类中,我们可以这样做,因为上面的 Helper 类被加载到依赖注入容器中(请注意,这两个 Helper 类的名称相同,但会在不同的命名空间中):

class Helper extends DIContainer {

  public static $helper_instance = NULL;

  public static function get_instance()
  {
    if( is_null( self::$helper_instance ) )
    {
      self::$helper_instance = parent::$container['helper'];
    }

    return self::$helper_instance;
  }

  public static function __callStatic( $method, $params )
  {
    $obj = self::get_instance();

    return call_user_func_array( [ $obj, $method ], $params );
  }
}

问题是,可能还有另一个方法需要参数,即使我们的加载方法没有任何参数。

所以在这种情况下我们可以使用Helper::load(),也可以使用Helper::aFunctionThatNeedsParameters( $param1, $param2 )

我认为这在知道静态类通常不合适的 PHP 框架中被大量使用,但他们希望能够像调用静态方法一样调用方法。我希望这是有道理的。

【讨论】:

  • 有趣!为什么要将非静态方法称为静态方法。它与依赖注入有关吗?
  • 考虑一下 Laravel,可以说是最流行的 PHP 框架,他们称这些为“Facades”,laravel.com/docs/5.5/facades。他们说这样做的好处是“提供了简洁、富有表现力的语法的好处,同时保持了比传统静态方法更多的可测试性和灵活性。”是的,这确实与依赖注入有关。
  • 所以基本上,除其他外,它看起来更好
  • 我想。调用静态方式的一个好处是您不必通过有时是一堆类来维护依赖注入链。
【解决方案2】:

David Sklar 在他的PHP Cookbook 中解决了何时使用call_user_func_array 的问题。因此,要关注 OP 的一条评论:

"...如果要使用参数,您是否总是需要知道参数 在函数中?”

这里有一个 call_user_func() 可以派上用场的例子:

<?php

function Test(){
  $args = func_get_args();
  call_user_func_array("printf",$args);
}

Test("Candidates 2014: %s %s %s %s\n","Tom","Debbie","Harry", "Sally");
Test("Candidates 2015: %s %s\n","Bob","Sam","Sarah");

live code

通过 call_user_func_array() 和 func_get_args() 对,用户定义函数 Test() 可以接受可变数量的参数。

此外,对于聚合方法很有用(请参阅 PHP Cookbook p208-210),如以下简化示例所示:

<?php
class Address {
   protected $city;

   public function getCity() {
      return $this->city;
   }
   public function setCity($city) {
      $this->city=$city;
   }
}

class Person {
     protected $name="Tester";
     protected $address;

     public function __construct() {
            $this->address = new Address;
     }
     public function getName(){
            return $this->name;
     }
    public function __call($method, $args){
           if (method_exists($this->address,$method)) {
                return call_user_func_array( array($this->address,$method),$args);
           }
    }
}

  $sharbear = new Person;
  echo $sharbear->setCity("Baltimore");
  echo "Name: ",$sharbear->getName(),"\n";
  echo "City: ",$sharbear->getCity();

live code

附录

从 PHP 5.6 开始,PHP 支持 variadic functionsargument unpacking,因此对于变量参数, call_user_func() 和 func_get_args() 可以替换如下:

<?php

function Test(...$args){

   printf(...$args);
}
$namesT = ["Timmy","Teddy","Theo","Tad"];
$namesB = ["Bobby","Bill","Brad"];

Test("T Names: %s %s %s %s\n",...$namesT);
Test("B Names: %s %s %s\n",...$namesB);

live code

【讨论】:

    【解决方案3】:

    &lt;!-- begin snippet: js hide: false console: true babel: false --&gt;
    <pre>
    I use call_user_func_array when I need to call a function or php function from my ajax enabled page. 
    
    eg.
    in html and javascript:
    
    <form id='my-form'>
    
    <input id='name' value='Wong, Billy'>
    <input id='age' value='50'>
    
    <a href='javascript;' onClick="xjx.query('helloWorld', $('#name').val(), $('#age').val())">Show name and age</a>
    
    <a href='javascript;' onClick="xjx.query('showName', $('#name').val())">Show name</a>
    
    <a href='javascript;' onClick="xjx.query('showAge', $('#age').val())">Show age</a>
    
    <a href='javascript;' onClick="xjx.post('#my-form', 'showPost', 'live long and prosper')">Show age</a>
    
    </form>
    
    xjx.query and xjx.post, personal javascript functions of mine will then format and submit to the php page via ajax
    
    in php
    $method = $_POST['method'];
    $args = $_POST['args'];
    
    call_user_func_array($method, $args)
    
    function helloWorld($name, $age) {
        ....
    }
    
    function showName($name) {
        ....
    }
    
    function showAge($age) {
        ....
    }
    
    function showPost($f, $message) {
       print_r($f);
       echo $message;
    }
    
    my libs are found in github with xjx as the project name
    </pre>
    

    【讨论】:

      【解决方案4】:

      假设我们有几个类

      class Request 
      {
          private $req;
      
          public function __construct($req)
          {
              $this->req = $req;
          }
      
          public function __call($name, $arg)
          {
              return \call_user_func_array(array($this->req,$name), $arg);
          }
      }
      
      class Get 
      {
          public function load($url)
          {
              // fire an http request
          }
      }
      
      class Post 
      {
          public function load($url)
          {
              // fire an http request
          }
      
          public function attachHeader($header=[])
          {
              // attach an header to the request
          }
      }
      
      $request_1 = new Request(new Get());
      $request_1->load($url);
      
      $request_2 = new Request(new Post());
      $request_2->attachHeader($header_data);
      $request_2->load($url);
      

      在这里你可以看到我们可以使用call_user_func_array() 调用attachHeader() 方法 没有任何问题。

      希望对你有帮助

      【讨论】:

        猜你喜欢
        • 2016-01-04
        • 1970-01-01
        • 1970-01-01
        • 2010-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-27
        • 1970-01-01
        相关资源
        最近更新 更多