Callback / Callable 类型

自 PHP 5.4 起可用 callable 类型指定回调类型 callback。本文档基于同样理由使用 callback 类型信息。

一些函数如 call_user_func()usort() 可以接受用户自定义的回调函数作为参数。回调函数不止可以是简单函数,还可以是对象的方法,包括静态类方法。

传递

PHP是将函数以string形式传递的。 可以使用任何内置或用户自定义函数,但除了语言结构例如:array()echoempty()eval()exit()isset()list()printunset()

一个已实例化的 object 的方法被作为 array 传递,下标 0 包含该 object,下标 1 包含方法名。 在同一个类里可以访问 protected 和 private 方法。

静态类方法也可不经实例化该类的对象而传递,只要在下标 0 中包含类名而不是对象。自 PHP 5.2.3 起,也可以传递 'ClassName::methodName'

除了普通的用户自定义函数外,也可传递 匿名函数 给回调参数。

Example #1 回调函数示例

<?php 

// An example callback function
function my_callback_function() {
    echo 
'hello world!';
}

// An example callback method
class MyClass {
    static function 
myCallbackMethod() {
        echo 
'Hello World!';
    }
}

// Type 1: Simple callback
call_user_func('my_callback_function'); 

// Type 2: Static class method call
call_user_func(array('MyClass''myCallbackMethod')); 

// Type 3: Object method call
$obj = new MyClass();
call_user_func(array($obj'myCallbackMethod'));

// Type 4: Static class method call (As of PHP 5.2.3)
call_user_func('MyClass::myCallbackMethod');

// Type 5: Relative static class method call (As of PHP 5.3.0)
class {
    public static function 
who() {
        echo 
"A\n";
    }
}

class 
extends {
    public static function 
who() {
        echo 
"B\n";
    }
}

call_user_func(array('B''parent::who')); // A

// Type 6: Objects implementing __invoke can be used as callables (since PHP 5.3)
class {
    public function 
__invoke($name) {
        echo 
'Hello '$name"\n";
    }
}

$c = new C();
call_user_func($c'PHP!');
?>

Example #2 使用 Closure 的示例

<?php
// Our closure
$double = function($a) {
    return 
$a 2;
};

// This is our range of numbers
$numbers range(15);

// Use the closure as a callback here to 
// double the size of each element in our 
// range
$new_numbers array_map($double$numbers);

print 
implode(' '$new_numbers);
?>

以上例程会输出:

2 4 6 8 10

Note:

在函数中注册有多个回调内容时(如使用 call_user_func()call_user_func_array()),如在前一个回调中有未捕获的异常,其后的将不再被调用。

User Contributed Notes

Daniel Klein 13-Sep-2017 04:26
You can use "self::method_name", "static::method_name" and "parent::method_name" in callables:

<?php
class StaticCallable {
    public static function
foo($values) {
        return
array_map('self::bar', $values);
    }

    public static function
bar($value) {
        return
"{$value}: 42";
    }

    public static function
baz($values) {
        return
array_map('static::qux', $values);
    }

    public static function
qux($value) {
        return
"{$value}: 123";
    }
}

class
StaticExtension extends StaticCallable {
    public static function
bar($value) {
        return
"{$value}: Marvin the Paranoid Android";
    }

    public static function
qux($value) {
        return
"{$value}: Zaphod Beeblebrox";
    }
}

print_r(StaticCallable::foo([1, 2, 3]));
print_r(StaticExtension::foo([1, 2, 3]));

print_r(StaticCallable::baz([1, 2, 3]));
print_r(StaticExtension::baz([1, 2, 3]));
?>

Results:
Array
(
    [0] => 1: 42
    [1] => 2: 42
    [2] => 3: 42
)
Array
(
    [0] => 1: 42
    [1] => 2: 42
    [2] => 3: 42
)
Array
(
    [0] => 1: 123
    [1] => 2: 123
    [2] => 3: 123
)
Array
(
    [0] => 1: Zaphod Beeblebrox
    [1] => 2: Zaphod Beeblebrox
    [2] => 3: Zaphod Beeblebrox
)

"self::" uses the same class as the called method, "static::" uses the same class as the called class, and "parent::" (not shown) uses the parent class, or generates a warning if there is no parent.
whysteepy at gmail dot com 18-Apr-2017 03:34
Another Appearance of Callbacks! Here is one way of them - methods of an instantiated object can be callable and implemented as variable functions without php's default functions that can call user-defined callback functions.

class Test {
    protected $items = array();

    public function __construct()
    {
        $this->items[] = array($this, 'callBackOne');
        $this->items[] = array($this, 'callBackTwo');
    }

    public function callBackOne()
    {
        echo __METHOD__ . ' has been called as a callback.';
    }

    public function callBackTwo()
    {
        echo __METHOD__ . ' has been called as a callback.';
    }   

    public function getItems()
    {
        return $this->items;
    }
}

$o = new Test();
$itemLists = $o->getItems();

foreach ($itemLists as $itemList) {

        // call each one as a variable function
        echo '<pre>';
        print_r($itemList());
        echo '</pre>';
}

// Outputs the following
// Test::callBackOne has been called as a callback.

// Test::callBackTwo has been called as a callback.
bradyn at NOSPAM dot bradynpoulsen dot com 14-Apr-2016 10:22
When trying to make a callable from a function name located in a namespace, you MUST give the fully qualified function name (regardless of the current namespace or use statements).

<?php

namespace MyNamespace;

function
doSomethingFancy($arg1)
{
   
// do something...
}

$values = [1, 2, 3];

array_map('doSomethingFancy', $values);
// array_map() expects parameter 1 to be a valid callback, function 'doSomethingFancy' not found or invalid function name

array_map('MyNamespace\doSomethingFancy', $values);
// => [..., ..., ...]
pawel dot tadeusz dot niedzielski at gmail dot com 12-Apr-2016 11:11
@edanschwartz at gmail dot com

You can use ::class property to always indicate the class you're in when using static methods:

<?php
class Foo {
    public static function
doAwesomeThings() {
       
FunctionCaller::callIt(self::class . '::someAwesomeMethod');
    }

    public static function
someAwesomeMethod() {
       
// fantastic code goes here.
   
}
}

class
FunctionCaller {
    public static function
callIt(callable $func) {
       
call_user_func($func);
    }
}

Foo::doAwesomeThings();
?>
mariano dot REMOVE dot perez dot rodriguez at gmail dot com 22-Sep-2015 04:45
I needed a function that would determine the type of callable being passed, and, eventually,
normalized it to some extent. Here's what I came up with:

<?php

/**
 * The callable types and normalizations are given in the table below:
 *
 *  Callable                        | Normalization                   | Type
 * ---------------------------------+---------------------------------+--------------
 *  function (...) use (...) {...}  | function (...) use (...) {...}  | 'closure'
 *  $object                         | $object                         | 'invocable'
 *  "function"                      | "function"                      | 'function'
 *  "class::method"                 | ["class", "method"]             | 'static'
 *  ["class", "parent::method"]     | ["parent of class", "method"]   | 'static'
 *  ["class", "self::method"]       | ["class", "method"]             | 'static'
 *  ["class", "method"]             | ["class", "method"]             | 'static'
 *  [$object, "parent::method"]     | [$object, "parent::method"]     | 'object'
 *  [$object, "self::method"]       | [$object, "method"]             | 'object'
 *  [$object, "method"]             | [$object, "method"]             | 'object'
 * ---------------------------------+---------------------------------+--------------
 *  other callable                  | idem                            | 'unknown'
 * ---------------------------------+---------------------------------+--------------
 *  not a callable                  | null                            | false
 *
 * If the "strict" parameter is set to true, additional checks are
 * performed, in particular:
 *  - when a callable string of the form "class::method" or a callable array
 *    of the form ["class", "method"] is given, the method must be a static one,
 *  - when a callable array of the form [$object, "method"] is given, the
 *    method must be a non-static one.
 *
 */
function callableType($callable, $strict = true, callable& $norm = null) {
  if (!
is_callable($callable)) {
    switch (
true) {
      case
is_object($callable):
       
$norm = $callable;
        return
'Closure' === get_class($callable) ? 'closure' : 'invocable';
      case
is_string($callable):
       
$m    = null;
        if (
preg_match('~^(?<class>[a-z_][a-z0-9_]*)::(?<method>[a-z_][a-z0-9_]*)$~i', $callable, $m)) {
          list(
$left, $right) = [$m['class'], $m['method']];
          if (!
$strict || (new \ReflectionMethod($left, $right))->isStatic()) {
           
$norm = [$left, $right];
            return
'static';
          }
        } else {
         
$norm = $callable;
          return
'function';
        }
        break;
      case
is_array($callable):
       
$m = null;
        if (
preg_match('~^(:?(?<reference>self|parent)::)?(?<method>[a-z_][a-z0-9_]*)$~i', $callable[1], $m)) {
          if (
is_string($callable[0])) {
            if (
'parent' === strtolower($m['reference'])) {
              list(
$left, $right) = [get_parent_class($callable[0]), $m['method']];
            } else {
              list(
$left, $right) = [$callable[0], $m['method']];
            }
            if (!
$strict || (new \ReflectionMethod($left, $right))->isStatic()) {
             
$norm = [$left, $right];
              return
'static';
            }
          } else {
            if (
'self' === strtolower($m['reference'])) {
              list(
$left, $right) = [$callable[0], $m['method']];
            } else {
              list(
$left, $right) = $callable;
            }
            if (!
$strict || !(new \ReflectionMethod($left, $right))->isStatic()) {
             
$norm = [$left, $right];
              return
'object';
            }
          }
        }
        break;
    }
   
$norm = $callable;
    return
'unknown';
  }
 
$norm = null;
  return
false;
}

?>

Hope someone else finds it useful.
Riikka K 11-May-2015 10:36
A note on differences when calling callbacks as "variable functions" without the use of call_user_func() (e.g. "<?php $callback = 'printf'; $callback('Hello World!') ?>"):

- Using the name of a function as string has worked since at least 4.3.0
- Calling anonymous functions and invokable objects has worked since 5.3.0
- Using the array structure [$object, 'method'] has worked since 5.4.0

Note, however, that the following are not supported when calling callbacks as variable functions, even though they are supported by call_user_func():

- Calling static class methods via strings such as 'foo::doStuff'
- Calling parent method using the [$object, 'parent::method'] array structure

All of these cases are correctly recognized as callbacks by the 'callable' type hint, however. Thus, the following code will produce an error "Fatal error: Call to undefined function foo::doStuff() in /tmp/code.php on line 4":

<?php
class foo {
    static function
callIt(callable $callback) {
       
$callback();
    }
   
    static function
doStuff() {
        echo
"Hello World!";
    }
}

foo::callIt('foo::doStuff');
?>

The code would work fine, if we replaced the '$callback()' with 'call_user_func($callback)' or if we used the array ['foo', 'doStuff'] as the callback instead.
edanschwartz at gmail dot com 03-Feb-2015 03:13
You can use 'self::methodName' as a callable, but this is dangerous. Consider this example:

<?php
class Foo {
    public static function
doAwesomeThings() {
       
FunctionCaller::callIt('self::someAwesomeMethod');
    }

    public static function
someAwesomeMethod() {
       
// fantastic code goes here.
   
}
}

class
FunctionCaller {
    public static function
callIt(callable $func) {
       
call_user_func($func);
    }
}

Foo::doAwesomeThings();
?>

This results in an error:
Warning: class 'FunctionCaller' does not have a method 'someAwesomeMethod'.

For this reason you should always use the full class name:
<?php
FunctionCaller
::callIt('Foo::someAwesomeMethod');
?>

I believe this is because there is no way for FunctionCaller to know that the string 'self' at one point referred to to `Foo`.
Yzmir Ramirez 16-Apr-2014 08:40
> As of PHP 5.2.3, it is also possible to pass 'ClassName::methodName'

You can also use 'self::methodName'.  This works in PHP 5.2.12 for me.
computrius at gmail dot com 12-Oct-2013 01:38
When specifying a call back in array notation (ie. array($this, "myfunc") ) the method can be private if called from inside the class, but if you call it from outside you'll get a warning:

<?php

class mc {
   public function
go(array $arr) {
      
array_walk($arr, array($this, "walkIt"));
   }

   private function
walkIt($val) {
       echo
$val . "<br />";
   }

    public function
export() {
        return array(
$this, 'walkIt');
    }
}

$data = array(1,2,3,4);

$m = new mc;
$m->go($data); // valid

array_walk($data, $m->export()); // will generate warning

?>

Output:
1<br />2<br />3<br />4<br />
Warning: array_walk() expects parameter 2 to be a valid callback, cannot access private method mc::walkIt() in /in/tfh7f on line 22
metamarkers at gmail dot com 04-Jun-2013 04:34
you can pass an object as a callable if its class defines the __invoke() magic method..
steve at mrclay dot org 18-Sep-2012 06:00
Performance note: The callable type hint, like is_callable(), will trigger an autoload of the class if the value looks like a static method callback.
andrewbessa at gmail dot com 19-Jun-2012 12:16
You can also use the $this variable to specify a callback:

<?php
class MyClass {

    public
$property = 'Hello World!';

    public function
MyMethod()
    {
       
call_user_func(array($this, 'myCallbackMethod'));
    }

    public function
MyCallbackMethod()
    {
        echo
$this->property;
    }

}
?>