2009-01-07 24 views
7

Tôi đã sử dụng cách mở rộng chức năng của jQuery/Javascript thông qua các bao đóng. Có thể làm điều gì đó tương tự trong PHP 5.3?Đóng cửa là thành viên của lớp học?

class Foo 
{ 
    public $bar; 
} 

$foo = new Foo; 
$foo->bar = function($baz) { echo strtoupper($baz); }; 
$foo->bar('lorem ipsum dolor sit amet'); 
// LOREM IPSUM DOLOR SIT AMET 

[sửa] trộn lẫn 'it' và 'is' trong câu hỏi của tôi. heh.

CẬP NHẬT

Tôi đã tải xuống 5,33 và nó hoạt động!

class Foo 
{ 
    protected $bar; 
    public $baz; 

    public function __construct($closure) 
    { 
     $this->bar = $closure; 
    } 

    public function __call($method, $args) 
    { 
     $closure = $this->$method; 
     call_user_func_array($closure, $args); 
    } 

} 

$foo = new Foo(function($name) { echo "Hello, $name!\n"; }); 
$foo->bar('Mon'); 
// Hello, Mon! 
$foo->baz = function($s) { echo strtoupper($s); }; 
$foo->baz('the quick brown fox jumps over the lazy dog'); 
// THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 
+0

này là khá nhiều bản sao của http://stackoverflow.com/questions/146737/closures-in-php-what-exact-are-them-and-when-would-you-need-to-use-them – troelskn

+0

Xem thêm: [Gọi đóng cửa được gán trực tiếp cho thuộc tính đối tượng] (http: // stackoverflow .com/q/4535330/367456) – hakre

Trả lời

1

PHP có thể qua create_function(). Cú pháp đang đóng đinh dù cho nó gần như hoàn toàn vô dụng.

Dường PHP5.3+ là nhận được/đã hỗ trợ cho closures and lambda's!

Tuy nhiên cũng từ RFC

chức năng Lambda/đóng cửa không phải là một cách để tự động mở rộng các lớp học bằng các phương pháp bổ sung tại thời gian chạy. Có một số khả năng khác để làm điều này, bao gồm cả ngữ nghĩa _ _all hiện tại.

+0

Tôi đoán câu trả lời đó trả lời câu hỏi của tôi. Tôi tự hỏi liệu 'một số khả năng khác' có bao gồm cách có thể tận dụng cú pháp đóng cửa bằng cách nào đó không ... – monzee

1

Các chức năng/đóng cửa ẩn danh thích hợp chỉ có sẵn trong PHP 5.3, sẽ không khả dụng rộng rãi trong một thời gian - some useful information. Tôi không nghĩ rằng nó thực sự có thể làm những gì bạn muốn.

Bạn có thể sử dụng create_function() đến một mức độ nào đó:

$func = create_function('$baz', ' echo strtoupper($baz); '); 
$func("hello"); 

Tuy nhiên, sau đây không làm việc như bạn mong chờ:

$foo = new Foo; 
$foo->bar = create_function('$baz', ' echo strtoupper($baz); '); 
$foo->bar('lorem ipsum dolor sit amet'); 

Bạn sẽ cần phải làm thay vì:

call_user_func($foo->bar, 'lorem ipsum dolor sit amet'); 

Ngoài ra, bạn không thể sử dụng $this bên trong hàm được tạo bởi beca sử dụng phạm vi của nó sẽ không giống như một phương thức.

Sửa

tôi vô tình lặp lại một số câu trả lời Martijn khi anh thay đổi nội dung của tôi trong khi tôi đang viết. Tôi sẽ để lại câu trả lời của tôi còn nguyên vẹn vì lợi ích của sự hoàn chỉnh

chỉnh sửa khác

Bạn có lẽ cũng có thể làm điều gì đó như

class Foo 
{ 
    public $bar; 

    protected $anonFuncs = array(); 

    public function create_method($name, $params, $code) { 
     if (strlen($params)) $params .= ','; 
     $params .= '$self'; 
     $this->anonFuncs[$name] = create_function($params, $code); 
    } 

    public function __call($name, $args) { 
     if (!isset($this->anonFuncs[$name])) { 
      trigger_error("No method $name", E_USER_ERROR); 
     } 

     $args[] = $this; 

     return call_user_func_array($this->anonFuncs[$name], $args); 
    } 
} 

$foo = new Foo; 
$foo->create_method('bar', '$baz', ' echo strtoupper($baz) . get_class($self); '); 
$foo->bar('lorem ipsum dolor sit amet'); 
+1

hãy cẩn thận, đừng dùng nó trong vòng lặp, vì nó sẽ ăn bộ nhớ. các hàm không được giải phóng khi được tạo. –

+0

Đưa mã bên trong dấu ngoặc kép là ... ugh. Tôi đã hy vọng việc đóng cửa sẽ cho phép chúng tôi làm điều đó. – monzee

+0

yeah đã đồng ý. các đóng cửa trong 5.3 sẽ cho phép bạn loại bỏ nó. –

4

PHP 5.3 sẽ cho phép tạo ra các hàm lambda và đóng cửa mà sẽ cho phép bạn triển khai ví dụ về mã của mình. Từ số PHP RFC:

function & (parameters) use (lexical vars) { body } 

Hàm này được trả về làm tham chiếu và có thể được chuyển thành gọi lại.Ví dụ, tạo ra một chức năng:

$lambda = function() { echo "Hello World!\n"; }; 

Đi qua một hàm lambda như một callback:

function replace_spaces ($text) { 
    $replacement = function ($matches) { 
     return str_replace ($matches[1], ' ', ' ').' '; 
    }; 
    return preg_replace_callback ('/(+) /', $replacement, $text); 
} 

đóng cửa cũng cho phép bạn nhập các biến từ phạm vi địa phương:

$map = function ($text) use ($search, $replacement) { 
    if (strpos ($text, $search) > 50) { 
     return str_replace ($search, $replacement, $text); 
    } else { 
     return $text; 
    } 
}; 

đâu $ tìm kiếm và $ thay thế đều được khai báo bên ngoài phần đóng và $ text là tham số hàm.

+0

Carefull bạn không xác định "thế giới!" không gian tên.

+0

Vâng, tôi đã đọc rằng đóng cửa sẽ đến trong PHP 5.3, tôi chỉ tự hỏi nếu chúng ta được phép gán nó cho một thành viên trong lớp. – monzee

+0

Đóng cửa và lambdas là tham chiếu đến hàm (gán hàm). Bạn có thể gán nó cho bất cứ điều gì bạn muốn. –

3

Bạn có thể tận dụng phương pháp __call để định tuyến lại các cuộc gọi phương thức không tồn tại. Thêm đăng ký vào lớp của bạn để bạn có thể thêm/xóa phương thức khi đang di chuyển. Quy ước gọi cho hàm ngoài là tham số đầu tiên là đối tượng mà hàm đã bị ràng buộc.

clas Foo { 

    private $registry; 

    public function __call($alias, $args) { 
     if (array_key_exists($alias, $this->registry)) { 
      // prepend list of parameters with $this object 
      array_unshift($args, $this); 
      return call_user_func_array($this->registry[$alias], $args) 
     } 
    } 

    public function register($method, $alias = null) { 
     $alias or $alias = $method; // use original method name if no alias 
     $this->registry[$alias] = $method; 
    } 

    public function unregister($alias) { 
     unset($this->registry[$alias]); 
    } 

} 

Hãy tưởng tượng hàm clone_object trả về mảng đối tượng nhân bản vô tính. Tham số đầu tiên là đối tượng được nhân bản và thứ hai là số bản sao.

function clone_object($foo, $n) { 
    $a = array(); 
    while ($n--) { 
     $a[] = clone $foo; 
    } 
    return $a; 
} 

Bây giờ, bằng cách này bạn có thể tiêm phương pháp clone_object vào lớp Foo và cung cấp cho nó một cái tên bí danh bar:

$f = new Foo(); 
$f->register('clone_object', 'bar'); 
$f->bar(5); // this will return array of 5 cloned objects 

Trong dòng cuối cùng phương pháp bar gọi sẽ gây ra chuyển hướng hoạt động clone_object. Tâm rằng quy ước gọi sẽ chèn thông số $this vào danh sách tham số, vì vậy (5) sẽ được đổi thành ($this, 5).

Thông báo trước là các chức năng bên ngoài không thể hoạt động trên các thuộc tính và phương thức riêng tư/được bảo vệ.

Chỉ là một từ cuối cùng, nếu bạn có thể nghĩ ra một giải pháp không thay đổi đối tượng khi đang di chuyển, có lẽ bạn nên sử dụng nó. Ở trên là một thuật sĩ da đen và nên được sử dụng hết sức thận trọng.

+0

Cảm ơn, đó là một đoạn trích thú vị. – monzee

0

Sử dụng tạo chức năng bạn có thể làm một cái gì đó như:

$Dynamic->lambda = create_function('$var', 'return $var." world."; '); 
$classic = $Dynamic->lambda("hello"); // classic would contain "hello world." 

Mã lớp sẽ chứa một cái gì đó như:

public function __set($methodname, $function) 
{ 
    $this->methods[$methodname] = $function; 
} 

public function __get($methodname) 
{ 
    return $this->methods[$methodname]; 
} 

public function __call($method, $args) 
{ 
    $funct = $this->{$method}; 
    //$args[] = $this; --to add the current object as a parameter option 
    return call_user_func_array($funct, $args); 
} 

Lưu ý: bạn sẽ không thể sử dụng phạm vi đối tượng ($this biến) trong chức năng tạo của bạn, về cơ bản đây chỉ là sử dụng một giao diện đối tượng để create_function trong không gian tên chung. Tuy nhiên, bạn có thể dễ dàng nối thêm một thể hiện của đối tượng vào danh sách tham số và kéo nó vào lớp, nhưng sau đó lại bạn sẽ chỉ có quyền truy cập vào các phương thức công khai của lớp được kéo vào.

0

Kể từ PHP5.4 phát hành bạn có thể làm nhiều hơn:

  1. động thêm các phương pháp của bạn để đối tượng
  2. Sử dụng $ này bên trong những phương pháp
  3. Kế thừa một đối tượng năng động từ khác

Ý tưởng cơ bản được triển khai tại đây: https://github.com/ptrofimov/jslikeobject

0

tôi sử dụng create_closure này() trong công việc của tôi để tách callbacks vào Lớp học:

<?php 
function create_closure($fun, $args, $uses) 
     {$params=explode(',', $args.','.$uses); 
      $str_params=''; 
      foreach ($params as $v) 
        {$v=trim($v, ' &$'); 
        $str_params.='\''.$v.'\'=>&$'.$v.', '; 
        } 
      return "return function({$args}) use ({$uses}) {{$fun}(array({$str_params}));};"; 
     } 
?> 

dụ:

<?php 
$loop->addPeriodicTimer(1, eval(create_closure('pop_message', '$timer', '$cache_key, $n, &$response, &$redis_client'))); 

function pop_message($params) 
     {extract($params, EXTR_REFS); 
      $redis_client->ZRANGE($cache_key, 0, $n) 
          ->then(//normal 
            function($data) use ($cache_key, $n, &$timer, &$response, &$redis_client) 
            {//... 
            }, 
            //exception 
            function ($e) use (&$timer, &$response, &$redis_client) 
            {//... 
            } 
           ); 
     } 
?> 
Các vấn đề liên quan