【问题标题】:How to define a callback with parameters without closure and use in PHP?如何在没有闭包的情况下定义带参数的回调并在 PHP 中使用?
【发布时间】:2015-08-19 10:47:39
【问题描述】:

在 PHP 中,为了定义回调,可以使用闭包,传递静态参数的工具是 use 指令。

class MyClass {
    public function foo($x) {
        echo $x;
    }
    public function bar() {
        $x = '123';
        $callback = function () use ($x) {
            $this->foo($x);
        };
        $callback();
    }
}

$myClass = new MyClass();
$myClass->bar();

是否有可能/如何避免匿名函数并用方法替换它?

class MyClass {
    public function foo($x) {
        echo $x;
    }
    public function baz() {
        $x = '567';
        // There is no function with the name create_user_func.
        // Just to show, what I'm looking for.
        $callback = create_callback([$this, 'foo'], $params = [$x]);
        $callback();
    }
}

$myClass = new MyClass();
$myClass->baz();

编辑

一些额外的/背景信息(明确说明我想要实现的目标和原因——并避免误解):

在我的具体情况下,我需要将回调传递给框架的方法。这意味着:我不能/可能不会影响它的调用方式。

该方法只接受回调本身,不接受回调的参数。这意味着,回调必须“知道”/提供它需要的静态参数(及其值)。

这正是use 指令解决的问题。但是我的方法中有多个回调定义,而且方法越来越长。所以我想将回调的逻辑移动到单独的方法中。

【问题讨论】:

  • 查看这个方法:php.net/manual/en/reflectionfunction.invoke.php。也许这就是你要找的东西?
  • 它对我不起作用。 “args - 传入的参数列表。它接受传递给函数的可变数量的参数,就像 call_user_func() 一样。”但实际上除了call_user_func() 之外,ReflectionFunction#_construct(...) 不接受像[$this, 'foo'] 这样的数组。仍然可以传递参数,但只能传递非静态参数,不能传递静态/早期绑定参数。
  • 基本上你是在尝试模拟 Javascript 的 someFunc.bind(null, x)?

标签: php lambda closures anonymous-function


【解决方案1】:

但是我的方法中有多个回调定义,而且方法越来越长。所以我想将回调的逻辑移动到单独的方法中。

这是魔术方法__invoke()的完美示例

对于您需要的每个回调,将其使用的功能和属性提取到一个新类中。将代码放入新类的__invoke() 方法中,将它需要的所有属性初始化到它的__construct() 中,就可以开始了。

代码:

class MyClass {
    public function bar() {
        $x = '123';
        // Create the callback, pass the values it needs to initialize
        $callback = new ActionFoo($x);
        // Call the callback without arguments
        $callback();
    }
}

class ActionFoo {
    private $x;
    public function __construct($x) {
        $this->x = $x;
    }
    public function __invoke() {
        echo($this->x);
    }
}

$myClass = new MyClass();
$myClass->bar();

【讨论】:

    【解决方案2】:

    我想可以公平地说您正在尝试模拟 Javascript 的 bind 方法。 PHP 中的问题是函数不是一等公民或对象,所以像->bind($x) 这样的东西是不可能的。也无法为callable 传递其他参数。所以你要把它包裹在something中。

    一个更可重用的方法是编写一个适当的类:

    class CallbackWrapper {
        protected $callable,
                  $args;
    
        public function __construct(callable $callable, array $args) {
            $this->callable = $callable;
            $this->args     = $args;
        }
    
        public function __invoke() {
            call_user_func_array($this->callable, $this->args);
        }
    }
    
    giveMeACallback(new CallbackWrapper([$this, 'foo'], [$x]));
    

    或者你只是简化匿名函数的构造:

    function make_callback(callable $callable, array $args) {
        return function () use ($callable, $args) {
            return call_user_func_array($callable, $args);
        };
    }
    
    giveMeACallback(make_callback([$this, 'foo'], [$x]));
    

    【讨论】:

      【解决方案3】:

      有很多方法可以实现这一点。

      1. 使用辅助函数。

      代码:

      class MyClass {
          private $x;
      
          public function foo() {
              echo $this->x;
          }
          public function bar() {
              $this->x = '123';
              anon($this);
          }
      }
      
      $myClass = new MyClass();
      $myClass->bar();
      
      function anon($obj) {
          $obj->foo();
      }
      
      1. 使用 call_user_func

      代码:

      class MyClass {
          private $x;
      
          public function foo() {
              echo $this->x;
          }
          public function bar() {
              $this->x = '123';
              call_user_func([$this, 'foo']);
          }
      }
      
      $myClass = new MyClass();
      $myClass->bar();
      
      1. 如果由于某种原因您不想使用成员变量,您可以使用 call_user_func_array 传递参数。

      代码:

      class MyClass {
          private $x;
      
          public function foo($x) {
              echo $x;
          }
          public function bar() {
              $x = '123';
              call_user_func_array([$this, 'foo'], [$x]);
          }
      }
      
      $myClass = new MyClass();
      $myClass->bar();
      

      【讨论】:

      • 非常感谢拉斐尔!但你的答案是关于调用。我的问题是关于 building 带有非静态参数的回调(使用方法“引用”而不是匿名函数)。请查看我的问题更新以及附加/背景信息。
      • 我很困惑。一些问题需要澄清。 1. $callback = create_callback([$this, 'foo'], $params = [$x]); $callback(); 是否以您想要的方式为您工作? 2. 如果它确实按照您想要的方式工作,您是否只想将此逻辑提取到另一个方法中?
      • PHP 中不存在方法create_callback(...)$callback = create_callback([$this, 'foo'], $params = [$x]) 只是一种伪代码,我在寻找什么。 $callback 需要传递给我写过的框架方法。所以我正在寻找一种方法来构建/定义具有非静态/早期绑定参数的回调/闭包(但没有匿名函数——为了重构代码并避免具有多个闭包定义块的无穷长方法) .
      • @deceze 写了一个答案,它定义了一个可以传递给你的框架的回调。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多