2010-01-17 60 views
13

注意:我对这篇文章有凝结成我个人的wiki:http://wiki.chacha102.com/Lambda - 享受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(); 

正确叫什么名字?如果您可以指向说明为何出现此问题的文档,请加点。

回答

9

这是一个有趣的问题。这工作:

$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()仅针对不存在或不可访问的方法(例如它们是私有的)调用。

+0

这与其他评论一起让我设置一个'stdObj'类,我可以使用它来可靠地创建一个没有类(类)的对象。谢谢! – 2010-01-17 16:53:34

3

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

+0

这似乎很奇怪。如果没有'bar'这个名称的方法,它应该查找变量'bar'并查看它是否可调用,所以这行'$ foo-> bar()'起作用。 – 2010-01-17 07:12:07

+4

你不一定是错的。但是Zend引擎不是以这种方式构建的。 – 2010-01-17 07:12:47

1

,你可以得到要做到这一点是通过使用__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(); 

虽然牢记从文档下面的注释:

匿名函数当前使用Closure类实现的 。 这是一个实现细节,不应该依赖 。

参考:Anonymous functions

+0

另一种选择是使用'is_callable'语句。我不确定这是否会告诉我它是否是封闭的。 – 2010-01-17 07:19:06

+0

这仍然不会起作用 - 如果有一个方法声明'foo()''__call()'不会被调用。 – leepowers 2010-01-17 07:21:03

+0

我不太关心重写一种方法。我会在早上回来,这个代码可能会起作用。只需要创建一个类似'stdCallableObj'的类,当我想为它指定闭包时,它就像'stdClass'一样。 – 2010-01-17 07:23:10

1

有趣的问题,虽然我看不出为什么这应该工作:

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

0

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

<?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知道如何运行它。

0

考虑一下:

<?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->foo)

我不知道任何其他简单的方法。

1

的OP已经提出的解决方案:

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

即含有一个匿名函数有其内在的不确定性,因为在属性名后加括号告诉PHP要调用的方法的任何财产,而不是在属性中调用anon函数。要解决这种歧义问题,您必须首先将属性存储到本地变量中,然后调用匿名函数来添加一定程度的分隔。

无论内容是什么,只要将类属性看作一个属性而不是“作为方法调用的属性”,并假定如果将括号括起来,php将寻找真正的类方法财产后。