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']);

// 类型 6:相对静态类方法调用
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()),如在前一个回调中有未捕获的异常,其后的将不再被调用。