2010-01-17 95 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將尋找真正的類方法財產後。