通常在PHP中,如果你有一个bar类的方法,Foo并且你想将它作为一个可调用的传递,你可以使用类似的东西
$foo = new Foo();
$callable = [$foo, 'bar'];
Run Code Online (Sandbox Code Playgroud)
这样做的缺点是is_array($callable)评估为true.
是否有另一种可行的方法来传递类方法作为可调用的is_array($callable)返回false?
Eli*_*gem 10
是的,有......虽然这是一个可怕的,可怕的黑客:
$foo = new Foo();
function makeCallable($instance, $method)
{
return function() use ($instance, $method) {
return $instance->{$method}();//use func_get_args + call_user_func_array to support arguments
};
}
Run Code Online (Sandbox Code Playgroud)
然后你可以使用:
$callable = makeCallable($foo, 'bar');
var_dump(is_array($callable));//false
Run Code Online (Sandbox Code Playgroud)
缺点是:
var_dump($callbale instanceof Closure);//true
Run Code Online (Sandbox Code Playgroud)
基本上,不要注意你的callable也是一个数组,并且只需在整个代码库中使用type-hinting:
function foobar(callable $action)
{
return call_user_func($action);
}
Run Code Online (Sandbox Code Playgroud)
这应该工作得很好.
关于为什么你觉得当前的可调用数组是一个缺点:我理解为什么你觉得这不是一件好事.确实没有必要知道特定的可调用构造是数组,字符串或匿名函数(实际上是Closure类的实例- 您可能不想传递的另一个实现细节).但这正是因为可调用的构造有多种形式,callable类型提示存在:你编写的需要可调用的代码不需要关心如何实现可调用的实体,它只需要知道它可以调用那条信息:
function handleEvent(callable $action, array $args = null)
{
if ($args) {
return call_user_func_array($action, $args);
}
return call_user_fun($action);
}
Run Code Online (Sandbox Code Playgroud)
我不需要检查是否$action是字符串(例如'strtolower',Closure实例或数组)我只知道我可以调用它
PHP 7.1 最终通过 Closure::fromCallable ( RFC )使回调成为一等公民。
正如这里所描述的,您可以像这样使用它:
class MyClass
{
public function getCallback() {
return Closure::fromCallable([$this, 'callback']);
}
public function callback($value) {
echo $value . PHP_EOL;
}
}
$callback = (new MyClass)->getCallback();
$callback('Hello World');
Run Code Online (Sandbox Code Playgroud)
使用它有一些好处:
更好的错误处理 - 当使用 Closure::fromCallable 时,它会在正确的位置显示错误,而不是在我们使用可调用的位置显示错误。这使得调试变得更加容易。
包装范围 - 即使回调是 MyClass 的私有/受保护方法,上面的示例也能正常工作。
性能 - 这还可以通过避免检查给定可调用对象是否实际上是可调用对象的开销来提高性能。
函数和方法不是PHP 中的一等公民,即它们不能分配给变量、没有类型等。
callable实际上不是类型,is_callable类型callable提示只是检查某些东西是否可以用作函数。这可以是:
[class, method]静态方法的数组[object, method]实例方法的数组Closure实例__invoke()正如您所看到的,所有这些值实际上都有不同的类型,并且恰好是“可调用的”。不存在没有其他类型的“纯”可调用对象。