首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 閉包 深度解析:use 機制、變數綁定、靜態閉包與實戰模式

     編輯:PHP 相關知識

什麼是閉包

閉包 是一種可攜帶定義時作用域變數的匿名函數。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 7.4+)

<?php
$x = 1;
$staticClosure = static function () use ($x) {
    // 無 $this,無法用 bindTo/call 改變作用域
    // 稍微節省記憶體、效能略優
    return $x * 2;
};

實戰設計模式

1. 延遲執行 / 惰性求值

<?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(); // 回傳快取

2. 策略模式 / 回呼注入

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

3. 依賴注入容器的工廠閉包

<?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 動態綁定 $this
  • 靜態閉包 static fn() 無 $this、較輕量
  • 善用閉包實作惰性載入、策略注入、DI 容器等進階模式
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved