Làm thế nào để tôi thực hiện một cuộc gọi lại trong PHP?


Câu trả lời:


173

Hướng dẫn sử dụng các thuật ngữ "gọi lại" và "có thể gọi được" có thể hoán đổi cho nhau, tuy nhiên, "gọi lại" theo truyền thống đề cập đến một giá trị chuỗi hoặc mảng hoạt động như một con trỏ hàm , tham chiếu một phương thức hàm hoặc lớp để gọi trong tương lai. Điều này đã cho phép một số yếu tố của lập trình chức năng kể từ PHP 4. Các 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ể gọi được 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);
}

Các 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 dưới dạng $cb(). call_user_funccall_user_func_arrayhỗ trợ tất cả các bên trên.

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

Ghi chú / Hãy cẩn thận:

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

Thật vậy, sử dụng chức năng là cách thích hợp để làm điều đó. Trong khi sử dụng một biến và sau đó chỉ gọi nó, như được đề xuất trong câu trả lời được chấp nhận là tuyệt vời, nó xấu và sẽ không mở rộng quy mô bằng mã.
icco

4
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.
Nick Stinemates

Nó sẽ hữu ích cho độc giả đến từ lập trình Python để chỉ ra câu trả lời 'someGlobalFunction'thực sự là một hàm xác định.
TMOTTM

1
Kể từ phiên bản PHP 5.3, có các phần đóng cửa, xem câu trả lời của Bart van Heukelom . Nó đơn giản và "chuẩn" hơn nhiều so với tất cả mớ hỗn độn này.
thực sự

Vâng, tôi đã đề cập đến các hàm ẩn danh trong câu trả lời của mình, nhưng OP đã yêu cầu "gọi lại" (năm 2008) và các cuộc gọi lại kiểu cũ này vẫn được sử dụng rất nhiều trong hàng tấn cơ sở mã PHP.
Steve Clay

74

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 là một cách tốt đẹp để làm điều đó. Một bổ sung tuyệt vời cho PHP, bởi vì các cuộc gọi lại là tuyệt vời.


1
Điều này cần phải là câu trả lời hàng đầu.
LỚP.

30

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

Hiển thị: Dữ liệu là: đây là dữ liệu của tôi


21
Ôi chúa ơi. Đó có phải là tiêu chuẩn? Đó là khủng khiếp!
Nick Retallack

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.
Nick Stinemates

3
@Nick Retallack, tôi không thấy điều gì quá kinh khủng về nó. Đối với các ngôn ngữ tôi biết, chẳng hạn như JavaScript và C #, tất cả chúng đều có thể cấu trúc chức năng gọi lại của chúng theo mẫu như vậy. Đến từ JavaScirpt và C #, tôi thực sự không quen sử dụng call_user_func (). Nó làm cho tôi cảm thấy như tôi phải tự thích nghi với PHP, thay vì cách khác.
Antony

4
@Antony Tôi đã phản đối thực tế là các chuỗi là con trỏ hàm trong ngôn ngữ này. Tôi đã đăng bình luận đó ba năm trước, vì vậy bây giờ tôi khá quen với nó, nhưng tôi nghĩ PHP là ngôn ngữ duy nhất tôi biết (ngoài kịch bản shell) trong trường hợp này.
Nick Retallack

1
@Antony Tôi thích sử dụng cùng một cú pháp tôi sử dụng trong javascript. Vì vậy, tôi thậm chí không hiểu tại sao mọi người muốn sử dụng call_user_func()Khi họ có một cú pháp cho phép họ tự động gọi các hàm và thực hiện các cuộc gọi lại. Tôi đồng ý với bạn!
botenvouwer

9

Một mẹo tiện lợi mà gần đây tôi đã tìm thấy là sử dụng PHP create_function()để tạo một hàm ẩn danh / lambda để sử dụng một lần. Nó rất hữu ích cho các PHP functions như array_map(), preg_replace_callback()hoặc usort()rằng callbacks sử dụng để chế biến tùy chỉnh. Nó trông khá giống như nó thực hiện eval()dưới vỏ bọc, nhưng nó vẫn là một cách tốt để sử dụng PHP.


6
Thật không may, trình 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ăng. Nếu bạn không có hiệu suất, hãy tránh tạo_feft ().
fuxia

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

Bạn có phiền cập nhật câu trả lời với phiên bản PHP 7.4 (chức năng mũi tên) và thêm cảnh báo về việc không dùng nữa không create_function()?
Dharman


7

Bạn sẽ muốn xác minh bất cứ điều gì cuộc gọi của bạn là 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 liệu chức năng đó có tồn tại không:

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

Điều gì 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?
d -_- b

3
hay đúng hơnis_callable( $callback )
Roko C. Buljan

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

5

create_functionđã không làm việc cho tôi trong một 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] Đã thêm dấu ngoặc đơn bị 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 để hoạt động?
James P.

@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 vào mã đó một lần nữa và phát hiện ra một lỗi cú pháp, bạn. Cảm ơn
goliatone

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

Sẽ không an toàn hơn khi khai báo hàm do_callback trước khi tạo đối tượng Dispatcher và gọi phương thức async?
ejectamenta

3

Tôi co rúm mỗi khi tôi sử dụng create_function()trong php.

Các tham số là một chuỗi tách biệt hôn mê, toàn bộ cơ thể chức năng trong một chuỗi ... Argh ... Tôi nghĩ rằng họ không thể làm cho nó xấu hơn ngay cả khi họ đã thử.

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


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

Câu trả lời này đã lỗi thời trong 2 năm qua. create_function()hiện không được dùng nữa và không nên sử dụng
Dharman

2

Đối với những người không quan tâm đến việc phá vỡ tính tương thích với PHP < 5.4, tôi khuyên bạn nên sử dụng gợi ý loại để thực hiện sạch 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ảnh báo create_function() đã được ĐỔI kể từ phiên bản PHP 7.2.0. Dựa vào chức năng này là rất nản lòng.
Dharman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.