แตกต่างที่สำคัญระหว่างพวกเขาคือการที่closure
เป็นระดับและประเภทcallable
callable
ประเภทยอมรับอะไรที่สามารถเรียกว่า :
var_dump(
is_callable('functionName'),
is_callable([$myClass, 'methodName']),
is_callable(function(){})
);
โดยที่ a closure
จะยอมรับเฉพาะฟังก์ชันที่ไม่ระบุตัวตน โปรดทราบว่าใน PHP เวอร์ชัน 7.1 คุณสามารถแปลงฟังก์ชันเป็นการปิดได้ดังนี้:
Closure::fromCallable('functionName')
.
ตัวอย่าง:
namespace foo{
class bar{
private $val = 10;
function myCallable(callable $cb){$cb()}
function myClosure(\Closure $cb){$cb()} // type hint must refer to global namespace
}
function func(){}
$cb = function(){};
$fb = new bar;
$fb->myCallable(function(){});
$fb->myCallable($cb);
$fb->myCallable('func');
$fb->myClosure(function(){});
$fb->myClosure($cb);
$fb->myClosure(\Closure::fromCallable('func'));
$fb->myClosure('func'); # TypeError
}
ทำไมต้องใช้closure
over callable
?
เข้มงวดเพราะclosure
เป็นวัตถุที่มีวิธีการอื่น ๆ บาง: call()
, และbind()
bindto()
ช่วยให้คุณสามารถใช้ฟังก์ชันที่ประกาศนอกคลาสและดำเนินการได้ราวกับว่าอยู่ในคลาส
$inject = function($i){return $this->val * $i;};
$cb1 = Closure::bind($inject, $fb);
$cb2 = $inject->bindTo($fb);
echo $cb1->call($fb, 2); // 20
echo $cb2(3); // 30
คุณไม่ต้องการเรียกใช้เมธอดในฟังก์ชันปกติเนื่องจากจะทำให้เกิดข้อผิดพลาดร้ายแรง ดังนั้นเพื่อที่จะหลีกเลี่ยงที่คุณจะต้องเขียนสิ่งที่ชอบ:
if($cb instanceof \Closure){}
การตรวจสอบทุกครั้งนี้ไม่มีจุดหมาย ดังนั้นหากคุณต้องการใช้วิธีการเหล่านั้นให้ระบุว่าอาร์กิวเมนต์เป็นclosure
. callback
มิฉะนั้นเพียงแค่ใช้ตามปกติ ทางนี้; เกิดข้อผิดพลาดขึ้นในการเรียกใช้ฟังก์ชันแทนที่จะเป็นรหัสของคุณทำให้ง่ายต่อการวินิจฉัย
สังเกตด้านบน:closure
ชั้นไม่สามารถขยายได้เป็นของสุดท้าย
["Foo", "bar"]
สำหรับFoo::bar
หรือสำหรับ[$foo, "bar"]
$foo->bar