2008-09-08 35 views

Trả lời

157

Tài liệu hướng dẫn sử dụng các thuật ngữ "gọi lại" và "callable" thay thế cho nhau, tuy nhiên, "gọi lại" truyền thống đề cập đến một chuỗi hoặc giá trị mảng hoạt động như một function pointer, tham chiếu một hàm hoặc phương thức lớp để gọi trong tương lai. Này đã cho phép một số yếu tố của lập trình chức năng từ PHP 4. hương vị là:

$cb1 = 'someGlobalFunction'; 
$cb2 = ['ClassName', 'someStaticMethod']; 
$cb3 = [$object, 'somePublicMethod']; 

// this syntax is callable since PHP 5.2.3 but a string containing it 
// cannot be called directly 
$cb2 = 'ClassName::someStaticMethod'; 
$cb2(); // fatal error 

// legacy syntax for PHP 4 
$cb3 = array(&$object, 'somePublicMethod'); 

Đây là một cách an toàn để sử dụng các giá trị có thể được gọi nói chung:

if (is_callable($cb2)) { 
    // Autoloading will be invoked to load the class "ClassName" if it's not 
    // yet defined, and PHP will check that the class has a method 
    // "someStaticMethod". Note that is_callable() will NOT verify that the 
    // method can safely be executed in static context. 

    $returnValue = call_user_func($cb2, $arg1, $arg2); 
} 

phiên bản PHP hiện đại cho phép ba định dạng đầu tiên ở trên để được gọi trực tiếp là $cb(). call_user_funccall_user_func_array hỗ trợ tất cả những điều trên.

Xem: http://php.net/manual/en/language.types.callable.php

Notes/Hãy cẩn thận:

  1. Nếu hàm/lớp được namespaced, chuỗi phải chứa tên đầy đủ điều kiện. Ví dụ. ['Vendor\Package\Foo', 'method']
  2. call_user_func không hỗ trợ chuyển đối tượng không theo tham chiếu, vì vậy bạn có thể sử dụng call_user_func_array hoặc, trong các phiên bản PHP sau, lưu gọi lại thành var và sử dụng cú pháp trực tiếp: $cb();
  3. Đối tượng có phương thức __invoke() (bao gồm cả chức năng ẩn danh) thuộc danh mục "có thể gọi" và có thể được sử dụng theo cùng một cách, nhưng cá nhân tôi không liên kết những điều này với cụm từ "gọi lại" cũ.
  4. Di sản create_function() tạo chức năng toàn cục và trả về tên của nó. Đó là trình bao bọc cho eval() và các chức năng ẩn danh nên được sử dụng thay thế.
+1

Điều này hoàn toàn tốt hơn so với câu trả lời tự chấp nhận của OP. – chaos

+0

Thật vậy, việc sử dụng hàm là cách thích hợp để thực hiện. Trong khi sử dụng một biến và sau đó chỉ cần gọi nó, như đề xuất trong câu trả lời được chấp nhận là mát mẻ, nó xấu xí và sẽ không quy mô tốt với mã. – icco

+3

Đã thay đổi câu trả lời được chấp nhận. Đồng ý với ý kiến, đây là một câu trả lời tuyệt vời. –

26

Thực hiện một cuộc gọi lại được thực hiện như vậy

// This function uses a callback function. 
function doIt($callback) 
{ 
    $data = "this is my data"; 
    $callback($data); 
} 


// This is a sample callback function for doIt(). 
function myCallback($data) 
{ 
    print 'Data is: ' . $data . "\n"; 
} 


// Call doIt() and pass our sample callback function's name. 
doIt('myCallback'); 

Displays: Dữ liệu là: đây là dữ liệu của tôi

+16

Ôi trời ơi. Đó có phải là tiêu chuẩn không? Đó là khủng khiếp! –

+0

Có một vài cách khác để làm điều đó như được hiển thị ở trên. Tôi thực sự nghĩ như vậy. –

+0

Wow, điều này cho biết thêm một số xoắn mới để lập trình thông thường. –

9

Một mẹo nhỏ tiện lợi mà tôi vừa mới tìm thấy là sử dụng PHP create_function() để tạo ra một ẩn danh/lambda chức năng cho sử dụng một shot. Tính năng này hữu ích cho các hàm PHP như array_map(), preg_replace_callback() hoặc usort() sử dụng gọi lại để xử lý tùy chỉnh. Nó trông khá giống như nó hiện một eval() dưới nắp, nhưng nó vẫn là một cách tốt đẹp theo phong cách chức năng để sử dụng PHP.

+6

Thật không may, bộ thu gom rác không hoạt động tốt với cấu trúc này tạo ra rò rỉ bộ nhớ tiềm ẩn. Nếu bạn không tham gia biểu diễn, hãy tránh create_function(). – fuxia

+1

Ouch. Cảm ơn cho những người đứng đầu lên. – yukondude

3

Tôi rạn nứt mỗi lần tôi sử dụng create_function() bằng php.

Các tham số là một chuỗi được phân cách bằng dấu phẩy, toàn bộ nội dung hàm trong một chuỗi ... Argh ... Tôi nghĩ rằng chúng không thể làm cho nó xấu hơn ngay cả khi chúng đã thử.

Thật không may, đó là lựa chọn duy nhất khi tạo một hàm được đặt tên không đáng để gây rắc rối.

+0

Và, tất nhiên, đó là chuỗi thời gian chạy eval, do đó, nó không được kiểm tra cú pháp hợp lệ hoặc bất cứ điều gì khác tại thời gian biên dịch. – hobbs

7

cũng ... với 5.3 trên đường chân trời, tất cả sẽ tốt hơn, bởi vì với 5.3, chúng tôi sẽ nhận được đóng cửa và với họ chức năng ẩn danh

http://wiki.php.net/rfc/closures

7

Bạn sẽ muốn xác minh bất cứ điều gì gọi của bạn hợp lệ. Ví dụ, trong trường hợp của một chức năng cụ thể, bạn sẽ muốn kiểm tra và xem chức năng tồn tại:

function doIt($callback) { 
    if(function_exists($callback)) { 
     $callback(); 
    } else { 
     // some error handling 
    } 
} 
+0

Điều gì sẽ xảy ra nếu gọi lại không phải là một hàm, mà là một đối tượng và phương thức giữ mảng? –

+0

hoặc đúng hơn là 'is_callable ($ callback)' –

+0

Cả hai đều là những gợi ý tốt - Cần phải kiểm tra việc thực hiện cụ thể của riêng bạn về cách bạn thực hiện gọi lại. Tôi đã hy vọng sẽ cảnh báo chống lại việc gọi cái gì đó không tồn tại gây tử vong. Tôi đưa ra câu trả lời chung chung hơn – SeanDowney

4

create_function không hoạt động đối với tôi trong lớp học. Tôi đã phải sử dụng call_user_func.

<?php 

class Dispatcher { 
    //Added explicit callback declaration. 
    var $callback; 

    public function Dispatcher($callback){ 
     $this->callback = $callback; 
    } 

    public function asynchronous_method(){ 
     //do asynch stuff, like fwrite...then, fire callback. 
     if (isset($this->callback)) { 
      if (function_exists($this->callback)) call_user_func($this->callback, "File done!"); 
     } 
    } 

} 

Sau đó, để sử dụng:

<?php 
include_once('Dispatcher.php'); 
$d = new Dispatcher('do_callback'); 
$d->asynchronous_method(); 

function do_callback($data){ 
    print 'Data is: ' . $data . "\n"; 
} 
?> 

[Chỉnh sửa] Added ngoặc thiếu. Ngoài ra, thêm khai báo gọi lại, tôi thích nó theo cách đó.

+1

Không phải lớp Dispatcher yêu cầu một thuộc tính cho $ this-> callback = $ callback để làm việc? –

+0

@james poulson: PHP là một ngôn ngữ động, vì vậy nó hoạt động. Nhưng tôi đã lười biếng. Tôi thường khai báo tài sản, làm cho cuộc sống của mọi người dễ dàng hơn. Câu hỏi của bạn khiến tôi nhìn lại mã đó và phát hiện ra một lỗi cú pháp. Cảm ơn – goliatone

+0

Tôi không biết điều này là có thể. Cảm ơn câu trả lời :). –

59

Với PHP 5.3, bây giờ bạn có thể làm điều này:

function doIt($callback) { $callback(); } 

doIt(function() { 
    // this will be done 
}); 

Cuối cùng một cách tốt đẹp để làm điều đó. Một bổ sung tuyệt vời cho PHP, bởi vì callbacks là tuyệt vời.

+4

khá gọn gàng, rất hữu ích – Ulterior

1

Đối với những người không quan tâm đến việc vi phạm khả năng tương thích với PHP < 5.4, tôi khuyên bạn nên sử dụng loại gợi ý để thực hiện triển khai rõ ràng hơn.

function call_with_hello_and_append_world(callable $callback) 
{ 
    // No need to check $closure because of the type hint 
    return $callback("hello")."world"; 
} 

function append_space($string) 
{ 
    return $string." "; 
} 

$output1 = call_with_hello_and_append_world(function($string) { return $string." "; }); 
var_dump($output1); // string(11) "hello world" 

$output2 = call_with_hello_and_append_world("append_space"); 
var_dump($output2); // string(11) "hello world" 

$old_lambda = create_function('$string', 'return $string." ";'); 
$output3 = call_with_hello_and_append_world($old_lambda); 
var_dump($output3); // string(11) "hello world" 
Các vấn đề liên quan