PHP - 可调用方法不是数组

PHP - Callable method not as array

通常在 PHP 中,如果你有一个 class Foo 的方法 bar 并且你想将它作为可调用对象传递,你可以使用一些东西喜欢

$foo = new Foo();
$callable = [$foo, 'bar'];

这样做的缺点是 is_array($callable) 的计算结果为 true

是否有另一种可行的方法将 class 方法作为可调用方法传递,使得 is_array($callable) returns false?

是的,有...即使它是一个可怕的黑客:

$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
    };
}

那么你可以使用:

$callable = makeCallable($foo, 'bar');
var_dump(is_array($callable));//false

缺点是:

var_dump($callbale instanceof Closure);//true

基本上,不要注意你的可调用对象也是一个数组这一事实,只需在你的代码库中使用类型提示:

function foobar(callable $action)
{
    return call_user_func($action);
}

那应该可以正常工作。

为什么你觉得当前的可调用数组是一个缺点:我理解你为什么觉得这不是一件好事。确实没有必要让任何人知道特定的可调用构造是数组、字符串或匿名函数(实际上是 Closure class 的实例 - 您可能会了解另一个实现细节不想传递)。但这正是因为可调用结构有多种形式,存在 callable 类型提示:您编写的需要可调用的代码不需要关心该可调用实体是如何实现的,它只需要知道它可以调用那条信息:

function handleEvent(callable $action, array $args = null)
{
    if ($args) {
        return call_user_func_array($action, $args);
    }
    return call_user_fun($action);
}

我不需要检查 $action 是否是字符串(如 'strtolower'Closure 实例或数组)我只是 知道 我可以调用它

函数和方法不是 第一个 class 公民 PHP,即它们不能分配给变量,没有类型等

callable 实际上不是一个类型,is_callable 以及 callable 类型提示只是检查某些东西是否可以 用作函数。这可以是:

  • 静态方法的数组[class, method]
  • 实例方法的数组[object, method]
  • 函数的字符串
  • 一个Closure实例
  • 实现魔术方法的对象__invoke()

如您所见,所有这些值实际上都有不同的类型,只是恰好是 "callable"。不存在没有其他类型的 "pure" 可调用对象。

PHP 7.1 终于在通过 Closure::fromCallable (RFC) 首先进行回调方面做出了巨大努力-class 公民。

here所述,您可以这样使用它:

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

使用它有一些好处:

  • 更好的错误处理 - 使用 Closure::fromCallable 时,它会在正确的位置显示错误,而不是在我们使用可调用对象的位置显示错误。这使得调试更容易。

  • 包装范围 - 即使回调是 MyClass 的 private/protected 方法,上述示例也能正常工作。

  • 性能 - 这也可以通过避免检查给定的可调用对象是否实际上是可调用对象的开销来提高性能。