PHP 5.3 引入閉包與匿名函數。理論上:閉包是「攜帶環境狀態的函數」,匿名函數是「無名稱的函數」。但在 PHP 實作中,兩者合一——Closure 類別實例同時扮演兩種角色。
閉包可像字串、整數一樣:指派給變數、當作參數傳遞、作為回傳值、存入陣列。其核心在於實作了 __invoke() 魔術方法,使變數加上 () 即可呼叫。
// 建立閉包並指派給變數
$greet = function ($name) {
return "Hello, {$name}";
};
echo $greet('World'); // Hello, World
// 當作回調函數:array_map 經典案例
$numbers = [1, 2, 3];
$plusOne = array_map(function ($n) { return $n + 1; }, $numbers);
// 結果:[2, 3, 4]
// 對比:傳統具名函數寫法(較繁瑣、效能稍差)
function increment($n) { return $n + 1; }
$plusOne = array_map('increment', $numbers);
閉包預設無法存取外部變數,需透過 use 明確引入(值複製),或 bindTo() 綁定物件上下文(參考綁定)。
function createGreeter($name) {
return function ($greeting) use ($name) {
return "{$greeting}, {$name}!";
};
}
$greeter = createGreeter('Alice');
echo $greeter('Good morning'); // Good morning, Alice!
將閉包的 $this 綁定到指定物件,可存取該物件的 private/protected 成員。
class App {
protected $routes = [];
protected $response = 'Hello World';
public function addRoute($path, $callback) {
// 綁定 $this 到目前 App 實例
$this->routes[$path] = $callback->bindTo($this, __CLASS__);
}
public function dispatch($path) {
if (isset($this->routes[$path])) {
($this->routes[$path])(); // 呼叫閉包
}
}
}
$app = new App();
$app->addRoute('/api/user', function () {
// 此處 $this 指向 $app 實例
$this->response = '{"name": "Alice"}';
header('Content-Type: application/json');
});
$app->dispatch('/api/user'); // 輸出 JSON
閉包會持有引用的變數/物件,長生命週期的閉包(如快取在靜態屬性)可能導致記憶體洩漏。PHP 7.1+ 引入 Closure::fromCallable() 可建立更輕量的可呼叫物件,視情況選用。