PHP 8.5.0 Released!

Callable

callable 是对函数或方法的引用,作为参数传递给其他函数,使用 callable 类型声明来表示。

<?php
function foo(callable $callback) {
$callback();
}
?>

一些函数接受回调函数作为参数,例如 array_map()usort()preg_replace_callback()

callable 的创建

callable 是一种表示可调用内容的类型。Callable 可作为参数传递给需要回调参数的函数或方法,也可直接调用。callable 类型不能用于类属性的类型声明,此时应使用 Closure 类型声明。

Callable 可通过多种方式创建:

Closure 对象可通过匿名函数语法、箭头函数语法、一级可调用语法,或 Closure::fromCallable() 方法创建。

注意: 一级可调用语法 仅自 PHP 8.1.0 起可用。

示例 #1 使用 Closure 的 Callback 示例

<?php
// 使用匿名函数语法
$double1 = function ($a) {
return
$a * 2;
};

// 使用一级可调用语法
function double_function($a) {
return
$a * 2;
}
$double2 = double_function(...);

// 使用箭头函数语法
$double3 = fn($a) => $a * 2;

// 使用 Closure::fromCallable
$double4 = Closure::fromCallable('double_function');

// 此处使用 closure 作为回调,将范围内每个元素的值翻倍。
$new_numbers = array_map($double1, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double2, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double3, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double4, range(1, 5));
print
implode(' ', $new_numbers);

?>

以上示例在 PHP 8.1 中的输出:

2 4 6 8 10
2 4 6 8 10
2 4 6 8 10
2 4 6 8 10

callable 也可以是包含函数名或静态方法名的字符串。除语言结构(如 array()echoempty()eval()isset()list()printunset())外,任何内置或用户自定义函数均可使用。

静态类方法可在不实例化该类 object 的情况下使用,方式包括:创建数组,其中索引 0 为类名,索引 1 为方法名;或使用作用域解析运算符 :: 的特殊语法,例如 'ClassName::methodName'

已实例化 object 的方法在以数组形式提供时可作为 callable,其中索引 0 为该 object,索引 1 为方法名。

Closure 对象与 callable 类型的主要区别在于,Closure 对象与作用域无关,始终可直接调用,而 callable 类型可能依赖于作用域,不一定能直接调用。创建 callable 时,推荐使用 Closure

注意: Closure 对象绑定于其创建时所在的作用域,而以字符串或数组形式引用类方法的 callable 则在其被调用的作用域中解析。若需从 private 或 protected 方法创建可在类作用域外部调用的可调用项,应使用 Closure::fromCallable()一级可调用语法

PHP 允许创建 callable,可用作回调参数,但无法直接调用。它们是上下文相关的 callable,引用类继承层次中的某个类方法,例如 'parent::method'["static", "method"]

注意: 自 PHP 8.2.0 起,已弃用上下文相关的 callable 。应通过将 'parent::method' 替换为 parent::class . '::method',或使用一级可调用语法,以消除上下文依赖。

示例 #2 使用 call_user_function() 调用各类 callable

<?php

// callback 函数示例
function my_callback_function() {
echo
'hello world!', PHP_EOL;
}

// callback 方法示例
class MyClass {
static function
myCallbackMethod() {
echo
'Hello World!', PHP_EOL;
}
}

// 类型 1:简单回调
call_user_func('my_callback_function');

// 类型2:静态类方法回调
call_user_func(['MyClass', 'myCallbackMethod']);

// 类型 3:对象方法回调
$obj = new MyClass();
call_user_func([$obj, 'myCallbackMethod']);

// 类型 4:静态类方法回调
call_user_func('MyClass::myCallbackMethod');

// 类型 5:使用 ::class 关键字的静态类方法回调
call_user_func([MyClass::class, 'myCallbackMethod']);

// Type 6: Relative static class method call
class A {
public static function
who() {
echo
'A', PHP_EOL;
}
}

class
B extends A {
public static function
who() {
echo
'B', PHP_EOL;
}
}

call_user_func(['B', 'parent::who']); // 自 PHP 8.2.0 起弃用

// 类型 7:实现 __invoke 的对象用于 callable
class C {
public function
__invoke($name) {
echo
'Hello ', $name;
}
}

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

以上示例会输出:

hello world!
Hello World!
Hello World!
Hello World!
Hello World!

Deprecated: Callables of the form ["B", "parent::who"] are deprecated in script on line 41
A
Hello PHP!

注意:

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

添加备注

用户贡献的备注 12 notes

up
280
andrewbessa at gmail dot com
13 years ago
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;
    }

}
?>
up
207
steve at mrclay dot org
13 years ago
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.
up
193
computrius at gmail dot com
12 years ago
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
up
184
Riikka K
10 years ago
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.
up
185
edanschwartz at gmail dot com
10 years ago
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`.
up
175
metamarkers at gmail dot com
12 years ago
you can pass an object as a callable if its class defines the __invoke() magic method..
up
118
mariano dot REMOVE dot perez dot rodriguez at gmail dot com
10 years ago
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.
up
24
bradyn at NOSPAM dot bradynpoulsen dot com
9 years ago
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);
// => [..., ..., ...]
up
11
InvisibleSmiley
4 years ago
If you pass a callable method to a function with a callable type declaration, the error message is misleading:

<?php
class X {
    protected function foo(): void {}
}

function bar(callable $c) {}

$x = new X;
$c = [$x, 'foo'];
bar($c);
?>

Error message will be something like "Argument #1 ($c) must be of type callable, array given" while the actual problem here is only the visibility of method "foo". All you need to do is changing it to public (or use a different approach, e.g. with a Closure).
up
6
gulaschsuppe2 at gmail dot com
6 years ago
I tried many possible ways of calling functions by function name directly and assigned to a variable on 3v4l. Not mentioned yet, it is possible to use an array as a caller, at least since PHP 7.1.25. The following script contains all the information I gained:

<?php

// Call function via function name:
    // Basics:
        // A function can also be called by using its string name:
        function callbackFunc() {
            echo 'Hello World';
        }

        'callbackFunc'(); // Hello World
                            
        // A function can also be called if its name is assigned to a variable:
            function callbackFunc() {
                echo 'Hello World';
            }

            $funcName = 'callbackFunc';
            $funcName(); // Hello World

    // Static class method:
        // It is also possible to call a public static class method via 'ClassName::functioName' notation:
            class A {
                public static function callbackMethod() {
                    echo "Hello World\n";
                }
            }
            'A::callbackMethod'(); // Hello World

            $funcName = 'A::callbackMethod';
            $funcName(); // Hello World

    // Non static class method:
        // It is also possible to call non static class methods by creating an array which first element is the object the method should be called on and the second element is the non static method to be called. The array can directly be used as a caller:
            class A {
                private $prop = "Hello World\n";

                public function callbackMethod() {
                    echo $this->prop;
                }
            }

            $a = new A;
            [$a, 'callbackMethod']();
            $funcCallArr = [$a, 'callbackMethod'];
            $funcCallArr();

        // Of course this also works inside the class with '$this':
            class A {
                private function privCallback() {
                    echo 'Private';
                }

                public function privCallbackCaller($funcName) {
                    [$this, $funcName]();
                }
            }

            (new A)->privCallbackCaller('privCallback'); // Private

?>
up
2
pawel dot tadeusz dot niedzielski at gmail dot com
9 years ago
@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();
?>
up
0
chris dot rutledge at gmail dot com
6 years ago
Having read this line in the manual above, 

"A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1. Accessing protected and private methods from within a class is allowed."

I decided to do some testing to see if I could access private methods using the call_user_func methods. Thankfully not, but for completeness here is my test which also covers using static and object contexts

<?php
class foo {
    
    public static $isInstance = false;
    
    public function __construct() {
        self::$isInstance = true;
    }

    public function bar() {
        var_dump(self::$isInstance);
        echo __METHOD__;
    }
    
    private function baz() {
        var_dump(self::$isInstance);
        echo __METHOD__;
    }
    
    public function qux() {
        $this->baz();
    }
    
    public function quux() {
        self::baz();
    }
}

call_user_func(['foo','bar']);    //fase, foo:bar

call_user_func(['foo','baz']);  //warning, cannot access private method

call_user_func(['foo','quux']); //false, foo::baz

call_user_func(['foo','qux']);  //fatal, Using $this when not in object context 

$foo = new foo;

call_user_func([$foo,'bar']);    //true, foo::bar
call_user_func([$foo,'baz']);    //warning, cannot access private method
call_user_func([$foo,'qux']);    //true, foo::baz

call_user_func(['foo','bar']);  //true, foo::bar (static call, yet $isInstance is true)

?>
To Top