閉包 是一種可攜帶定義時作用域變數的匿名函數。PHP 5.3 引入,底層為 Closure 類別實例。
<?php
$closure = function (參數列表) use (外部變數列表) {
// 函數主體
};
<?php
$result = 0;
// 1. 不 use:無法存取外部變數
$one = function () {
var_dump($result); // Notice: Undefined variable
};
// 2. use 值傳遞:複製建立時的值
$two = function () use ($result) {
var_dump($result); // int(0)
};
// 3. use 參考傳遞:綁定變數本身
$three = function () use (&$result) {
var_dump($result); // int(1)
};
$result++;
$one(); // 錯誤
$two(); // 0 (捕獲建立時的值)
$three(); // 1 (參考最新值)
Closure 類別進階操作<?php
$greet = function ($name) use ($prefix = 'Hello') {
return "{$prefix}, {$name}!";
};
// 1. bindTo:改變 $this 綁定物件
class Speaker { public $prefix = 'Hi'; }
$bound = $greet->bindTo(new Speaker(), 'Speaker');
echo $bound('World'); // "Hi, World!" (內部 $prefix 仍為 'Hello',因 use 捕獲優先)
// 2. call:臨時綁定 $this 呼叫
echo $greet->call(new Speaker(), 'World'); // "Hi, World!"
// 3. fromCallable:將可呼叫物件轉為閉包
$strlen = Closure::fromCallable('strlen');
echo $strlen('abc'); // 3
<?php
$x = 1;
$staticClosure = static function () use ($x) {
// 無 $this,無法用 bindTo/call 改變作用域
// 稍微節省記憶體、效能略優
return $x * 2;
};
<?php
function lazyQuery(callable $factory) {
return function () use ($factory) {
static $result = null;
if ($result === null) $result = $factory();
return $result;
};
}
$getUsers = lazyQuery(function () {
return User::all(); // 昂貴查詢
});
// 多次呼叫只查一次
$users1 = $getUsers();
$users2 = $getUsers(); // 回傳快取
<?php
class PaymentProcessor {
public function process(float $amount, callable $onSuccess, callable $onFail) {
if ($this->charge($amount)) {
$onSuccess($amount);
} else {
$onFail($amount);
}
}
}
$processor = new PaymentProcessor();
$processor->process(100,
fn($amt) => Log::info("Paid {$amt}"),
fn($amt) => Log::error("Failed {$amt}")
);
<?php
$container = [];
$container['db'] = function () {
return new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
};
$container['userRepo'] = function ($c) {
return new UserRepository($c['db']());
};
// 使用
$db = $container['db']();
$repo = $container['userRepo']($container);
<?php
class Heavy {
public $data = [];
public function __destruct() { echo "Heavy destroyed\n"; }
}
$obj = new Heavy();
// 閉包捕獲 $obj 形成循環引用
$obj->callback = function () use ($obj) {
return $obj->data;
};
unset($obj); // 不會立即觸發 __destruct (PHP 5.3+ GC 會定期清理)
// 解決:use (&$obj) 參考、或用弱引用 WeakReference (PHP 7.4+)
use ($var) = 值捕獲 (複製)use (&$var) = 參考捕獲 (同步變化)Closure 物件,可 bindTo/call 動態綁定 $thisstatic fn() 無 $this、較輕量