Bagaimana cara menerapkan panggilan balik di PHP?

181

Bagaimana panggilan balik ditulis dalam PHP?

Nick Stinemates
sumber
1
Akan menghubungkan pertanyaan lain dengan yang ini, karena saya mencoba menelepon penutupan.
akinuri

Jawaban:

173

Manual menggunakan istilah "callback" dan "callable" secara bergantian, namun, "callback" secara tradisional mengacu pada nilai string atau array yang bertindak seperti penunjuk fungsi , referensi fungsi atau metode kelas untuk doa selanjutnya. Ini telah memungkinkan beberapa elemen pemrograman fungsional sejak PHP 4. Rasa adalah:

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

Ini adalah cara yang aman untuk menggunakan nilai-nilai yang bisa dipanggil pada umumnya:

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

Versi PHP modern memungkinkan tiga format pertama di atas untuk dipanggil secara langsung sebagai $cb(). call_user_funcdan call_user_func_arraymendukung semua hal di atas.

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

Catatan / Peringatan:

  1. Jika fungsi / kelas di-namespace, string harus berisi nama yang sepenuhnya memenuhi syarat. Misalnya['Vendor\Package\Foo', 'method']
  2. call_user_functidak mendukung lewat non-objek dengan referensi, sehingga Anda bisa menggunakan call_user_func_arrayatau, dalam versi PHP kemudian, menyimpan callback untuk var dan menggunakan sintaks langsung: $cb();
  3. Objek dengan __invoke()metode (termasuk fungsi anonim) termasuk dalam kategori "bisa dipanggil" dan dapat digunakan dengan cara yang sama, tapi saya pribadi tidak mengaitkan ini dengan istilah "panggilan balik" lama.
  4. Warisan create_function()menciptakan fungsi global dan mengembalikan namanya. Ini pembungkus untuk eval()dan fungsi anonim harus digunakan sebagai gantinya.
Steve Clay
sumber
Memang, menggunakan fungsi adalah cara yang tepat untuk melakukannya. Saat menggunakan variabel dan kemudian memanggilnya, seperti yang disarankan dalam jawaban yang diterima itu keren, itu jelek dan tidak akan skala dengan kode.
icco
4
Mengubah jawaban yang diterima. Setuju dengan komentar, ini adalah jawaban yang bagus.
Nick Stinemates
Akan sangat membantu bagi pembaca yang datang dari pemrograman Python untuk menunjukkan jawaban yang 'someGlobalFunction'memang merupakan fungsi yang didefinisikan.
TMOTTM
1
Pada PHP 5.3, ada penutupan, lihat jawaban Bart van Heukelom . Ini jauh lebih sederhana dan "standar" daripada semua kekacauan warisan ini.
reallynice
Ya, saya menyebutkan fungsi anonim dalam jawaban saya, tetapi OP meminta "panggilan balik" (pada 2008) dan panggilan balik gaya lama ini masih sangat banyak digunakan dalam banyak basis kode PHP.
Steve Clay
74

Dengan PHP 5.3, Anda sekarang dapat melakukan ini:

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

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

Akhirnya cara yang bagus untuk melakukannya. Tambahan yang bagus untuk PHP, karena panggilan baliknya mengagumkan.

Bart van Heukelom
sumber
1
Ini harus menjadi jawaban teratas.
GROVER.
30

Implementasi panggilan balik dilakukan seperti itu

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

Menampilkan: Data adalah: ini adalah data saya

Nick Stinemates
sumber
21
Ya Tuhan. Apakah itu standarnya? Itu buruk!
Nick Retallack
Ada beberapa cara lain untuk melakukannya seperti yang ditunjukkan di atas. Saya benar-benar memikirkan hal yang sama.
Nick Stinemates
3
@Nick Retallack, saya tidak melihat apa yang mengerikan tentangnya. Untuk bahasa yang saya tahu, seperti JavaScript dan C #, mereka semua dapat menyusun fungsi panggilan balik mereka dalam pola tersebut. Berasal dari JavaScirpt dan C #, saya benar-benar tidak terbiasa dengan call_user_func (). Itu membuat saya merasa harus beradaptasi dengan PHP, bukan sebaliknya.
Antony
4
@Antony Saya keberatan dengan fakta bahwa string adalah pointer fungsi dalam bahasa ini. Saya memposting komentar itu tiga tahun yang lalu, jadi saya sudah terbiasa sekarang, tapi saya pikir PHP adalah satu-satunya bahasa yang saya tahu (selain dari skrip shell) di mana ini terjadi.
Nick Retallack
1
@Antony Saya lebih suka menggunakan sintaks yang sama yang saya gunakan dalam javascript. Jadi saya bahkan tidak mengerti mengapa orang ingin menggunakan call_user_func()Ketika mereka memiliki sintaks yang memungkinkan mereka untuk secara dinamis memanggil fungsi dan membuat panggilan balik. Saya setuju denganmu!
botenvouwer
9

Satu trik bagus yang baru-baru ini saya temukan adalah menggunakan PHP create_function()untuk membuat fungsi anonim / lambda untuk penggunaan sekali pakai. Ini berguna untuk fungsi PHP seperti array_map(), preg_replace_callback()atau usort()bahwa penggunaan callback untuk pengolahan kustom. Ini terlihat seperti itu di eval()bawah selimut, tapi itu masih cara gaya fungsional yang bagus untuk menggunakan PHP.

Yukondude
sumber
6
Sayangnya, pengumpul sampah tidak bermain dengan baik dengan konstruksi ini yang menghasilkan kebocoran memori potensial. Jika Anda keluar untuk kinerja, hindari create_function ().
fuxia
1
Aduh. Terimakasih atas peringatannya.
yukondude
Maukah Anda memperbarui jawaban dengan versi PHP 7.4 (fungsi panah) dan menambahkan peringatan tentang usang create_function()?
Dharman
7

Anda akan ingin memverifikasi apa pun panggilan Anda valid. Misalnya, dalam hal fungsi tertentu, Anda akan ingin memeriksa dan melihat apakah fungsi tersebut ada:

function doIt($callback) {
    if(function_exists($callback)) {
        $callback();
    } else {
        // some error handling
    }
}
SeanDowney
sumber
Bagaimana jika callback bukan fungsi, tetapi sebuah array yang menahan objek dan metode?
d -_- b
3
atau lebih tepatnyais_callable( $callback )
Roko C. Buljan
1
Keduanya adalah saran yang bagus - Anda perlu memeriksa implementasi khusus Anda sendiri tentang bagaimana Anda melakukan panggilan balik. Saya berharap untuk memperingatkan agar tidak memanggil sesuatu yang tidak ada yang menyebabkan kematian. Saya membuat jawabannya lebih umum
SeanDowney
5

create_functiontidak bekerja untuk saya di dalam kelas. Saya harus menggunakan 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!" );
        }
    }

}

Lalu, untuk menggunakan:

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

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

[Sunting] Menambahkan tanda kurung yang hilang. Juga, menambahkan deklarasi panggilan balik, saya lebih suka seperti itu.

goliatone
sumber
1
Bukankah kelas Dispatcher memerlukan atribut untuk $ this-> callback = $ callback untuk berfungsi?
James P.
@james poulson: PHP adalah bahasa yang dinamis, jadi ia berfungsi. Tapi saya malas. Saya biasanya mendeklarasikan properti, membuat hidup semua orang lebih mudah. Pertanyaan Anda membuat saya melihat kode itu lagi dan melihat kesalahan sintaksis, Anda. Terima kasih
goliatone
Saya tidak tahu ini mungkin. Terima kasih atas jawabannya :) .
James P.
Bukankah lebih aman untuk mendeklarasikan fungsi do_callback sebelum membuat objek Dispatcher dan memanggil metode async?
ejectamenta
3

Saya merasa ngeri setiap kali saya menggunakan create_function()php.

Parameter adalah string yang dipisahkan koma, seluruh fungsi tubuh dalam sebuah string ... Argh ... Saya pikir mereka tidak bisa membuatnya lebih buruk bahkan jika mereka mencoba.

Sayangnya, itu adalah satu-satunya pilihan ketika membuat fungsi bernama tidak sebanding dengan masalahnya.

Menepuk
sumber
1
Dan, tentu saja, ini string runtime eval, jadi itu tidak diperiksa untuk sintaks yang valid atau apa pun pada waktu kompilasi.
hobbs
Jawaban ini telah usang selama 2 tahun terakhir. create_function()sekarang sudah usang dan tidak boleh digunakan.
Dharman
2

Bagi mereka yang tidak peduli tentang melanggar kompatibilitas dengan PHP < 5.4, saya sarankan menggunakan tipe hinting untuk membuat implementasi yang lebih bersih.

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"
Kendall Hopkins
sumber
Peringatan create_function() telah DITANGGUHKAN pada PHP 7.2.0. Mengandalkan fungsi ini sangat tidak dianjurkan.
Dharman