2024 年日本 PHP 研討會

一流可呼叫語法

一流可呼叫語法是從 PHP 8.1.0 開始引入的,它是一種從匿名函式 建立 可呼叫物件的方法。它取代了使用字串和陣列的現有可呼叫語法。這種語法的優點是它可以進行靜態分析,並使用獲取可呼叫物件所在位置的作用域。

CallableExpr(...) 語法用於從可呼叫物件建立 Closure 物件。CallableExpr 接受任何可以在 PHP 語法中直接呼叫的表達式。

範例 #1 簡單的一流可呼叫語法

<?php

class Foo {
public function
method() {}
public static function
staticmethod() {}
public function
__invoke() {}
}

$obj = new Foo();
$classStr = 'Foo';
$methodStr = 'method';
$staticmethodStr = 'staticmethod';


$f1 = strlen(...);
$f2 = $obj(...); // 可呼叫物件
$f3 = $obj->method(...);
$f4 = $obj->$methodStr(...);
$f5 = Foo::staticmethod(...);
$f6 = $classStr::$staticmethodStr(...);

// 傳統使用字串、陣列的可呼叫語法
$f7 = 'strlen'(...);
$f8 = [$obj, 'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
?>

備註:

程式碼中的 ... 是語法的一部分,並非省略。

CallableExpr(...) 的語義與 Closure::fromCallable() 相同。也就是說,與使用字串和陣列的可呼叫語法不同,CallableExpr(...) 會遵循建立它時的範圍。

範例 #2 CallableExpr(...) 與傳統可呼叫語法的範圍比較

<?php

class Foo {
public function
getPrivateMethod() {
return [
$this, 'privateMethod'];
}

private function
privateMethod() {
echo
__METHOD__, "\n";
}
}

$foo = new Foo;
$privateMethod = $foo->getPrivateMethod();
$privateMethod();
// 嚴重錯誤:從全域範圍呼叫私有方法 Foo::privateMethod()
// 這是因為呼叫是在 Foo 之外執行的,並且將從這一點檢查可見性。

class Foo1 {
public function
getPrivateMethod() {
// 使用獲取可呼叫物件的範圍。
return $this->privateMethod(...); // 等同於 Closure::fromCallable([$this, 'privateMethod']);
}

private function
privateMethod() {
echo
__METHOD__, "\n";
}
}

$foo1 = new Foo1;
$privateMethod = $foo1->getPrivateMethod();
$privateMethod(); // Foo1::privateMethod
?>

備註:

不支援使用此語法 (例如 new Foo(...)) 建立物件,因為 new Foo() 語法不被視為呼叫。

備註:

一級可呼叫語法不能與空值安全運算子結合使用。以下兩種情況都會導致編譯時錯誤

<?php
$obj
?->method(...);
$obj?->prop->method(...);
?>

新增筆記

使用者貢獻的筆記 1 則筆記

bienvenunet at yahoo dot com
1 年前
這種語法有一個主要的陷阱,在你使用它之前可能不會很明顯,直到你發現你在一些隨機的程式庫程式碼中得到「無法重新綁定從方法建立的閉包的範圍」的例外。

如同文件中所述,一級可呼叫物件會使用獲取該物件時的範圍。只要你的程式碼中沒有任何東西嘗試使用 `\Closure::bindTo` 方法綁定該可呼叫物件,那就沒問題。

我是在將 Laravel 的 Macroable 功能的可呼叫物件從陣列樣式改為一級可呼叫物件樣式時,才痛苦地發現了這一點。Macroable 功能會在可呼叫物件上呼叫 `\Closure::bindTo`。

據我所知,唯一的解決方法是使用比較醜的陣列語法。
To Top