【问题标题】:PHP - Callable method not as arrayPHP - 可调用方法不是数组
【发布时间】:2015-12-04 05:36:29
【问题描述】:

通常在 PHP 中,如果您有一个类 Foo 的方法 bar,并且您想将它作为可调用对象传递,您可以使用类似

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

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

是否有另一种可行的方法将类方法作为可调用对象传递,以使is_array($callable) 返回false

【问题讨论】:

  • 为什么会有缺点?
  • @HalayemAnis 我知道这不是一个明显的缺点,但我认为它是一个,因为我不希望一个可调用对象是一个数组。这似乎是一个不应该传递的实现细节

标签: php callable


【解决方案1】:

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

$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 类的一个实例 - 您可能不想传递的另一个实现细节) .但这正是因为可调用构造有多种形式,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 实例或数组)我只是知道我可以调用它

【讨论】:

  • 这看起来真的很像黑客!感谢您的回答
  • @marcosh:正在快速咆哮为什么你真的不应该关心可调用的外观:PHP中有许多有效的可调用/回调构造,其中只有一个是数组
【解决方案2】:

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

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

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

如您所见,所有这些值实际上都具有不同的类型,并且恰好是“可调用的”。没有其他类型的“纯”可调用对象。

【讨论】:

    【解决方案3】:

    PHP 7.1 终于通过 Closure::fromCallable (RFC) 使回调成为一等公民。

    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 的私有/受保护方法,上述示例也可以正常工作。

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

    【讨论】:

      猜你喜欢
      • 2012-09-30
      • 1970-01-01
      • 1970-01-01
      • 2022-07-29
      • 1970-01-01
      • 2013-06-29
      • 2019-12-03
      • 2020-07-01
      • 1970-01-01
      相关资源
      最近更新 更多