2010-01-17 39 views
13

Note: Tôi đã ngưng tụ bài này vào người wiki của tôi: http://wiki.chacha102.com/Lambda - Thưởng thứcChức năng Lambda trong PHP là không logic

Tôi gặp một số rắc rối với các chức năng Lambda phong cách trong PHP.

Đầu tiên, công trình này:

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

Thứ hai, công trình này:

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

Thứ ba, này hoạt động:

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

Nhưng, điều này không làm việc:

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

Và, điều này không làm việc

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

Câu hỏi của tôi là Tại sao?, và làm thế nào tôi có thể đảm bảo rằng cả hai điều này:

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

và điều này

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

được gọi là đúng cách? Điểm thêm nếu bạn có thể trỏ đến tài liệu nêu rõ lý do xảy ra sự cố này.

Trả lời

9

Đây là một câu hỏi thú vị.Đây hoạt động:

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

nhưng như bạn nói điều này không:

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

Nếu bạn muốn một đối tượng mà bạn có thể tự động gọi các thành viên, hãy thử:

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)); 

Nhưng bạn không thể thay thế các phương thức có thể gọi theo cách này bởi vì __call() chỉ được gọi cho các phương thức không tồn tại hoặc không thể truy cập (ví dụ: chúng là riêng tư).

+0

Điều này cùng với các nhận xét khác cho phép tôi thiết lập một lớp 'stdObj' mà tôi có thể sử dụng để tạo một đối tượng không cần một lớp (loại) một cách đáng tin cậy. Cảm ơn! –

3

Các hàm và phương thức được xử lý khác nhau trong PHP. Bạn có thể sử dụng runkit_method_add() để thêm một phương thức vào một lớp, nhưng tôi biết không có cách nào để thêm một phương thức vào một cá thể.

+0

Nó có vẻ lạ. Nếu không có phương thức nào bằng tên 'bar', nó sẽ tìm biến' bar' và xem nó có thể gọi được không, do đó, dòng này '$ foo-> bar()' hoạt động. –

+4

Bạn không nhất thiết phải sai. Nhưng động cơ Zend không được xây dựng theo cách đó. –

1

Gần nhất bạn có thể nhận được để làm cho điều này xảy ra sẽ là bằng cách sử dụng sự quá tải __call để kiểm tra xem một tài sản có chứa một đóng cửa:

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

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

Mặc dù gấu trong tâm trí các lưu ý sau đây từ các tài liệu:

Các chức năng ẩn danh hiện đang được thực hiện bằng cách sử dụng lớp Closure (Đóng cửa). Đây là chi tiết triển khai và không nên dựa vào .

tham khảo:Anonymous functions

+0

Một tùy chọn khác sẽ sử dụng câu lệnh 'is_callable'. Tôi không chắc chắn nếu điều đó sẽ cho tôi biết nếu nó là một đóng cửa mặc dù. –

+0

Điều này vẫn không hoàn toàn hoạt động - nếu có một khai báo phương thức cho 'foo()' thì '__call()' sẽ không được gọi. – leepowers

+0

Tôi không quá quan tâm đến việc ghi đè phương pháp. Tôi sẽ trở lại vào buổi sáng, mã này có thể hoạt động. Chỉ cần tạo một lớp như 'stdCallableObj' sẽ hoạt động như một' stdClass' khi tôi muốn gán các bao đóng cho nó. –

1

câu hỏi thú vị mặc dù tôi thấy không có lý do tại sao điều này nên làm việc:

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

Tôi đã có một vấn đề tương tự với __invoke(), và tôi cũng đã không thể giải quyết nó:

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

Giải pháp ? Không bao giờ sử dụng __invoke()! : P

0

Đối với tôi nó có vẻ như một lỗi chứ không phải là một "đứa":

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

Nó là có, tôi chỉ không nghĩ rằng PHP biết làm thế nào để chạy nó.

0

Hãy xem xét điều này:

<?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()); 

Bạn có nghĩa là $ obj-> foo() đóng cửa hoặc $ obj-> foo() phương pháp này? Đó là mơ hồ và PHP đưa ra quyết định rằng bạn có nghĩa là phương pháp. Để sử dụng việc đóng cửa, bạn phải phân biệt ý nghĩa của mình. Một cách bạn có thể làm điều này là sử dụng một biến tạm thời như bạn đã làm. Một cách khác là sử dụng call_user_func($obj->foo).

Tôi không biết bất kỳ cách nào dễ dàng khác.

1

Các OP đã trình bày những giải pháp:

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

Ie, bất cứ tài sản có chứa một hàm anon có một sự nhập nhằng cố hữu vì thêm ngoặc sau tên tài sản cho PHP mà bạn đang gọi một phương thức, và không gọi một hàm anon trong một thuộc tính. Để giải quyết sự mơ hồ này, bạn phải thêm một mức độ phân tách bằng cách lưu trữ thuộc tính vào một biến cục bộ trước, và sau đó gọi hàm anon.

Bạn chỉ cần xem thuộc tính lớp làm thuộc tính thay vì "thuộc tính có thể gọi là phương thức" bất kể nội dung của nó là gì và giả sử rằng php sẽ tìm kiếm một phương thức lớp thực nếu bạn đặt dấu ngoặc đơn sau tài sản.

Các vấn đề liên quan