• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 可变函数

    PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。

    可变函数不能用于例如echo,print,unset(),isset(),empty(),include,require以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。

    Example #1 可变函数示例

    <?php
    function foo() {
        echo "In foo()<br />\n";
    }
    function bar($arg = '') {
        echo "In bar(); argument was '$arg'.<br />\n";
    }
    // 使用 echo 的包装函数
    function echoit($string)
    {
        echo $string;
    }
    $func = 'foo';
    $func();        // This calls foo()
    $func = 'bar';
    $func('test');  // This calls bar()
    $func = 'echoit';
    $func('test');  // This calls echoit()
    ?>
    

    也可以用可变函数的语法来调用一个对象的方法。

    Example #2 可变方法范例

    <?php
    class Foo
    {
        function Variable()
        {
            $name = 'Bar';
            $this->$name(); // This calls the Bar() method
        }
        function Bar()
        {
            echo "This is Bar";
        }
    }
    $foo = new Foo();
    $funcname = "Variable";
    $foo->$funcname();   // This calls $foo->Variable()
    ?>
    

    当调用静态方法时,函数调用要比静态属性优先:

    Example #3 Variable 方法和静态属性示例

    <?php
    class Foo
    {
        static $variable = 'static property';
        static function Variable()
        {
            echo 'Method Variable called';
        }
    }
    echo Foo::$variable; // This prints 'static property'. It does need a $variable in this scope.
    $variable = "Variable";
    Foo::$variable();  // This calls $foo->Variable() reading $variable in this scope.
    ?>
    

    As of PHP 5.4.0, you can call anycallable stored in a variable.

    Example #4 Complex callables

    <?php
    class Foo
    {
        static function bar()
        {
            echo "bar\n";
        }
        function baz()
        {
            echo "baz\n";
        }
    }
    $func = array("Foo", "bar");
    $func(); // prints "bar"
    $func = array(new Foo, "baz");
    $func(); // prints "baz"
    $func = "Foo::bar";
    $func(); // prints "bar" as of PHP 7.0.0; prior, it raised a fatal error
    ?>
    

    参见is_callable(),call_user_func(),可变变量和function_exists()。

    更新日志

    版本说明
    7.0.0'ClassName::methodName' is allowed as variable function.
    5.4.0 Arrays, which are valid callables, are allowed as variable functions.
    $ wget http://www.php.net/get/php_manual_en.tar.gz/from/a/mirror
    $ grep -l "\$\.\.\." php-chunked-xhtml/function.*.html
    List of functions that accept variable arguments.
    <?php
    array_diff_assoc()
    array_diff_key()
    array_diff_uassoc()
    array()
    array_intersect_ukey()
    array_map()
    array_merge()
    array_merge_recursive()
    array_multisort()
    array_push()
    array_replace()
    array_replace_recursive()
    array_unshift()
    call_user_func()
    call_user_method()
    compact()
    dba_open()
    dba_popen()
    echo()
    forward_static_call()
    fprintf()
    fscanf()
    httprequestpool_construct()
    ibase_execute()
    ibase_set_event_handler()
    ibase_wait_event()
    isset()
    list()
    maxdb_stmt_bind_param()
    maxdb_stmt_bind_result()
    mb_convert_variables()
    newt_checkbox_tree_add_item()
    newt_grid_h_close_stacked()
    newt_grid_h_stacked()
    newt_grid_v_close_stacked()
    newt_grid_v_stacked()
    newt_win_choice()
    newt_win_entries()
    newt_win_menu()
    newt_win_message()
    newt_win_ternary()
    pack()
    printf()
    register_shutdown_function()
    register_tick_function()
    session_register()
    setlocale()
    sprintf()
    sscanf()
    unset()
    var_dump()
    w32api_deftype()
    w32api_init_dtype()
    w32api_invoke_function()
    wddx_add_vars()
    wddx_serialize_vars()
    ?>
    
    A small, but helpful note. If you are trying to call a static function from a different namespace, you must use the fully qualified namespace, even if they have the same top level namespace(s). For example if you have the following class to call:
    <?php
    namespace Project\TestClass;
    class Test {
      static function funcToCall() {
        return "test";
      }
    }
    ?>
    You must call it as:
    <?php
    namespace Project\OtherTestClass;
    class OtherTest {
      static function callOtherFunc() {
        $func = '\Project\TestClass::funcToCall';
        $func();
      }
    }
    ?>
    and not:
    <?php
    class OtherTest {
      static function callOtherFunc() {
        $func = 'TestClass::funcToCall';
        $func();
      }
    }
    ?>
    
    While the documentation suggests that the use of a constant is similar to the use of a variable, there is an exception regarding variable functions. You cannot use a constant as the function name to call a variable function.
    const DEBUGME ='func';
    function func($s) { echo $s. "\n"; }
    DEBUGME('abc'); // results in a syntax error
    $call = DEBUGME;
    $call('abc');     // does the job
    But you can use a constant as an argument to a function. Here's a simple workaround when you need to call a variable constant function: 
    function dynamic($what, $with)
      {
       $what($with);
      }
    dynamic(DEBUGME, 'abc'); 
    This makes sense to me to hide API's and/or long (complicated) static calls.
    Enjoy!
    If you want to call a static function (PHP5) in a variable method:
    Make an array of two entries where the 0th entry is the name of the class to be invoked ('self' and 'parent' work as well) and the 1st entry is the name of the function. Basically, a 'callback' variable is either a string (the name of the function) or an array (0 => 'className', 1 => 'functionName').
    Then, to call that function, you can use either call_user_func() or call_user_func_array(). Examples:
    <?php
    class A {
     protected $a;
     protected $c;
     function __construct() {
      $this->a = array('self', 'a');
      $this->c = array('self', 'c');
     }
     static function a($name, &$value) {
      echo $name,' => ',$value++,"\n";
     }
     function b($name, &$value) {
      call_user_func_array($this->a, array($name, &$value));
     }
     static function c($str) {
      echo $str,"\n";
     }
     function d() {
      call_user_func_array($this->c, func_get_args());
     }
     function e() {
      call_user_func($this->c, func_get_arg(0));
     }
    }
    class B extends A {
     function __construct() {
      $this->a = array('parent', 'a');
      $this->c = array('self', 'c');
     }
     static function c() {
      print_r(func_get_args());
     }
     function d() {
      call_user_func_array($this->c, func_get_args());
     }
     function e() {
      call_user_func($this->c, func_get_args());
     }
    }
    $a =& new A;
    $b =& new B;
    $i = 0;
    A::a('index', $i);
    $a->b('index', $i);
    $a->c('string');
    $a->d('string');
    $a->e('string');
    # etc.
    ?>