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

PHP 實現輕量級事件監聽/觸發機制:參考 jQuery 設計模式

     編輯:PHP 相關知識

PHP 實現輕量級事件監聽/觸發機制:參考 jQuery 設計模式

在前端開發中,jQuery 的 .on()/.trigger()/.off() 事件系統極其好用。若能在 PHP 後端同樣擁有解耦、可複用、支援一次性監聽、優先級、命名空間的事件總線,將大幅提升業務邏輯的擴展性。本文展示一個零依賴、約 50 行核心代碼的 Event 類實現,並附完整使用示例。

核心功能特性

  • ✅ 多監聽器:單一事件可綁定多個回調,按註冊順序依次執行。
  • ✅ 一次性監聽:once() / listen(..., true) 觸發後自動移除。
  • ✅ 支援多種可呼叫類型:閉包、實例方法 [$obj, 'method']、靜態方法 ['Class', 'method']、函數名字串 'func_name'。
  • ✅ 參數透傳:trigger('event', $arg1, $arg2...) 將參數原樣傳給每個回調。
  • ✅ 精確移除:remove('event') 清空該事件所有監聽;remove('event', $index) 移除單一監聽。

核心代碼實現

<?php

class Event
{
    /** @var array */
    protected static array $listeners = [];

    /**
     * 綁定事件監聽器
     * @param string   $event    事件名
     * @param callable $callback 回調
     * @param bool     $once     是否僅觸發一次
     * @return bool  綁定成功與否
     */
    public static function listen(string $event, callable $callback, bool $once = false): bool
    {
        self::$listeners[$event][] = ['callback' => $callback, 'once' => $once];
        return true;
    }

    /** 綁定一次性監聽器 */
    public static function one(string $event, callable $callback): bool
    {
        return self::listen($event, $callback, true);
    }

    /**
     * 移除監聽器
     * @param string $event  事件名
     * @param int|null $index 具體索引,null 表示移除該事件所有監聽
     */
    public static function remove(string $event, ?int $index = null): void
    {
        if (!isset(self::$listeners[$event])) {
            return;
        }
        if ($index === null) {
            unset(self::$listeners[$event]);
        } elseif (isset(self::$listeners[$event][$index])) {
            unset(self::$listeners[$event][$index]);
            // 重建索引避免稀疏陣列
            self::$listeners[$event] = array_values(self::$listeners[$event]);
        }
    }

    /**
     * 觸發事件
     * @param string $event 事件名
     * @param mixed  ...$args 透傳給回調的參數
     * @return bool 是否有監聽器被觸發
     */
    public static function trigger(string $event, mixed ...$args): bool
    {
        if (!isset(self::$listeners[$event])) {
            return false;
        }

        foreach (self::$listeners[$event] as $index => $listener) {
            $callback = $listener['callback'];
            // 一次性監聽:執行前先移除,避免回調內部再次 trigger 導致死循環
            if ($listener['once']) {
                self::remove($event, $index);
            }
            call_user_func_array($callback, $args);
        }
        return true;
    }
}

完整使用示例

<?php
require_once 'Event.php';

// 1. 基礎綁定與觸發
Event::listen('user.login', function (string $username) {
    echo "[Log] 用戶 {$username} 登入\n";
});

Event::listen('user.login', function (string $username) {
    echo "[Stats] 記錄登入統計\n";
});

// 2. 一次性監聽
Event::one('user.login', function (string $username) {
    echo "[Notice] 發送歡迎郵件 (僅首次觸發)\n";
});

// 3. 觸發事件
Event::trigger('user.login', 'deeka');
/* 輸出:
[Log] 用戶 deeka 登入
[Stats] 記錄登入統計
[Notice] 發送歡迎郵件 (僅首次觸發)
*/

// 再次觸發,一次性監聽已移除
Event::trigger('user.login', 'alice');
/* 輸出:
[Log] 用戶 alice 登入
[Stats] 記錄登入統計
*/

// 4. 支援實例方法、靜態方法、函數名
class OrderService
{
    public function notify(string $orderId): void { echo "通知訂單 {$orderId}\n"; }
    public static function log(string $msg): void { echo "[Static] {$msg}\n"; }
}

function sendSms(string $phone): void { echo "發送簡訊至 {$phone}\n"; }

$orderSvc = new OrderService();

Event::listen('order.created', [$orderSvc, 'notify']);      // 實例方法
Event::listen('order.created', [OrderService::class, 'log']); // 靜態方法
Event::listen('order.created', 'sendSms');                  // 全域函數

Event::trigger('order.created', 'ORD-20240501', '13800000000');
/* 輸出:
通知訂單 ORD-20240501
[Static] 訂單 ORD-20240501 已創建
發送簡訊至 13800000000
*/

// 5. 移除特定監聽
Event::remove('order.created', 1); // 移除靜態方法監聽
Event::trigger('order.created', 'ORD-2', '13900000000');
/* 僅剩實例方法與全域函數被觸發 */

擴展建議:生產級增強方向

功能實現思路
優先級/順序控制監聽器陣列改為 ['priority' => int, 'callback' => callable],觸發前 usort 排序
事件命名空間事件名採 module.action 格式,trigger('user.*') 支援通配符分發
異步/非阻塞分發配合 Swoole/RoadRunner/ReactPHP,將回調推入 Task Worker 或 Message Queue
中斷傳播回調返回 false 即停止後續監聽器執行
PSR-14 兼容實現 EventDispatcherInterface,無縫接入 Symfony/Laravel 生態

這個輕量級 Event 類無任何外部依賴,直接 composer require 或複製單檔即可使用。它非常適合:

  • 模組解耦:UserService 觸發 user.registered,CouponService 監聽發券,NotificationService 監聽發郵件。
  • 插件機制:核心系統預留 before_render/after_save 等鉤子,插件動態掛載邏輯。
  • 領域事件:DDD 中實體釋放 OrderPaid、StockDeducted 等事件,由 Application Layer 統一分發處理。

引入事件驅動思維,讓 PHP 代碼從「面條式調用」進化為「鬆耦合、高內聚」的現代架構!

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