ฉันจะใช้โทรกลับใน PHP ได้อย่างไร


คำตอบ:


173

คู่มือใช้คำว่า "callback" และ "callable" สลับกันได้อย่างไรก็ตาม "callback" ตามธรรมเนียมอ้างถึงสตริงหรือค่าอาร์เรย์ที่ทำหน้าที่เหมือนตัวชี้ฟังก์ชั่นการอ้างอิงฟังก์ชั่นหรือวิธีการเรียนสำหรับการร้องขอในอนาคต สิ่งนี้ได้อนุญาตให้องค์ประกอบบางส่วนของการเขียนโปรแกรมการทำงานตั้งแต่ PHP 4 รสชาติคือ:

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

นี่เป็นวิธีที่ปลอดภัยในการใช้ค่าที่เรียกใช้ได้โดยทั่วไป:

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

รุ่นที่ทันสมัย PHP $cb()อนุญาตให้สามรูปแบบดังกล่าวข้างต้นเป็นครั้งแรกที่จะเรียกโดยตรงเป็น call_user_funcและcall_user_func_arrayสนับสนุนทั้งหมดข้างต้น

ดู: http://php.net/manual/th/language.types.callable.php

Notes / เตือน:

  1. ถ้าฟังก์ชั่น / ชั้นเรียนเป็น namespaced สตริงจะต้องมีชื่อที่ผ่านการรับรอง เช่น['Vendor\Package\Foo', 'method']
  2. call_user_funcไม่รองรับการส่งข้อมูลที่ไม่ใช่วัตถุโดยการอ้างอิงดังนั้นคุณสามารถใช้call_user_func_arrayหรือในเวอร์ชัน PHP ในภายหลังให้บันทึกการเรียกกลับไปยัง var และใช้ไวยากรณ์โดยตรง: $cb();
  3. วัตถุที่มี__invoke()วิธีการ (รวมถึงฟังก์ชั่นที่ไม่ระบุชื่อ) อยู่ภายใต้หมวดหมู่ "callable" และสามารถใช้วิธีเดียวกันได้ แต่โดยส่วนตัวแล้วฉันไม่ได้เชื่อมโยงสิ่งเหล่านี้กับคำว่า "callback" แบบดั้งเดิม
  4. มรดกcreate_function()สร้างฟังก์ชันสากลและส่งกลับชื่อ มันเป็นเสื้อคลุมeval()และฟังก์ชั่นที่ไม่ระบุชื่อควรใช้แทน

แน่นอนการใช้ฟังก์ชั่นเป็นวิธีที่เหมาะสมที่จะทำ ในขณะที่ใช้ตัวแปรแล้วเรียกมันว่าดังที่คำแนะนำในคำตอบที่ยอมรับนั้นเจ๋งมากมันน่าเกลียดและไม่ค่อยจะใช้โค้ดได้ดี
icco

4
เปลี่ยนคำตอบที่ยอมรับแล้ว เห็นด้วยกับความคิดเห็นนี่เป็นคำตอบที่ดี
Nick Stinemates

มันจะเป็นประโยชน์สำหรับผู้อ่านที่มาจากการเขียนโปรแกรม Python เพื่อชี้ให้เห็นในคำตอบที่'someGlobalFunction'แน่นอนเป็นหน้าที่ที่กำหนดไว้
TMOTTM

1
ในฐานะของ PHP 5.3 มีการปิดให้ดูคำตอบของ Bart van Heukelomคำตอบมันง่ายกว่าและ "มาตรฐาน" มากกว่าระเบียบแบบเดิม ๆ ทั้งหมด
จริงๆ

ใช่ฉันพูดถึงฟังก์ชั่นที่ไม่ระบุชื่อในคำตอบของฉัน แต่ OP ขอให้ "callback" (ในปี 2008) และการโทรกลับแบบเก่าเหล่านี้ยังคงมีการใช้งานอย่างมากในโค้ด PHP จำนวนมาก
Steve Clay

74

ด้วย PHP 5.3 คุณสามารถทำสิ่งนี้ได้:

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

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

ในที่สุดก็เป็นวิธีที่ดีที่จะทำ นอกจากนี้ยังยอดเยี่ยมสำหรับ PHP เพราะการโทรกลับนั้นยอดเยี่ยม


1
นี่ต้องเป็นคำตอบที่ดีที่สุด
โกรเวอร์

30

การดำเนินการติดต่อกลับจะทำเช่นนั้น

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

แสดง: ข้อมูลคือ: นี่คือข้อมูลของฉัน


21
โอ้พระเจ้า. เป็นมาตรฐานหรือไม่ นั่นแย่มาก!
Nick Retallack

มีวิธีอื่นอีกสองสามวิธีที่จะทำตามที่แสดงด้านบน ฉันคิดเหมือนกันจริงๆ
Nick Stinemates

3
@Nick Retallack ฉันไม่เห็นสิ่งที่น่ากลัวเกี่ยวกับมัน สำหรับภาษาที่ฉันรู้จักเช่น JavaScript และ C # พวกเขาทุกคนสามารถจัดโครงสร้างฟังก์ชันการเรียกกลับในรูปแบบดังกล่าวได้ มาจาก JavaScirpt และ C # ฉันไม่คุ้นเคยกับ call_user_func () มันทำให้ฉันรู้สึกว่าฉันต้องปรับตัวเองให้เข้ากับ PHP แทนที่จะใช้วิธีอื่น
แอนโทนี

4
@ Antony ฉันคัดค้านความจริงที่ว่าสตริงเป็นตัวชี้ฟังก์ชั่นในภาษานี้ ฉันโพสต์ความคิดเห็นนั้นเมื่อสามปีที่แล้วดังนั้นตอนนี้ฉันค่อนข้างชินแล้ว แต่ฉันคิดว่า PHP เป็นภาษาเดียวที่ฉันรู้จัก (นอกเหนือจากการเขียนสคริปต์เชลล์) ซึ่งเป็นกรณีนี้
Nick Retallack

1
@Antony ฉันชอบที่จะใช้ไวยากรณ์เดียวกับที่ฉันใช้ในจาวาสคริปต์ ดังนั้นฉันจึงไม่เข้าใจด้วยซ้ำว่าทำไมผู้คนถึงต้องการใช้call_user_func()เมื่อมีไวยากรณ์ซึ่งทำให้พวกเขาสามารถเรียกใช้ฟังก์ชันและทำการโทรกลับได้ ฉันเห็นด้วยกับคุณ!
botenvouwer

9

เคล็ดลับที่ดีอย่างหนึ่งที่ฉันได้พบเมื่อเร็ว ๆ นี้คือการใช้ PHP create_function()เพื่อสร้างฟังก์ชั่นนิรนาม / แลมบ์ดาสำหรับการใช้งานครั้งเดียว มันมีประโยชน์สำหรับการทำงาน PHP ชอบarray_map(), preg_replace_callback()หรือusort()ที่เรียกกลับใช้สำหรับการประมวลผลที่กำหนดเอง ดูเหมือนว่าจะeval()อยู่ภายใต้การคุ้มครอง แต่ก็ยังเป็นวิธีการใช้งาน PHP ที่ดี


6
น่าเสียดายที่ตัวรวบรวมขยะไม่เล่นได้ดีนักกับโครงสร้างนี้ซึ่งทำให้เกิดการรั่วไหลของหน่วยความจำ หากคุณไม่ต้องการประสิทธิภาพการทำงานให้หลีกเลี่ยง create_function ()
fuxia

1
อุ๊ยตาย ขอบคุณสำหรับหัวขึ้น.
yukondude

คุณคิดจะอัพเดตคำตอบด้วยเวอร์ชัน PHP 7.4 (ฟังก์ชั่นลูกศร) และเพิ่มคำเตือนเกี่ยวกับการเลิกใช้create_function()หรือไม่?
Dharman


7

คุณจะต้องยืนยันว่าการโทรของคุณนั้นถูกต้องหรือไม่ ตัวอย่างเช่นในกรณีของฟังก์ชั่นที่เฉพาะเจาะจงคุณจะต้องตรวจสอบและดูว่าฟังก์ชั่นที่มีอยู่:

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

เกิดอะไรขึ้นถ้าโทรกลับไม่ใช่ฟังก์ชั่น แต่อาร์เรย์ถือวัตถุและวิธีการ?
d -_- b

3
หรือมากกว่าis_callable( $callback )
Roko C. Buljan

1
ทั้งคู่เป็นคำแนะนำที่ดี - คุณจำเป็นต้องตรวจสอบการใช้งานเฉพาะของคุณเองว่าคุณโทรกลับอย่างไร ฉันหวังว่าจะเตือนไม่ให้โทรหาบางสิ่งที่ไม่มีอยู่ทำให้เกิดอันตรายถึงชีวิต ฉันตอบทั่วไปมากขึ้น
SeanDowney

5

create_functionไม่ได้ผลสำหรับฉันในชั้นเรียน 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!" );
        }
    }

}

จากนั้นใช้:

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

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

[แก้ไข] เพิ่มวงเล็บที่ขาดหายไป นอกจากนี้ยังเพิ่มการประกาศโทรกลับฉันชอบมันมาก


1
คลาส Dispatcher ไม่ต้องการแอตทริบิวต์สำหรับ $ this-> callback = $ callback เพื่อใช้งานหรือไม่
James P.

@james poulson: PHP เป็นภาษาแบบไดนามิกดังนั้นจึงใช้งานได้ แต่ฉันขี้เกียจ ฉันมักจะประกาศคุณสมบัติทำให้ทุกคนชีวิตง่ายขึ้น คำถามของคุณทำให้ฉันดูรหัสนั้นอีกครั้งและพบข้อผิดพลาดทางไวยากรณ์ ขอบคุณ
goliatone

ฉันไม่รู้ว่ามันเป็นไปได้ ขอบคุณสำหรับคำตอบ :) .
James P.

ไม่ปลอดภัยที่จะประกาศฟังก์ชัน do_callback ก่อนที่จะสร้างออบเจกต์ Dispatcher และเรียกเมธอด async
ejectamenta

3

ฉันประจบประแจงทุกครั้งที่ฉันใช้ create_function()ใน PHP

พารามิเตอร์เป็นสตริงที่คั่นด้วยโคม่าตัวฟังก์ชันทั้งหมดในสตริง ... เอ๊ะ ... ฉันคิดว่าพวกเขาไม่สามารถทำให้มันน่าเกลียดได้แม้ว่าพวกเขาจะพยายาม

น่าเสียดายที่มันเป็นทางเลือกเดียวเมื่อสร้างฟังก์ชั่นที่ตั้งชื่อไว้ไม่คุ้มค่ากับปัญหา


1
และแน่นอนว่ามันเป็นสตริงไทม์ไลน์ eval ดังนั้นจึงไม่ได้รับการตรวจสอบไวยากรณ์ที่ถูกต้องหรืออย่างอื่นในเวลารวบรวม
hobbs

คำตอบนี้ล้าสมัยใน 2 ปีที่ผ่านมา create_function()เลิกใช้แล้วและไม่ควรใช้
Dharman

2

สำหรับผู้ที่ไม่สนใจเกี่ยวกับความเข้ากันได้กับ PHP < 5.4ฉันขอแนะนำให้ใช้การบอกกล่าวแบบเพื่อทำให้การใช้งานมีความสะอาดยิ่งขึ้น

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"

คำเตือน create_function()เลิกใช้แล้วตั้งแต่ PHP 7.2.0 การใช้ฟังก์ชั่นนี้เป็นสิ่งที่ท้อแท้อย่างมาก
Dharman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.