【问题标题】:Lambda Functions in PHP aren't LogicalPHP 中的 Lambda 函数不合逻辑
【发布时间】:2010-01-17 07:03:29
【问题描述】:

注意:我已将这篇文章压缩到我的个人 wiki 中:http://wiki.chacha102.com/Lambda - 享受

我在 PHP 中使用 Lambda 样式函数时遇到了一些问题。

首先,这是可行的:

$foo = function(){ echo "bar"; };
$foo();

第二,这可行:

class Bar{
    public function foo(){
       echo "Bar";
    }

第三,这行得通:

$foo = new stdClass;
$foo->bar = function(){ echo "bar"; };
$test = $foo->bar;
$test();

但是,这不起作用:

$foo = new stdClass;
$foo->bar = function(){ echo "bar"; };
$foo->bar();

而且,这不起作用

class Bar{
    public function foo(){
       echo "Bar";
    }
$foo = new Bar;
$foo->foo = function(){ echo "foo"; };
$foo->foo(); // echo's bar instead of Foo.

我的问题是为什么?,我如何保证这两个:

$foo->bar = function(){ echo "test"; };
$foo->bar();

还有这个

$foo = new Bar;
$foo->bar();

被正确调用了吗?如果您可以指出说明此问题发生原因的文档,则加分。

【问题讨论】:

  • 这更多地与您无法在 PHP 中动态覆盖类的方法有关,这一次又一次让我感到沮丧。我最终使用数组(或许多其他“解决方案”之一)来动态构建包含数据和函数的“事物”。数组不能很好地用于自动完成,并且您会丢失许多其他功能。从你问到现在已经 10 年了,没有什么明显的变化。如果要在对象实例化后重新定义方法,则必须改用属性(即,您根本无法重新定义方法),并且必须以不同的方式调用属性。

标签: php oop lambda


【解决方案1】:

这是一个有趣的问题。这有效:

$a = new stdClass;
$a->foo = function() { echo "bar"; };
$b = $a->foo;
$b(); // echos bars

但正如你所说,这不是:

$a = new stdClass;
$a->foo = function() { echo "bar"; };
$a->foo();

如果您想要一个可以动态调用成员的对象,请尝试:

class A {
  public function __call($func, $args) {
    $f = $this->$func;
    if (is_callable($f)) {
      return call_user_func_array($f, $args);
    }
  }
}

$a = new A;
$a->foo = function() { echo "bar\n"; };
$a->foo2 = function($args) { print_r($args); };
$a->foo();
$a->foo2(array(1 => 2, 3 => 4));

但是您不能以这种方式替换可调用方法,因为 __call() 仅在不存在或不可访问的方法(例如它们是私有的)时调用。

【讨论】:

  • 这与其他 cmets 一起让我设置了一个 stdObj 类,我可以使用它来可靠地创建一个没有类(种类)的对象。谢谢!
【解决方案2】:

函数和方法在 PHP 中的处理方式不同。您可以使用runkit_method_add() 向类添加方法,但我不知道如何向实例添加方法。

【讨论】:

  • 这看起来很奇怪。如果没有名为bar 的方法,它应该查找变量bar 并查看它是否可调用,因此$foo->bar() 这一行有效。
  • 你不一定是错的。但是 Zend 引擎不是这样构建的。
【解决方案3】:

有趣的问题,虽然我看不出这应该起作用的理由:

class Bar{
    public function foo(){
       echo "Bar";
    }
$foo = new Bar;
$foo->foo = function(){ echo "foo"; };
$foo->foo(); // echo's bar instead of Foo.

__invoke() 也有类似的问题,我也无法解决:

class base {
    function __construct() {
        $this->sub = new sub();
    }

    function __call($m, $a) {
    }
}

class sub {
    function __invoke($a) {
    }
}

$b = new base();
$b->sub('test'); // should trigger base::__call('sub', 'test') or sub::__invoke('test')?

解决方案?永远不要使用__invoke()! :P

【讨论】:

    【解决方案4】:

    实现这一点的最接近方法是使用__call 重载来检查属性是否包含闭包:

    class what {
      function __call($name, $args) {
        $f= $this->$name;
        if ($f instanceof Closure) {
          $f();
        }
      }
    }
    
    $foo = new what();
    $foo->bar = function(){ echo "bar"; };
    $foo->bar();
    

    请记住文档中的以下注释:

    匿名函数目前是 使用闭包类实现。 这是一个实现细节和 不应依赖。

    参考:Anonymous functions

    【讨论】:

    • 另一种选择是使用is_callable 语句。我不确定这是否会告诉我它是否是一个关闭。
    • 这仍然无法正常工作 - 如果有 foo() 的方法声明,则不会调用 __call()
    • 我不太关心重写方法。我会在早上回来,这段代码可能会起作用。只需要创建一个像stdCallableObj 这样的类,当我想为其分配闭包时,它就可以充当stdClass
    【解决方案5】:

    OP已经给出了解决方案:

    $foo = new stdClass;
    $foo->bar = function(){ echo "bar"; };
    $test = $foo->bar;
    $test();
    

    也就是说,任何包含 anon 函数的属性都具有固有的歧义,因为在属性名称后添加括号告诉 PHP 您正在调用方法,而不是在属性中调用 anon 函数。要解决这种歧义,您必须先将属性存储到局部变量中,然后调用 anon 函数来添加一定程度的分离。

    无论它的内容是什么,您只需将类属性视为属性而不是“可作为方法调用的属性”,并假设如果您在后面加上括号,php 将寻找真正的类方法属性。

    【讨论】:

      【解决方案6】:

      对我来说,这似乎是一个错误而不是一个“怪癖”:

      <?php
      $t = new stdClass;
      $t->a = function() { echo "123"; };
      var_dump($t);
      echo "<br><br>";
      $x = (array)$t;
      var_dump($x);
      echo "<br><br>";
      $x["a"]();
      echo "<br><br>";
      ?>
      

      object(stdClass)#1 (1) { ["a"]=> object(Closure)#2 (0) { } } 
      
      array(1) { ["a"]=> object(Closure)#2 (0) { } } 
      
      123
      

      它就在那里,我只是不认为 PHP 知道如何运行它。

      【讨论】:

        【解决方案7】:

        考虑一下:

        <?php
        
        class A {
        
            public $foo
        
            public function foo() {
                return 'The method foo';
            }
        
        }
        
        $obj = new A;
        $obj->foo = function() { return 'The closure foo'; };
        
        var_dump($obj->foo());
        

        你是说 $obj->foo() 闭包还是 $obj->foo() 方法?它是模棱两可的,PHP 决定你的意思是方法。要使用闭包,您必须消除歧义。您可以这样做的一种方法是像您所做的那样使用临时变量。另一种方法是使用call_user_func($obj-&gt;foo)

        我不知道还有其他简单的方法。

        【讨论】:

          猜你喜欢
          • 2012-01-03
          • 2021-11-16
          • 1970-01-01
          • 1970-01-01
          • 2016-03-18
          • 2020-08-27
          • 1970-01-01
          • 1970-01-01
          • 2012-06-30
          相关资源
          最近更新 更多