插件,亦即Plug-in,是指一類特定的功能模塊(通常由第三方開發者實現)
它的特點:
1. 隨時安裝、卸載、激活、禁用
2. 無論什么狀態都不影響系統核心模塊的運行,
3. 是一種非侵入式的模塊化設計,實現了核心程序與插件程序的松散耦合。
一個健壯的PHP插件機制,我認為必須具備以下特點:
1. 插件的動態監聽和加載(Lookup)
2. 插件的動態觸發
以上兩點的PHP插件機制實現均不影響核心程序的運行
鈎子(Hooks)
要在程序中實現插件,我們首先應該想到的就是定義不同的鈎子(Hooks);“鈎子”是一個很形象的邏輯概念,你可以認為它是系統預留的插件觸發條件。
它的邏輯原理如下:
當系統執行到某個鈎子時,會判斷這個鈎子的條件是否滿足;
如果滿足,會轉而先去調用鈎子所制定的功能,然后返回繼續執行余下的程序;
如果不滿足,跳過即可。這有點像匯編中的“中斷保護”邏輯。
某些鈎子可能是系統事先就設計好的,比如之前我舉的關於評論Spam過濾的鈎子,通常它已經由核心系統開發人員設計進了評論的處理邏輯中;另外一類鈎子則可能是由用戶自行定制的(由第三方開發人員制定),通常存在於表現層,比如一個普通的PHP表單顯示頁面中。
整個機制核心分為三大塊:
1.一個插件經理類:
這是核心之核心。它是一個應用程序全局Global對象。它主要有三個職責:
負責監聽已經注冊了的所有插件,並實例化這些插件對象。
負責注冊所有插件。
當鈎子條件滿足時,觸發對應的對象方法。
2.插件的功能實現:
這大多由第三方開發人員完成,但需要遵循一定的規則,這個規則是插件機制所規定的,因插件機制的不同而不同,下面的顯示代碼你會看到這個規則。
3.插件的觸發:
也就是鈎子的觸發條件。具體來說這是一小段代碼,放置在你需要插件實現的地方,用於觸發這個鈎子。
1 <?php 2 3 class PluginManager { 4 5 private $_listeners = array(); 6 7 public function __construct() { 8 //這里$plugin數組包含我們獲取已經由用戶 9 //激活的插件信息 10 //為演示方便,我們假定$plugin中至少包含 11 //$plugin = array( 12 // 'name' => '插件名稱', 13 // 'directory'=>'插件安裝目錄' 14 //); 15 $plugins = array( 16 array( 17 'name' => '插件名稱', 18 'directory' => '插件安裝目錄' 19 ) 20 ); 21 //這個函數請自行實現 22 if ($plugins) { 23 foreach ($plugins as $plugin) { 24 ////假定每個插件文件夾中包含一個actions. 25 //php文件,它是插件的具體實現 26 if (@file_exists(STPATH . 'plugins/' . $plugin['directory'] . '/actions.php')) { 27 include_once(STPATH . 'plugins/' . $plugin['directory'] . '/actions.php'); 28 $class = $plugin['name'] . '_actions'; 29 if (class_exists($class)) { 30 //初始化所有插件 31 new $class($this); 32 } 33 } 34 } 35 } 36 //此處做些日志記錄方面的東西 37 } 38 39 function register($hook, &$reference, $method) { 40 //獲取插件要實現的方法 41 $key = get_class($reference) . '->' . $method; 42 //將插件的引用連同方法push進監聽數組中 43 $this->_listeners[$hook][$key] = 44 array(&$reference, $method); 45 //此處做些日志記錄方面的東西 46 } 47 48 function trigger($hook, $data = '') { 49 $result = ''; 50 //查看要實現的鈎子,是否在監聽數組之中 51 if (isset($this->_listeners[$hook]) && is_array($this->_listeners[$hook]) && count($this->_listeners[$hook]) > 0) { 52 // 循環調用開始 53 foreach ($this->_listeners[$hook] as $listener) { 54 // 取出插件對象的引用和方法 55 $class = & $listener[0]; 56 $method = $listener[1]; 57 if (method_exists($class, $method)) { 58 // 動態調用插件的方法 59 $result .= $class->$method($data); 60 } 61 } 62 } 63 //此處做些日志記錄方面的東西 64 return $result; 65 } 66 67 } 68 69 ?>