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

PHP 實作事件機制:觀察者模式與 debug_backtrace 存取控制

     編輯:PHP 綜合知識

設計目標

PHP 原生不支援事件,需自行實作。本文展示一個 Event 類別,結合觀察者模式與 debug_backtrace() 實現「只有直接持有者才能觸發事件」的存取控制。

核心程式碼

class Event {
    private $callbacks = [];
    private $holder;

    public function __construct() {
        $bt = debug_backtrace();
        // 建構子呼叫者為持有者($bt[1]['object'])
        $this->holder = (count($bt) >= 2) ? $bt[1]['object'] : null;
    }

    // 註冊監聽器:支援閉包或 [物件, 方法名]
    public function attach() {
        $args = func_get_args();
        if (count($args) === 1 && is_callable($args[0])) {
            $this->callbacks[] = $args[0];
        } elseif (count($args) === 2 && is_object($args[0]) && is_string($args[1])) {
            $this->callbacks[] = [$args[0], $args[1]];
        }
    }

    // 觸發事件:檢查呼叫者是否為持有者
    public function notify() {
        $bt = debug_backtrace();
        $caller = (count($bt) >= 2) ? $bt[count($bt)-1]['object'] : null;
        if ($this->holder && $caller !== $this->holder) {
            throw new Exception('Notify can only be called in holder');
        }
        $args = func_get_args();
        foreach ($this->callbacks as $cb) {
            call_user_func_array($cb, $args);
        }
    }
}

運作原理解析

  1. 建構時記錄持有者:new Event() 通常在持有者類別的建構子或屬性初始化中呼叫,此時 debug_backtrace()[1]['object'] 即為持有者實例。
  2. 觸發時驗證呼叫者:notify() 取得調用堆疊最底層的 $bt[count($bt)-1]['object'](即最初呼叫者),比對是否為持有者。
  3. 隔離性:外部物件無法偽造呼叫堆疊來觸發事件,保護封裝性。

使用範例

class Order {
    public $onShipped;
    public function __construct() {
        $this->onShipped = new Event();
    }
    public function ship() {
        // 業務邏輯...
        $this->onShipped->notify($this, 'shipped'); // 只有 Order 實例能呼叫
    }
}

class Notifier {
    public function onShipped($order, $status) {
        echo "通知客戶:訂單 {$order->id} 已{$status}";
    }
}

$order = new Order();
$notifier = new Notifier();
$order->onShipped->attach([$notifier, 'onShipped']);
$order->ship(); // 正常觸發

// 惡意嘗試
$hacker = new class {
    public function hack(Event $evt) { $evt->notify('hacked'); }
};
$hacker->hack($order->onShipped); // 拋出 Exception: Notify can only be called in holder

優缺點與改進

優點缺點/風險
純 PHP 實作,無需擴充套件debug_backtrace() 效能較差,高頻事件不適用
強制封裝,防止外部任意觸發堆疊檢查在 PHP 8+ 傳遞行為可能不同,需實測
支援閉包與物件方法兩種監聽器無內建優先序、一次性監聽、非同步事件等進階功能

現代化替代方案

  • Symfony EventDispatcher:標準 PSR-14 實作,支援優先序、傳播阻止、訂閱者介面。
  • Laravel Events:內建廣播、隊列、非同步監聽器,開箱即用。
  • League/Event:輕量級 Emitter,API 簡潔,適合非框架專案。

結語

此 Event 類別巧妙利用 debug_backtrace() 實現存取控制,展示了 PHP 反射能力與設計模式結合的可能性。理解其原理後,建議專案直接採用成熟的事件分發器,獲得標準化介面、效能優化與豐富生態支援。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved